Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
MarkdownDocument.cs
1using SkiaSharp;
2using System;
3using System.Collections;
4using System.Collections.Generic;
5using System.IO;
6using System.Reflection;
7using System.Text;
8using System.Text.RegularExpressions;
9using System.Threading.Tasks;
10using System.Xml;
20using Waher.Events;
23using Waher.Script;
28
30{
37 public delegate bool MarkdownElementHandler(MarkdownElement Element, object State);
38
43 public delegate Task AsyncMarkdownProcessing(object State);
44
49 public class MarkdownDocument : IFileNameResource, IEnumerable<MarkdownElement>, IJsonEncodingHint
50 {
54 public const string MarkdownSettingsVariableName = " MarkdownSettings ";
55
56 internal static readonly Regex endOfHeader = new Regex(@"\n\s*\n", RegexOptions.Multiline | RegexOptions.Compiled);
57 internal static readonly Regex scriptHeader = new Regex(@"^(?'Tag'(([Ss][Cc][Rr][Ii][Pp][Tt])|([Ii][Nn][Ii][Tt]))):\s*(?'ScriptFile'[^\r\n]*)", RegexOptions.Multiline | RegexOptions.Compiled);
58
59 private readonly List<KeyValuePair<AsyncMarkdownProcessing, object>> asyncTasks = new List<KeyValuePair<AsyncMarkdownProcessing, object>>();
60 private readonly Dictionary<string, Multimedia> references = new Dictionary<string, Multimedia>();
61 private readonly Dictionary<string, KeyValuePair<string, bool>[]> metaData = new Dictionary<string, KeyValuePair<string, bool>[]>();
62 private Dictionary<string, int> footnoteNumberByKey = null;
63 private Dictionary<string, Footnote> footnotes = null;
64 private SortedDictionary<int, string> toInsert = null;
65 private readonly Type[] transparentExceptionTypes;
66 private List<string> footnoteOrder = null;
67 private readonly LinkedList<MarkdownElement> elements;
68 private readonly List<Header> headers = new List<Header>();
69 private readonly IEmojiSource emojiSource;
70 private string markdownText;
71 private string fileName = string.Empty;
72 private string resourceName = string.Empty;
73 private string url = string.Empty;
74 private MarkdownDocument master = null;
75 private MarkdownDocument detail = null;
76 private readonly MarkdownSettings settings;
77 private int lastFootnote = 0;
78 private bool syntaxHighlighting = false;
79 private bool includesTableOfContents = false;
80 private bool isDynamic = false;
81 private bool? allowScriptTag = null;
82 private object tag = null;
83
90 public static Task<MarkdownDocument> CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
91 {
92 return CreateAsync(MarkdownText, new MarkdownSettings(), string.Empty, string.Empty, string.Empty, TransparentExceptionTypes);
93 }
94
102 public static Task<MarkdownDocument> CreateAsync(string MarkdownText, MarkdownSettings Settings, params Type[] TransparentExceptionTypes)
103 {
104 return CreateAsync(MarkdownText, Settings, string.Empty, string.Empty, string.Empty, TransparentExceptionTypes);
105 }
106
118 public static async Task<MarkdownDocument> CreateAsync(string MarkdownText, MarkdownSettings Settings, string FileName, string ResourceName, string URL,
119 params Type[] TransparentExceptionTypes)
120 {
121 bool IsDynamic = false;
122
123 if (!(Settings.Variables is null))
124 {
125 KeyValuePair<string, bool> P = await Preprocess(MarkdownText, Settings, FileName, TransparentExceptionTypes);
126 MarkdownText = P.Key;
127 IsDynamic = P.Value;
128 }
129
131 }
132
133 private MarkdownDocument(string MarkdownText, bool IsDynamic, MarkdownSettings Settings, string FileName, string ResourceName, string URL,
134 params Type[] TransparentExceptionTypes)
135 {
136 this.markdownText = MarkdownText?.Replace("\r\n", "\n").Replace('\r', '\n') ?? string.Empty;
137 this.isDynamic = IsDynamic;
138 this.emojiSource = Settings.EmojiSource;
139 this.settings = Settings;
140 this.fileName = FileName;
141 this.resourceName = ResourceName;
142 this.url = URL;
143 this.transparentExceptionTypes = TransparentExceptionTypes;
144
145 List<Block> Blocks = ParseTextToBlocks(this.markdownText);
146 List<KeyValuePair<string, bool>> Values = new List<KeyValuePair<string, bool>>();
147 Block Block;
148 KeyValuePair<string, bool>[] Prev;
149 string s, s2;
150 string Key = null;
151 int Start = 0;
152 int End = Blocks.Count - 1;
153 int i, j;
154
155 if (Settings.ParseMetaData && Blocks.Count > 0)
156 {
157 Block = Blocks[0];
158 for (i = Block.Start; i <= Block.End; i++)
159 {
160 s = Block.Rows[i];
161
162 j = s.IndexOf(':');
163 if (j < 0)
164 {
165 if (string.IsNullOrEmpty(Key))
166 break;
167
168 Values.Add(new KeyValuePair<string, bool>(s.Trim(), s.EndsWith(" ")));
169 }
170 else
171 {
172 s2 = s.Substring(0, j).TrimEnd().ToUpper();
173
174 if (string.IsNullOrEmpty(Key))
175 {
176 foreach (char ch in s2)
177 {
178 if (!char.IsLetter(ch) && !char.IsWhiteSpace(ch))
179 {
180 s2 = null;
181 break;
182 }
183 }
184
185 if (s2 is null)
186 break;
187 }
188 else
189 {
190 if (this.metaData.TryGetValue(Key, out Prev))
191 Values.InsertRange(0, Prev);
192
193 this.metaData[Key] = Values.ToArray();
194 }
195
196 Values.Clear();
197 Key = s2;
198 Values.Add(new KeyValuePair<string, bool>(s.Substring(j + 1).Trim(), s.EndsWith(" ")));
199 }
200 }
201
202 if (!string.IsNullOrEmpty(Key))
203 {
204 if (this.metaData.TryGetValue(Key, out Prev))
205 Values.InsertRange(0, Prev);
206 else if (string.Compare(Key, "Login", true) == 0)
207 this.isDynamic = true;
208
209 this.metaData[Key] = Values.ToArray();
210 Start++;
211 }
212 }
213
214 this.elements = this.ParseBlocks(Blocks, Start, End);
215
216 if (!(this.toInsert is null))
217 {
218 foreach (KeyValuePair<int, string> P in this.toInsert)
219 this.markdownText = this.markdownText.Insert(P.Key, P.Value);
220 }
221 }
222
226 [Obsolete("Use GenerateMarkdown() instead.")]
227 public string MarkdownText
228 {
229 get
230 {
231 return this.GenerateMarkdown(false).Result;
232 }
233 }
234
239 public Type[] TransparentExceptionTypes => this.transparentExceptionTypes;
240
246 public static int? HeaderEndPosition(string Markdown)
247 {
248 Match M = endOfHeader.Match(Markdown);
249 if (!M.Success)
250 return null;
251
252 string Header = Markdown.Substring(0, M.Index);
253 string[] Rows = Header.Split(CommonTypes.CRLF);
254 string s;
255
256 foreach (string Row in Rows)
257 {
258 s = Row.Trim();
259 if (string.IsNullOrEmpty(s))
260 continue;
261
262 if (s.IndexOf(':') < 0)
263 return null;
264 }
265
266 return M.Index;
267 }
268
277 public static async Task<string> Preprocess(string Markdown, MarkdownSettings Settings, params Type[] TransparentExceptionTypes)
278 {
279 KeyValuePair<string, bool> P = await Preprocess(Markdown, Settings, string.Empty, false, TransparentExceptionTypes);
280 return P.Key;
281 }
282
292 public static Task<KeyValuePair<string, bool>> Preprocess(string Markdown, MarkdownSettings Settings, string FileName, params Type[] TransparentExceptionTypes)
293 {
294 return Preprocess(Markdown, Settings, FileName, false, TransparentExceptionTypes);
295 }
296
308 public static async Task<KeyValuePair<string, bool>> Preprocess(string Markdown, MarkdownSettings Settings, string FileName, bool FromScript, params Type[] TransparentExceptionTypes)
309 {
310 if (Settings.Variables is null)
311 Settings.Variables = new Variables();
312
314 Expression Exp;
315 string Script, s2;
316 object Result;
317 int i, j;
318 bool IsDynamic = false;
319 bool UsesImplicitPrint = false;
320 bool HasImplicitPrint = false;
321
322 if (!string.IsNullOrEmpty(FileName))
323 {
324 Match M = endOfHeader.Match(Markdown);
325 if (M.Success)
326 {
327 s2 = Markdown.Substring(0, M.Index);
328
329 foreach (Match M2 in scriptHeader.Matches(s2))
330 {
331 if (M.Success)
332 {
333 string Tag = M2.Groups["Tag"].Value.ToUpper();
334 string FileName2 = M2.Groups["ScriptFile"].Value;
335
336 FileName2 = Settings.GetFileName(FileName, FileName2);
337
338 if (Tag == "INIT" && !await InitScriptFile.NeedsExecution(FileName2))
339 continue;
340
341 try
342 {
343 Script = await Resources.ReadAllTextAsync(FileName2);
344
345 if (!IsDynamic)
346 {
347 IsDynamic = true;
349 }
350
351 Exp = new Expression(Script, FileName2);
352
353 if (!(Settings.AuthorizeExpression is null))
354 {
355 ScriptNode Prohibited = await Settings.AuthorizeExpression(Exp);
356 if (!(Prohibited is null))
357 throw new UnauthorizedAccessException("Expression not permitted: " + Prohibited.SubExpression);
358 }
359
360 await Exp.EvaluateAsync(Variables);
361 }
362 catch (Exception ex)
363 {
364 Log.Exception(ex, FileName2);
365 }
366 }
367 }
368 }
369 }
370
371 i = Markdown.IndexOf("{{");
372
373 while (i >= 0)
374 {
375 j = Markdown.IndexOf("}}", i + 2);
376 if (j < 0)
377 break;
378
379 Script = Markdown.Substring(i + 2, j - i - 2);
380 Markdown = Markdown.Remove(i, j - i + 2);
381
382 try
383 {
384 Exp = new Expression(Script, FileName);
385
386 if (!(Settings.AuthorizeExpression is null))
387 {
388 ScriptNode Prohibited = await Settings.AuthorizeExpression(Exp);
389 if (!(Prohibited is null))
390 throw new UnauthorizedAccessException("Expression not permitted: " + Prohibited.SubExpression);
391 }
392
393 if (!IsDynamic)
394 {
395 IsDynamic = true;
397 }
398
399 HasImplicitPrint = Exp.ContainsImplicitPrint;
400 if (!HasImplicitPrint && UsesImplicitPrint && Exp.ReferencesImplicitPrint(Variables))
401 HasImplicitPrint = true;
402
403 if (HasImplicitPrint)
404 {
405 UsesImplicitPrint = true;
406
407 if (!FromScript)
408 await Variables.LockAsync();
409
410 ValuePrinter PrinterBak = Variables.Printer;
411 TextWriter Bak = Variables.ConsoleOut;
412 StringBuilder sb = new StringBuilder();
413
414 Variables.ConsoleOut = new StringWriter(sb);
415 Variables.Printer = PrintMarkdown;
416 try
417 {
418 await Exp.EvaluateAsync(Variables);
419 }
420 finally
421 {
422 Variables.ConsoleOut?.Flush();
423 Variables.ConsoleOut = Bak;
424 Variables.Printer = PrinterBak;
425
426 if (!FromScript)
428 }
429
430 Result = sb.ToString();
431 }
432 else
433 Result = await Exp.EvaluateAsync(Variables);
434 }
435 catch (Exception ex)
436 {
437 ex = Log.UnnestException(ex);
438
439 StringBuilder sb = new StringBuilder();
440
441 sb.AppendLine("<font class=\"error\">");
442
443 if (ex is AggregateException ex2)
444 {
445 foreach (Exception ex3 in ex2.InnerExceptions)
446 {
447 CheckException(ex3, TransparentExceptionTypes);
448
449 Log.Exception(ex3, FileName);
450
451 sb.Append("<p>");
452 sb.Append(XML.HtmlValueEncode(ex3.Message));
453 sb.AppendLine("</p>");
454 }
455 }
456 else
457 {
458 CheckException(ex, TransparentExceptionTypes);
459
461
462 sb.AppendLine(XML.HtmlValueEncode(ex.Message));
463 }
464
465 sb.AppendLine("</font>");
466
467 Result = sb.ToString();
468 }
469
470 if (!(Result is null))
471 {
472 if (!(Result is string s3))
473 s3 = await PrintMarkdown(Result, Variables);
474
475 Markdown = Markdown.Insert(i, s3);
476 i += s3.Length;
477 }
478
479 i = Markdown.IndexOf("{{", i);
480 }
481
482 return new KeyValuePair<string, bool>(Markdown, IsDynamic);
483 }
484
485 private static async Task<string> PrintMarkdown(object Value, Variables Variables)
486 {
487 if (Value is null)
488 return string.Empty;
489
490 if (Value.GetType().GetTypeInfo().IsValueType || Value is string)
491 return Value.ToString();
492
493 if (Value is XmlDocument ||
494 Value is IToMatrix ||
495 Value is Graph ||
496 Value is PixelInformation ||
497 Value is SKImage ||
498 Value is MarkdownDocument ||
499 Value is MarkdownContent ||
500 Value is Exception ||
501 Value is IMatrix ||
502 Value is Array)
503 {
505 {
506 await Renderer.RenderObject(Value, false, Variables);
507 return Renderer.ToString();
508 }
509 }
510 else
511 return Value.ToString();
512 }
513
514 internal void CheckException(Exception ex)
515 {
516 CheckException(ex, this.transparentExceptionTypes);
517 }
518
519 internal static void CheckException(Exception ex, Type[] TransparentExceptionTypes)
520 {
521 TypeInfo ExceptionType = ex.GetType().GetTypeInfo();
522
523 foreach (Type T in TransparentExceptionTypes)
524 {
525 if (T.GetTypeInfo().IsAssignableFrom(ExceptionType))
526 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
527 }
528 }
529
530 private LinkedList<MarkdownElement> ParseBlocks(List<Block> Blocks)
531 {
532 return this.ParseBlocks(Blocks, 0, Blocks.Count - 1);
533 }
534
535 private LinkedList<MarkdownElement> ParseBlocks(List<Block> Blocks, int StartBlock, int EndBlock)
536 {
537 LinkedList<MarkdownElement> Elements = new LinkedList<MarkdownElement>();
538 LinkedList<MarkdownElement> Content;
539 List<Block> AlignedBlocks;
540 Block NextBlock;
541 Block Block;
542 string[] Rows;
543 string s, s2;
544 string InitialSectionSeparator = null;
545 int BlockIndex;
546 int i, j, c, d;
547 int Index;
548 int SectionNr = 0;
549 int InitialNrColumns = 1;
550 bool HasSections = false;
551
552 for (BlockIndex = StartBlock; BlockIndex <= EndBlock; BlockIndex++)
553 {
554 Block = Blocks[BlockIndex];
555
556 if (Block.Indent > 0)
557 {
558 c = Block.Indent;
559 i = BlockIndex + 1;
560 while (i <= EndBlock && (j = Blocks[i].Indent) > 0)
561 {
562 i++;
563 if (j < c)
564 c = j;
565 }
566
567 if (i == BlockIndex + 1)
568 Elements.AddLast(new CodeBlock(this, Block.Rows, Block.Start, Block.End, c - 1));
569 else
570 {
571 List<string> CodeBlock = new List<string>();
572
573 while (BlockIndex < i)
574 {
575 if (CodeBlock.Count > 0)
576 CodeBlock.Add(string.Empty);
577
578 Block = Blocks[BlockIndex++];
579
580 if (Block.Indent == c)
581 {
582 for (j = Block.Start; j <= Block.End; j++)
583 CodeBlock.Add(Block.Rows[j]);
584 }
585 else
586 {
587 s = new string('\t', Block.Indent - c);
588 for (j = Block.Start; j <= Block.End; j++)
589 CodeBlock.Add(s + Block.Rows[j]);
590 }
591 }
592
593 Elements.AddLast(new CodeBlock(this, CodeBlock.ToArray(), 0, CodeBlock.Count - 1, c - 1));
594 BlockIndex--;
595 }
596 continue;
597 }
598 else if (Block.IsPrefixedBy("```", false))
599 {
600 s = Block.Rows[Block.Start];
601 i = 0;
602 foreach (char ch in s)
603 {
604 if (ch == '`')
605 i++;
606 else
607 break;
608 }
609
610 s = s.Substring(0, i);
611
612 i = BlockIndex;
613 while (i <= EndBlock &&
614 (!(Block = Blocks[i]).Rows[Block.End].StartsWith(s) ||
615 (i == BlockIndex && Block.Start == Block.End)))
616 {
617 i++;
618 }
619
620 List<string> Code = new List<string>();
621 bool Complete = true;
622
623 if (i > EndBlock)
624 {
625 i = EndBlock;
626 Complete = false;
627 }
628
629 for (j = BlockIndex; j <= i; j++)
630 {
631 Block = Blocks[j];
632 if (j == BlockIndex)
633 Index = Block.Start + 1;
634 else
635 {
636 Code.Add(string.Empty);
637 Index = Block.Start;
638 }
639
640 if (j == i && Complete)
641 c = Block.End - 1;
642 else
643 c = Block.End;
644
645 while (Index <= c)
646 {
647 Code.Add(Block.Rows[Index]);
648 Index++;
649 }
650 }
651
652 Block = Blocks[BlockIndex];
653 s = Block.Rows[Block.Start].Substring(3).Trim('`', ' ', '\t');
654
656
657 if (s.StartsWith("base64", StringComparison.CurrentCultureIgnoreCase))
658 {
659 try
660 {
661 StringBuilder sb = new StringBuilder();
662
663 foreach (string Row in Code)
664 sb.Append(Row);
665
666 byte[] Bin = Convert.FromBase64String(sb.ToString());
667 s2 = Encoding.UTF8.GetString(Bin);
668
669 Rows = s2.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n');
670
671 CodeBlock = new CodeBlock(this, Rows, 0, Rows.Length - 1, 0, s.Substring(6));
672 }
673 catch (Exception)
674 {
675 CodeBlock = new CodeBlock(this, Code.ToArray(), 0, Code.Count - 1, 0, s);
676 }
677 }
678 else
679 CodeBlock = new CodeBlock(this, Code.ToArray(), 0, Code.Count - 1, 0, s);
680
681 Elements.AddLast(CodeBlock);
682
683 if (!this.syntaxHighlighting && !string.IsNullOrEmpty(CodeBlock.Language))
684 {
686 if (HtmlRenderer is null)
687 this.syntaxHighlighting = true;
688 }
689
690 BlockIndex = i;
691 continue;
692 }
693
694 if (Block.IsPrefixedBy(">", false))
695 {
696 if (Block.IsSuffixedBy("<<") && Block.IsPrefixedBy(">>", false))
697 {
698 AlignedBlocks = Block.RemovePrefixAndSuffix(">>", 2, "<<");
699
700 while (BlockIndex < EndBlock &&
701 (NextBlock = Blocks[BlockIndex + 1]).IsPrefixedBy(">>", false) &&
702 NextBlock.IsSuffixedBy("<<"))
703 {
704 BlockIndex++;
705 AlignedBlocks.AddRange(NextBlock.RemovePrefixAndSuffix(">>", 2, "<<"));
706 }
707
708 Content = this.ParseBlocks(AlignedBlocks);
709
710 if (Elements.Last?.Value is CenterAligned CenterAligned)
711 CenterAligned.AddChildren(Content);
712 else
713 Elements.AddLast(new CenterAligned(this, Content));
714 }
715 else if (Block.IsSuffixedBy(">>"))
716 {
717 AlignedBlocks = Block.RemoveSuffix(">>");
718
719 while (BlockIndex < EndBlock &&
720 (NextBlock = Blocks[BlockIndex + 1]).IsSuffixedBy(">>"))
721 {
722 BlockIndex++;
723 AlignedBlocks.AddRange(NextBlock.RemoveSuffix(">>"));
724 }
725
726 Content = this.ParseBlocks(AlignedBlocks);
727
728 if (Elements.Last?.Value is RightAligned RightAligned)
729 RightAligned.AddChildren(Content);
730 else
731 Elements.AddLast(new RightAligned(this, Content));
732 }
733 else
734 {
735 Content = this.ParseBlocks(Block.RemovePrefix(">", 2));
736
737 if (Elements.Last?.Value is BlockQuote BlockQuote)
738 BlockQuote.AddChildren(Content);
739 else
740 Elements.AddLast(new BlockQuote(this, Content));
741 }
742
743 continue;
744 }
745 else if (Block.IsPrefixedBy("<<", false))
746 {
747 if (Block.IsSuffixedBy(">>"))
748 {
749 AlignedBlocks = Block.RemovePrefixAndSuffix("<<", 2, ">>");
750
751 while (BlockIndex < EndBlock &&
752 (NextBlock = Blocks[BlockIndex + 1]).IsPrefixedBy("<<", false) &&
753 NextBlock.IsSuffixedBy(">>"))
754 {
755 BlockIndex++;
756 AlignedBlocks.AddRange(NextBlock.RemovePrefixAndSuffix("<<", 2, ">>"));
757 }
758
759 Content = this.ParseBlocks(AlignedBlocks);
760
761 if (Elements.Last?.Value is MarginAligned MarginAligned)
762 MarginAligned.AddChildren(Content);
763 else
764 Elements.AddLast(new MarginAligned(this, Content));
765 }
766 else
767 {
768 AlignedBlocks = Block.RemovePrefix("<<", 2);
769
770 while (BlockIndex < EndBlock &&
771 (NextBlock = Blocks[BlockIndex + 1]).IsPrefixedBy("<<", false))
772 {
773 BlockIndex++;
774 AlignedBlocks.AddRange(NextBlock.RemovePrefix("<<", 2));
775 }
776
777 Content = this.ParseBlocks(AlignedBlocks);
778
779 if (Elements.Last?.Value is LeftAligned LeftAligned)
780 LeftAligned.AddChildren(Content);
781 else
782 Elements.AddLast(new LeftAligned(this, Content));
783 }
784
785 continue;
786 }
787 else if (Block.IsSuffixedBy(">>"))
788 {
789 Content = this.ParseBlocks(Block.RemoveSuffix(">>"));
790
791 if (Elements.Last?.Value is RightAligned RightAligned)
792 RightAligned.AddChildren(Content);
793 else
794 Elements.AddLast(new RightAligned(this, Content));
795
796 continue;
797 }
798 else if (Block.IsPrefixedBy("+>", false))
799 {
800 Content = this.ParseBlocks(Block.RemovePrefix("+>", 3));
801
802 if (Elements.Last?.Value is InsertBlocks InsertBlocks)
803 InsertBlocks.AddChildren(Content);
804 else
805 Elements.AddLast(new InsertBlocks(this, Content));
806
807 continue;
808 }
809 else if (Block.IsPrefixedBy("->", false))
810 {
811 Content = this.ParseBlocks(Block.RemovePrefix("->", 3));
812
813 if (Elements.Last?.Value is DeleteBlocks DeleteBlocks)
814 DeleteBlocks.AddChildren(Content);
815 else
816 Elements.AddLast(new DeleteBlocks(this, Content));
817
818 continue;
819 }
820 else if (Block.IsPrefixedBy("//", false))
821 {
822 string[] Comment = new string[Block.End - Block.Start + 1];
823
824 for (i = Block.Start; i <= Block.End; i++)
825 Comment[i] = Block.Rows[i].Substring(2);
826
827 Elements.AddLast(new CommentBlock(this, Comment));
828 continue;
829 }
830 else if (Block.End == Block.Start && (IsUnderline(Block.Rows[0], '-', true, true) || IsUnderline(Block.Rows[0], '*', true, true)))
831 {
832 Elements.AddLast(new HorizontalRule(this, Block.Rows[0]));
833 continue;
834 }
835 else if (Block.End == Block.Start && (IsUnderline(Block.Rows[0], '=', true, false)))
836 {
837 int NrColumns = Block.Rows[0].Split(whiteSpace, StringSplitOptions.RemoveEmptyEntries).Length;
838 HasSections = true;
839
840 if (Elements.First is null)
841 {
842 InitialNrColumns = NrColumns;
843 InitialSectionSeparator = Block.Rows[0];
844 }
845 else
846 Elements.AddLast(new SectionSeparator(this, ++SectionNr, NrColumns, Block.Rows[0]));
847 continue;
848 }
849 else if (Block.End == Block.Start && IsUnderline(Block.Rows[0], '~', false, false))
850 {
851 Elements.AddLast(new InvisibleBreak(this, Block.Rows[0]));
852 continue;
853 }
854 else if (Block.IsPrefixedBy(s2 = "*", true) ||
855 Block.IsPrefixedBy(s2 = "+", true) ||
856 Block.IsPrefixedBy(s2 = "-", true))
857 {
858 LinkedList<Block> Segments = null;
859 i = 0;
860 c = Block.End;
861
862 for (d = Block.Start + 1; d <= c; d++)
863 {
864 s = Block.Rows[d];
865 if (IsPrefixedBy(s, s2, true))
866 {
867 if (Segments is null)
868 Segments = new LinkedList<Block>();
869
870 Segments.AddLast(new Block(Block.Rows, Block.Positions, 0, i, d - 1));
871 i = d;
872 }
873 }
874
875 Segments?.AddLast(new Block(Block.Rows, Block.Positions, 0, i, c));
876
877 LinkedList<MarkdownElement> Items;
878 UnnumberedItem LastItem;
879
880 if (Segments is null)
881 {
882 List<Block> SubBlocks = Block.RemovePrefix(s2, 4);
883
884 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 1)
885 {
886 BlockIndex++;
887 Block.Indent--;
888 SubBlocks.Add(Block);
889 }
890
891 Items = this.ParseBlocks(SubBlocks);
892 LastItem = new UnnumberedItem(this, s2, new NestedBlock(this, Items));
893
894 if (Elements.Last?.Value is BulletList BulletList)
895 BulletList.AddChildren(LastItem);
896 else
897 Elements.AddLast(new BulletList(this, LastItem));
898 }
899 else
900 {
901 Items = new LinkedList<MarkdownElement>();
902 LastItem = null;
903
904 foreach (Block Segment in Segments)
905 {
906 foreach (Block SegmentItem in Segment.RemovePrefix(s2, 4))
907 {
908 LastItem = new UnnumberedItem(this, s2, new NestedBlock(this, this.ParseBlock(SegmentItem)));
909 Items.AddLast(LastItem);
910 }
911 }
912
913 if (Elements.Last?.Value is BulletList BulletList)
914 BulletList.AddChildren(Items);
915 else
916 Elements.AddLast(new BulletList(this, Items));
917 }
918
919 if (!(LastItem is null))
920 {
921 i = BlockIndex;
922 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 0)
923 {
924 BlockIndex++;
925 Block.Indent--;
926 }
927
928 if (BlockIndex > i)
929 {
930 Items = this.ParseBlocks(Blocks, i + 1, BlockIndex);
931
932 if (LastItem.Child is NestedBlock LastItemChildren)
933 {
934 if (LastItemChildren.IsBlockElement)
935 LastItemChildren.AddChildren(Items);
936 else
937 {
938 Items.AddFirst(new Paragraph(this, LastItemChildren.Children, true));
939 LastItem.Child = new NestedBlock(this, Items);
940 }
941 }
942 else
943 {
944 if (LastItem.Child.IsBlockElement)
945 Items.AddFirst(LastItem.Child);
946 else
947 Items.AddFirst(new Paragraph(this, new MarkdownElement[] { LastItem.Child }, true));
948
949 LastItem.Child = new NestedBlock(this, Items);
950 }
951 }
952 }
953
954 continue;
955 }
956 else if (Block.IsPrefixedBy("#.", true))
957 {
958 LinkedList<Tuple<int, bool, Block>> Segments = null;
959 i = 0;
960 c = Block.End;
961 int Index2 = 1;
962 bool Explicit = false;
963
964 for (d = Block.Start + 1; d <= c; d++)
965 {
966 s = Block.Rows[d];
967 if (IsPrefixedByNumber(s, out j))
968 {
969 if (Segments is null)
970 Segments = new LinkedList<Tuple<int, bool, Block>>();
971
972 Segments.AddLast(new Tuple<int, bool, Block>(Index2, Explicit, new Block(Block.Rows, Block.Positions, 0, i, d - 1)));
973 i = d;
974 Index2 = j;
975 Explicit = true;
976 }
977 else if (IsPrefixedBy(s, "#.", true))
978 {
979 if (Segments is null)
980 Segments = new LinkedList<Tuple<int, bool, Block>>();
981
982 Segments.AddLast(new Tuple<int, bool, Block>(Index2, Explicit, new Block(Block.Rows, Block.Positions, 0, i, d - 1)));
983 i = d;
984 Index2++;
985 Explicit = false;
986 }
987 }
988
989 Segments?.AddLast(new Tuple<int, bool, Block>(Index2, Explicit, new Block(Block.Rows, Block.Positions, 0, i, c)));
990
991 LinkedList<MarkdownElement> Items;
992 NumberedItem LastItem;
993
994 if (Segments is null)
995 {
996 List<Block> SubBlocks = Block.RemovePrefix("#.", 4);
997
998 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 1)
999 {
1000 BlockIndex++;
1001 Block.Indent--;
1002 SubBlocks.Add(Block);
1003 }
1004
1005 Items = this.ParseBlocks(SubBlocks);
1006 LastItem = new NumberedItem(this, Index2, Explicit, new NestedBlock(this, Items));
1007
1008 if (Elements.Last?.Value is NumberedList NumberedList)
1009 NumberedList.AddChildren(LastItem);
1010 else
1011 Elements.AddLast(new NumberedList(this, LastItem));
1012 }
1013 else
1014 {
1015 Items = new LinkedList<MarkdownElement>();
1016 LastItem = null;
1017
1018 foreach (Tuple<int, bool, Block> Segment in Segments)
1019 {
1020 s = Segment.Item2 ? Segment.Item1.ToString() + "." : "#.";
1021 foreach (Block SegmentItem in Segment.Item3.RemovePrefix(s, Math.Max(4, s.Length + 2)))
1022 {
1023 LastItem = new NumberedItem(this, Segment.Item1, Segment.Item2,
1024 new NestedBlock(this, this.ParseBlock(SegmentItem)));
1025
1026 Items.AddLast(LastItem);
1027 }
1028 }
1029
1030 if (Elements.Last?.Value is NumberedList NumberedList)
1032 else
1033 Elements.AddLast(new NumberedList(this, Items));
1034 }
1035
1036 if (!(LastItem is null))
1037 {
1038 i = BlockIndex;
1039 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 0)
1040 {
1041 BlockIndex++;
1042 Block.Indent--;
1043 }
1044
1045 if (BlockIndex > i)
1046 {
1047 Items = this.ParseBlocks(Blocks, i + 1, BlockIndex);
1048
1049 if (LastItem.Child is NestedBlock LastItemChildren)
1050 {
1051 if (LastItemChildren.IsBlockElement)
1052 LastItemChildren.AddChildren(Items);
1053 else
1054 {
1055 Items.AddFirst(new Paragraph(this, LastItemChildren.Children, true));
1056 LastItem.Child = new NestedBlock(this, Items);
1057 }
1058 }
1059 else
1060 {
1061 if (LastItem.Child.IsBlockElement)
1062 Items.AddFirst(LastItem.Child);
1063 else
1064 Items.AddFirst(new Paragraph(this, new MarkdownElement[] { LastItem.Child }, true));
1065
1066 LastItem.Child = new NestedBlock(this, Items);
1067 }
1068 }
1069 }
1070
1071 continue;
1072 }
1073 else if (Block.IsPrefixedBy(s2 = "[ ]", true) ||
1074 Block.IsPrefixedBy(s2 = "[x]", true) ||
1075 Block.IsPrefixedBy(s2 = "[X]", true))
1076 {
1077 LinkedList<Tuple<Block, string, int>> Segments = null;
1078 int CheckPosition = Block.Positions[0] + 1;
1079 string s3;
1080 i = 0;
1081 c = Block.End;
1082
1083 for (d = Block.Start + 1; d <= c; d++)
1084 {
1085 s = Block.Rows[d];
1086 if (IsPrefixedBy(s, s3 = "[ ]", true) ||
1087 IsPrefixedBy(s, s3 = "[x]", true) ||
1088 IsPrefixedBy(s, s3 = "[X]", true))
1089 {
1090 if (Segments is null)
1091 Segments = new LinkedList<Tuple<Block, string, int>>();
1092
1093 Segments.AddLast(new Tuple<Block, string, int>(new Block(Block.Rows, Block.Positions, 0, i, d - 1), s2, CheckPosition));
1094 s2 = s3;
1095 i = d;
1096 CheckPosition = Block.Positions[d] + 1;
1097 }
1098 }
1099
1100 Segments?.AddLast(new Tuple<Block, string, int>(new Block(Block.Rows, Block.Positions, 0, i, c), s2, CheckPosition));
1101
1102 LinkedList<MarkdownElement> Items;
1103 TaskItem LastItem;
1104
1105 if (Segments is null)
1106 {
1107 List<Block> SubBlocks = Block.RemovePrefix(s2, 4);
1108
1109 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 1)
1110 {
1111 BlockIndex++;
1112 Block.Indent--;
1113 SubBlocks.Add(Block);
1114 }
1115
1116 Items = this.ParseBlocks(SubBlocks);
1117 LastItem = new TaskItem(this, s2 != "[ ]", CheckPosition, new NestedBlock(this, Items));
1118
1119 if (Elements.Last?.Value is TaskList TaskList)
1120 TaskList.AddChildren(LastItem);
1121 else
1122 Elements.AddLast(new TaskList(this, LastItem));
1123 }
1124 else
1125 {
1126 Items = new LinkedList<MarkdownElement>();
1127 LastItem = null;
1128
1129 foreach (Tuple<Block, string, int> Segment in Segments)
1130 {
1131 foreach (Block SegmentItem in Segment.Item1.RemovePrefix(Segment.Item2, 4))
1132 {
1133 LastItem = new TaskItem(this, Segment.Item2 != "[ ]", Segment.Item3,
1134 new NestedBlock(this, this.ParseBlock(SegmentItem)));
1135
1136 Items.AddLast(LastItem);
1137 }
1138 }
1139
1140 if (Elements.Last?.Value is TaskList TaskList)
1141 TaskList.AddChildren(Items);
1142 else
1143 Elements.AddLast(new TaskList(this, Items));
1144 }
1145
1146 if (!(LastItem is null))
1147 {
1148 i = BlockIndex;
1149 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 0)
1150 {
1151 BlockIndex++;
1152 Block.Indent--;
1153 }
1154
1155 if (BlockIndex > i)
1156 {
1157 Items = this.ParseBlocks(Blocks, i + 1, BlockIndex);
1158
1159 if (LastItem.Child is NestedBlock LastItemChildren)
1160 {
1161 if (LastItemChildren.IsBlockElement)
1162 LastItemChildren.AddChildren(Items);
1163 else
1164 {
1165 Items.AddFirst(new Paragraph(this, LastItemChildren.Children, true));
1166 LastItem.Child = new NestedBlock(this, Items);
1167 }
1168 }
1169 else
1170 {
1171 if (LastItem.Child.IsBlockElement)
1172 Items.AddFirst(LastItem.Child);
1173 else
1174 Items.AddFirst(new Paragraph(this, new MarkdownElement[] { LastItem.Child }, true));
1175
1176 LastItem.Child = new NestedBlock(this, Items);
1177 }
1178 }
1179 }
1180
1181 continue;
1182 }
1183 else if (Block.IsPrefixedByNumber(out Index))
1184 {
1185 LinkedList<Tuple<int, bool, Block>> Segments = null;
1186 i = 0;
1187 c = Block.End;
1188 bool Explicit = true;
1189
1190 for (d = Block.Start + 1; d <= c; d++)
1191 {
1192 s = Block.Rows[d];
1193 if (IsPrefixedByNumber(s, out j))
1194 {
1195 if (Segments is null)
1196 Segments = new LinkedList<Tuple<int, bool, Block>>();
1197
1198 Segments.AddLast(new Tuple<int, bool, Block>(Index, Explicit, new Block(Block.Rows, Block.Positions, 0, i, d - 1)));
1199 i = d;
1200 Index = j;
1201 Explicit = true;
1202 }
1203 else if (IsPrefixedBy(s, "#.", true))
1204 {
1205 if (Segments is null)
1206 Segments = new LinkedList<Tuple<int, bool, Block>>();
1207
1208 Segments.AddLast(new Tuple<int, bool, Block>(Index, Explicit, new Block(Block.Rows, Block.Positions, 0, i, d - 1)));
1209 i = d;
1210 Index++;
1211 Explicit = false;
1212 }
1213 }
1214
1215 Segments?.AddLast(new Tuple<int, bool, Block>(Index, Explicit, new Block(Block.Rows, Block.Positions, 0, i, c)));
1216
1217 LinkedList<MarkdownElement> Items;
1218 NumberedItem LastItem;
1219
1220 if (Segments is null)
1221 {
1222 s = Index.ToString();
1223 List<Block> SubBlocks = Block.RemovePrefix(s + ".", Math.Max(4, s.Length + 2));
1224
1225 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 1)
1226 {
1227 BlockIndex++;
1228 Block.Indent--;
1229 SubBlocks.Add(Block);
1230 }
1231
1232 Items = this.ParseBlocks(SubBlocks);
1233 LastItem = new NumberedItem(this, Index, Explicit, new NestedBlock(this, Items));
1234
1235 if (Elements.Last?.Value is NumberedList NumberedList)
1236 NumberedList.AddChildren(LastItem);
1237 else
1238 Elements.AddLast(new NumberedList(this, LastItem));
1239 }
1240 else
1241 {
1242 Items = new LinkedList<MarkdownElement>();
1243 LastItem = null;
1244
1245 foreach (Tuple<int, bool, Block> Segment in Segments)
1246 {
1247 s = Segment.Item2 ? Segment.Item1.ToString() + "." : "#.";
1248 foreach (Block SegmentItem in Segment.Item3.RemovePrefix(s, Math.Max(4, s.Length + 2)))
1249 {
1250 LastItem = new NumberedItem(this, Segment.Item1, Segment.Item2,
1251 new NestedBlock(this, this.ParseBlock(SegmentItem)));
1252
1253 Items.AddLast(LastItem);
1254 }
1255 }
1256
1257 if (Elements.Last?.Value is NumberedList NumberedList)
1259 else
1260 Elements.AddLast(new NumberedList(this, Items));
1261 }
1262
1263 if (!(LastItem is null))
1264 {
1265 i = BlockIndex;
1266 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 0)
1267 {
1268 BlockIndex++;
1269 Block.Indent--;
1270 }
1271
1272 if (BlockIndex > i)
1273 {
1274 Items = this.ParseBlocks(Blocks, i + 1, BlockIndex);
1275
1276 if (LastItem.Child is NestedBlock LastItemChildren)
1277 {
1278 if (LastItemChildren.IsBlockElement)
1279 LastItemChildren.AddChildren(Items);
1280 else
1281 {
1282 Items.AddFirst(new Paragraph(this, LastItemChildren.Children, true));
1283 LastItem.Child = new NestedBlock(this, Items);
1284 }
1285 }
1286 else
1287 {
1288 if (LastItem.Child.IsBlockElement)
1289 Items.AddFirst(LastItem.Child);
1290 else
1291 Items.AddFirst(new Paragraph(this, new MarkdownElement[] { LastItem.Child }, true));
1292
1293 LastItem.Child = new NestedBlock(this, Items);
1294 }
1295 }
1296 }
1297
1298 continue;
1299 }
1300 else if (Block.IsTable(out TableInformation TableInformation))
1301 {
1302 MarkdownElement[][] Headers = new MarkdownElement[TableInformation.NrHeaderRows][];
1303 MarkdownElement[][] DataRows = new MarkdownElement[TableInformation.NrDataRows][];
1304 TextAlignment?[][] HeaderCellAlignments = new TextAlignment?[TableInformation.NrHeaderRows][];
1305 TextAlignment?[][] DataCellAlignments = new TextAlignment?[TableInformation.NrDataRows][];
1306 LinkedList<MarkdownElement> CellElements;
1307 string[] Row;
1308 int[] Positions;
1309
1310 c = TableInformation.Columns;
1311
1312 for (j = 0; j < TableInformation.NrHeaderRows; j++)
1313 {
1314 Row = TableInformation.Headers[j];
1315 Positions = TableInformation.HeaderPositions[j];
1316
1317 Headers[j] = new MarkdownElement[c];
1318 HeaderCellAlignments[j] = new TextAlignment?[c];
1319
1320 for (i = 0; i < c; i++)
1321 {
1322 s = Row[i];
1323 if (s is null)
1324 {
1325 Headers[j][i] = null;
1326 HeaderCellAlignments[j][i] = null;
1327 }
1328 else
1329 {
1330 CellElements = this.ParseCell(Row[i], Positions[i], out HeaderCellAlignments[j][i]);
1331
1332 if (!(CellElements.First is null) && CellElements.First.Next is null)
1333 {
1334 if (CellElements.First.Value is FootnoteReference FRef)
1335 {
1336 FRef.AutoExpand = true;
1337
1338 if (this.footnotes.TryGetValue(FRef.Key, out Footnote Note))
1339 Note.TableCellContents = true;
1340
1341 if (this.footnoteNumberByKey.TryGetValue(FRef.Key, out int Nr) &&
1342 Nr == this.lastFootnote)
1343 {
1344 this.footnoteNumberByKey.Remove(FRef.Key);
1345 this.lastFootnote--;
1346 }
1347 }
1348
1349 Headers[j][i] = CellElements.First.Value;
1350 }
1351 else
1352 Headers[j][i] = new NestedBlock(this, CellElements);
1353 }
1354 }
1355 }
1356
1357 for (j = 0; j < TableInformation.NrDataRows; j++)
1358 {
1359 Row = TableInformation.Rows[j];
1360 Positions = TableInformation.RowPositions[j];
1361
1362 DataRows[j] = new MarkdownElement[c];
1363 DataCellAlignments[j] = new TextAlignment?[c];
1364
1365 for (i = 0; i < c; i++)
1366 {
1367 s = Row[i];
1368 if (s is null)
1369 {
1370 DataRows[j][i] = null;
1371 DataCellAlignments[j][i] = null;
1372 }
1373 else
1374 {
1375 CellElements = this.ParseCell(Row[i], Positions[i], out DataCellAlignments[j][i]);
1376
1377 if (!(CellElements.First is null) && CellElements.First.Next is null)
1378 {
1379 if (CellElements.First.Value is FootnoteReference FRef)
1380 {
1381 FRef.AutoExpand = true;
1382
1383 if (this.footnotes.TryGetValue(FRef.Key, out Footnote Note))
1384 Note.TableCellContents = true;
1385
1386 if (this.footnoteNumberByKey.TryGetValue(FRef.Key, out int Nr) &&
1387 Nr == this.lastFootnote)
1388 {
1389 this.footnoteNumberByKey.Remove(FRef.Key);
1390 this.lastFootnote--;
1391 }
1392 }
1393
1394 DataRows[j][i] = CellElements.First.Value;
1395 }
1396 else
1397 DataRows[j][i] = new NestedBlock(this, CellElements);
1398 }
1399 }
1400 }
1401
1402 Elements.AddLast(new Table(this, c, Headers, DataRows, TableInformation.Alignments, TableInformation.AlignmentDefinitions,
1403 HeaderCellAlignments, DataCellAlignments, TableInformation.Caption, TableInformation.Id));
1404
1405 continue;
1406 }
1407 else if (Block.IsPrefixedBy(":", true) && !(Elements.Last is null))
1408 {
1409 LinkedList<MarkdownElement> Description = this.ParseBlocks(Block.RemovePrefix(":", 4));
1411
1412 i = BlockIndex;
1413 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 0)
1414 {
1415 BlockIndex++;
1416 Block.Indent--;
1417 }
1418
1419 if (BlockIndex > i)
1420 {
1421 foreach (MarkdownElement E in this.ParseBlocks(Blocks, i + 1, BlockIndex))
1422 Description.AddLast(E);
1423 }
1424
1425 if (Description.First is null)
1426 continue;
1427
1428 if (Description.First.Next is null)
1430 else
1432
1433 if (Elements.Last.Value is DefinitionDescriptions DefinitionDescriptions2)
1434 DefinitionDescriptions2.AddChildren(DefinitionDescriptions.Children);
1435 else if (Elements.Last.Value is DefinitionTerms DefinitionTerms)
1436 Elements.Last.Value = new DefinitionList(this, DefinitionTerms, DefinitionDescriptions);
1437 else if (Elements.Last.Value is DefinitionList DefinitionList)
1439 else
1441
1442 continue;
1443 }
1444 else if (BlockIndex < EndBlock && Blocks[BlockIndex + 1].IsPrefixedBy(":", true))
1445 {
1446 LinkedList<MarkdownElement> Terms = new LinkedList<MarkdownElement>();
1447 LinkedList<MarkdownElement> Term;
1448
1449 Rows = Block.Rows;
1450 c = Block.End;
1451 for (i = Block.Start; i <= c; i++)
1452 {
1453 Term = this.ParseBlock(Rows, Block.Positions, i, i);
1454 if (Term.First is null)
1455 continue;
1456
1457 if (Term.First.Next is null)
1458 Terms.AddLast(Term.First.Value);
1459 else
1460 Terms.AddLast(new NestedBlock(this, Term));
1461 }
1462
1463 if (Elements.Last?.Value is DefinitionList DefinitionList)
1464 DefinitionList.AddChildren(new DefinitionTerms(this, Terms));
1465 else
1466 Elements.AddLast(new DefinitionTerms(this, Terms));
1467
1468 continue;
1469 }
1470 else if (Block.IsFootnote(out s, out int WhiteSparePrefix))
1471 {
1472 Footnote Footnote = new Footnote(this, s,
1473 this.ParseBlocks(Block.RemovePrefix(string.Empty, WhiteSparePrefix)));
1474
1475 i = BlockIndex;
1476 while (BlockIndex < EndBlock && (Block = Blocks[BlockIndex + 1]).Indent > 0)
1477 {
1478 BlockIndex++;
1479 Block.Indent--;
1480 }
1481
1482 if (BlockIndex > i)
1483 Footnote.AddChildren(this.ParseBlocks(Blocks, i + 1, BlockIndex));
1484
1485 if (this.footnoteNumberByKey is null)
1486 {
1487 this.footnoteNumberByKey = new Dictionary<string, int>();
1488 this.footnoteOrder = new List<string>();
1489 this.footnotes = new Dictionary<string, Footnote>();
1490 }
1491
1492 this.footnotes[Footnote.Key] = Footnote;
1493
1494 continue;
1495 }
1496
1497 Rows = Block.Rows;
1498 c = Block.End;
1499
1500 if (c >= 1)
1501 {
1502 s = Rows[c];
1503
1504 if (IsUnderline(s, '=', false, false))
1505 {
1506 Header Header = new Header(this, 1, false, s, this.PrepareHeader(this.ParseBlock(Rows, Block.Positions, 0, c - 1)));
1507 Elements.AddLast(Header);
1508 this.headers.Add(Header);
1509 continue;
1510 }
1511 else if (IsUnderline(s, '-', false, false))
1512 {
1513 Header Header = new Header(this, 2, false, s, this.PrepareHeader(this.ParseBlock(Rows, Block.Positions, 0, c - 1)));
1514 Elements.AddLast(Header);
1515 this.headers.Add(Header);
1516 continue;
1517 }
1518 }
1519
1520 s = Rows[Block.Start];
1521 if (IsPrefixedBy(s, '#', out d, true) && d < s.Length)
1522 {
1523 string Prefix = s.Substring(0, d);
1524 Rows[Block.Start] = s.Substring(d).Trim();
1525
1526 s = Rows[c];
1527 i = s.Length - 1;
1528 while (i >= 0 && s[i] == '#')
1529 i--;
1530
1531 if (++i < s.Length)
1532 Rows[c] = s.Substring(0, i).TrimEnd();
1533
1534 Header Header = new Header(this, d, true, Prefix, this.PrepareHeader(this.ParseBlock(Rows, Block.Positions, Block.Start, c)));
1535 Elements.AddLast(Header);
1536 this.headers.Add(Header);
1537 continue;
1538 }
1539
1540 Content = this.ParseBlock(Block, Blocks, ref BlockIndex, EndBlock);
1541 if (!(Content.First is null))
1542 {
1543 if (Content.First.Value is InlineHTML && Content.Last.Value is InlineHTML && this.settings.AllowHtml)
1544 Elements.AddLast(new HtmlBlock(this, Content));
1545 else if (Content.First.Next is null && Content.First.Value.OutsideParagraph)
1546 {
1547 if (Content.First.Value is MarkdownElementChildren MarkdownElementChildren &&
1548 MarkdownElementChildren.JoinOverParagraphs && !(Elements.Last is null) &&
1549 Elements.Last.Value is MarkdownElementChildren MarkdownElementChildrenLast)
1550 {
1551 MarkdownElementChildrenLast.AddChildren(MarkdownElementChildren.Children);
1552 }
1553 else
1554 Elements.AddLast(Content.First.Value);
1555 }
1556 else
1557 Elements.AddLast(new Paragraph(this, Content));
1558 }
1559 }
1560
1561 if (HasSections)
1562 {
1563 LinkedList<MarkdownElement> Sections = new LinkedList<MarkdownElement>();
1564 Sections.AddLast(new Sections(this, InitialNrColumns, InitialSectionSeparator, Elements));
1565 return Sections;
1566 }
1567 else
1568 return Elements;
1569 }
1570
1571 private LinkedList<MarkdownElement> PrepareHeader(LinkedList<MarkdownElement> Content)
1572 {
1573 if (Content?.First?.Value is NumberedList NumberedList &&
1574 Content.First.Next is null &&
1577 Item.NumberExplicit)
1578 {
1579 LinkedList<MarkdownElement> NewContent = new LinkedList<MarkdownElement>();
1580 NewContent.AddLast(new InlineText(this, Item.Number.ToString() + ". "));
1581
1582 if (Item.Child is NestedBlock B)
1583 {
1584 foreach (MarkdownElement E in B.Children)
1585 NewContent.AddLast(E);
1586 }
1587 else
1588 NewContent.AddLast(Item.Child);
1589
1590 return NewContent;
1591 }
1592 else
1593 return Content;
1594 }
1595
1596 private LinkedList<MarkdownElement> ParseCell(string Cell, int Position, out TextAlignment? Alignment)
1597 {
1598 if (Cell.StartsWith("<<"))
1599 {
1600 Position += 2;
1601
1602 if (Cell.EndsWith(">>"))
1603 {
1604 Alignment = TextAlignment.Center;
1605 Cell = Cell.Substring(2, Cell.Length - 4);
1606 }
1607 else
1608 {
1609 Alignment = TextAlignment.Left;
1610 Cell = Cell.Substring(2);
1611 }
1612 }
1613 else if (Cell.StartsWith(">>") && Cell.EndsWith("<<"))
1614 {
1615 Alignment = TextAlignment.Center;
1616 Cell = Cell.Substring(2, Cell.Length - 4);
1617 Position += 2;
1618 }
1619 else if (Cell.EndsWith(">>"))
1620 {
1621 Alignment = TextAlignment.Right;
1622 Cell = Cell.Substring(0, Cell.Length - 2);
1623 }
1624 else
1625 Alignment = null;
1626
1627 return this.ParseBlock(new string[] { Cell }, new int[] { Position });
1628 }
1629
1630 private LinkedList<MarkdownElement> ParseBlock(string[] Rows, int[] Positions)
1631 {
1632 int BlockIndex = 0;
1633 return this.ParseBlock(Rows, Positions, 0, Rows.Length - 1, null, ref BlockIndex, 0);
1634 }
1635
1636 private LinkedList<MarkdownElement> ParseBlock(Block Block)
1637 {
1638 int BlockIndex = 0;
1639 return this.ParseBlock(Block.Rows, Block.Positions, Block.Start, Block.End, null, ref BlockIndex, 0);
1640 }
1641
1642 private LinkedList<MarkdownElement> ParseBlock(Block Block, List<Block> Blocks, ref int BlockIndex, int EndBlock)
1643 {
1644 return this.ParseBlock(Block.Rows, Block.Positions, Block.Start, Block.End, Blocks, ref BlockIndex, EndBlock);
1645 }
1646
1647 private LinkedList<MarkdownElement> ParseBlock(string[] Rows, int[] Positions, int StartRow, int EndRow)
1648 {
1649 int BlockIndex = 0;
1650 return this.ParseBlock(Rows, Positions, StartRow, EndRow, null, ref BlockIndex, 0);
1651 }
1652
1653 private LinkedList<MarkdownElement> ParseBlock(string[] Rows, int[] Positions, int StartRow, int EndRow, List<Block> Blocks,
1654 ref int BlockIndex, int EndBlock)
1655 {
1656 LinkedList<MarkdownElement> Elements = new LinkedList<MarkdownElement>();
1657 bool PreserveCrLf = Rows[StartRow].StartsWith("<") && Rows[EndRow].EndsWith(">");
1658 BlockParseState State = new BlockParseState(Rows, Positions, StartRow, EndRow, PreserveCrLf, Blocks, BlockIndex, EndBlock);
1659
1660 this.ParseBlock(State, (char)0, 1, Elements, true);
1661
1662 BlockIndex = State.BlockIndex;
1663 return Elements;
1664 }
1665
1666 private bool ParseBlock(BlockParseState State, char TerminationCharacter, int TerminationCharacterCount,
1667 LinkedList<MarkdownElement> Elements, bool AcceptIncomplete)
1668 {
1669 LinkedList<MarkdownElement> ChildElements;
1670 StringBuilder Text = new StringBuilder();
1671 string Url, Title;
1672 char ch, ch2, ch3;
1673 char PrevChar = ' ';
1674 int? Width;
1675 int? Height;
1676 bool FirstCharOnLine;
1677
1678 while ((ch = State.NextChar()) != (char)0)
1679 {
1680 if (ch == TerminationCharacter)
1681 {
1682 if (TerminationCharacterCount == 1 ||
1683 State.CheckRestOfTermination(TerminationCharacter, TerminationCharacterCount - 1))
1684 {
1685 break;
1686 }
1687 }
1688
1689 switch (ch)
1690 {
1691 case '\n':
1692 this.AppendAnyText(Elements, Text);
1693 Elements.AddLast(new LineBreak(this));
1694 break;
1695
1696 case '\r':
1697 Text.AppendLine();
1698 break;
1699
1700 case '*':
1701 if (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
1702 {
1703 if (State.IsFirstCharOnLine)
1704 {
1705 this.AppendAnyText(Elements, Text);
1706
1707 while (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
1708 State.NextCharSameRow();
1709
1710 UnnumberedItem Item;
1711 List<string> Rows = new List<string>();
1712 List<int> Positions = new List<int>()
1713 {
1714 State.CurrentPosition
1715 };
1716
1717 Rows.Add(State.RestOfRow());
1718
1719 while (!State.EOF)
1720 {
1721 if ((ch2 = State.PeekNextCharSameRow()) == '*' || ch2 == '+' || ch2 == '-')
1722 {
1723 Item = new UnnumberedItem(this, new string(ch, 1), new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
1724
1725 if (Elements.Last?.Value is BulletList BulletList)
1726 BulletList.AddChildren(Item);
1727 else
1728 Elements.AddLast(new BulletList(this, Item));
1729
1730 State.NextCharSameRow();
1731 State.SkipWhitespaceSameRow(3);
1732
1733 Rows.Clear();
1734 Positions.Clear();
1735
1736 Positions.Add(State.CurrentPosition);
1737 Rows.Add(State.RestOfRow());
1738 }
1739 else
1740 {
1741 State.SkipWhitespaceSameRow(4);
1742
1743 Positions.Add(State.CurrentPosition);
1744 Rows.Add(State.RestOfRow());
1745 }
1746 }
1747
1748 if (Rows.Count > 0)
1749 {
1750 Item = new UnnumberedItem(this, new string(ch, 1), new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
1751
1752 if (Elements.Last?.Value is BulletList BulletList)
1753 BulletList.AddChildren(Item);
1754 else
1755 Elements.AddLast(new BulletList(this, Item));
1756 }
1757 }
1758 else
1759 Text.Append('*');
1760
1761 break;
1762 }
1763
1764 this.AppendAnyText(Elements, Text);
1765 ChildElements = new LinkedList<MarkdownElement>();
1766 ch2 = State.PeekNextCharSameRow();
1767 if (ch2 == '*')
1768 {
1769 State.NextCharSameRow();
1770
1771 this.ParseBlock(State, '*', 2, ChildElements, true);
1772 Elements.AddLast(new Strong(this, ChildElements));
1773 }
1774 else
1775 {
1776 if (this.emojiSource is null)
1777 ch2 = (char)0;
1778
1779 switch (ch2)
1780 {
1781 case ')':
1782 State.NextCharSameRow();
1784 break;
1785
1786 case '-':
1787 State.BackupState();
1788 State.NextCharSameRow();
1789
1790 if (State.PeekNextCharSameRow() == ')')
1791 {
1792 State.DiscardBackup();
1793 State.NextCharSameRow();
1795 }
1796 else
1797 {
1798 State.RestoreState();
1799
1800 if (this.ParseBlock(State, '*', 1, ChildElements, TerminationCharacter != '*'))
1801 Elements.AddLast(new Emphasize(this, ChildElements));
1802 else
1803 this.FixSyntaxError(Elements, "*", ChildElements);
1804 }
1805 break;
1806
1807 case '\\':
1808 State.BackupState();
1809 State.NextCharSameRow();
1810 if ((ch3 = State.NextCharSameRow()) == '0' || ch3 == 'O')
1811 {
1812 if (State.NextCharSameRow() == '/')
1813 {
1814 if (State.NextCharSameRow() == '*')
1815 {
1816 State.DiscardBackup();
1817 this.AppendAnyText(Elements, Text);
1819 break;
1820 }
1821 }
1822 }
1823
1824 State.RestoreState();
1825 if (this.ParseBlock(State, '*', 1, ChildElements, TerminationCharacter != '*'))
1826 Elements.AddLast(new Emphasize(this, ChildElements));
1827 else
1828 this.FixSyntaxError(Elements, "*", ChildElements);
1829
1830 break;
1831
1832 default:
1833 if (this.ParseBlock(State, '*', 1, ChildElements, TerminationCharacter != '*'))
1834 Elements.AddLast(new Emphasize(this, ChildElements));
1835 else
1836 this.FixSyntaxError(Elements, "*", ChildElements);
1837 break;
1838 }
1839 }
1840 break;
1841
1842 case '_':
1843 if ((ch2 = State.PeekNextCharSameRow()) <= ' ' || ch2 == 160)
1844 {
1845 Text.Append('_');
1846 break;
1847 }
1848
1849 this.AppendAnyText(Elements, Text);
1850 ChildElements = new LinkedList<MarkdownElement>();
1851 ch2 = State.PeekNextCharSameRow();
1852 if (ch2 == '_')
1853 {
1854 State.NextCharSameRow();
1855
1856 this.ParseBlock(State, '_', 2, ChildElements, true);
1857 Elements.AddLast(new Insert(this, ChildElements));
1858 }
1859 else
1860 {
1861 if (this.ParseBlock(State, '_', 1, ChildElements, TerminationCharacter != '_'))
1862 Elements.AddLast(new Underline(this, ChildElements));
1863 else
1864 this.FixSyntaxError(Elements, "_", ChildElements);
1865 }
1866 break;
1867
1868 case '~':
1869 if ((ch2 = State.PeekNextCharSameRow()) <= ' ' || ch2 == 160)
1870 {
1871 Text.Append('~');
1872 break;
1873 }
1874
1875 this.AppendAnyText(Elements, Text);
1876 ChildElements = new LinkedList<MarkdownElement>();
1877 ch2 = State.PeekNextCharSameRow();
1878 if (ch2 == '~')
1879 {
1880 State.NextCharSameRow();
1881
1882 this.ParseBlock(State, '~', 2, ChildElements, true);
1883 Elements.AddLast(new Delete(this, ChildElements));
1884 }
1885 else
1886 {
1887 if (this.ParseBlock(State, '~', 1, ChildElements, TerminationCharacter != '~'))
1888 Elements.AddLast(new StrikeThrough(this, ChildElements));
1889 else
1890 this.FixSyntaxError(Elements, "~", ChildElements);
1891 }
1892 break;
1893
1894 case '`':
1895 this.AppendAnyText(Elements, Text);
1896 ch2 = State.PeekNextCharSameRow();
1897 if (ch2 == '`')
1898 {
1899 State.NextCharSameRow();
1900
1901 while ((ch2 = State.NextChar()) != 0)
1902 {
1903 if (ch2 == '`' && State.PeekNextCharSameRow() == '`')
1904 {
1905 State.NextCharSameRow();
1906 break;
1907 }
1908
1909 Text.Append(ch2);
1910 }
1911 }
1912 else
1913 {
1914 while ((ch2 = State.NextChar()) != '`' && ch2 != 0)
1915 Text.Append(ch2);
1916 }
1917
1918 Elements.AddLast(new InlineCode(this, Text.ToString()));
1919 Text.Clear();
1920 break;
1921
1922 case '[':
1923 case '!':
1924 FirstCharOnLine = State.IsFirstCharOnLine;
1925
1926 if (ch == '!')
1927 {
1928 ch2 = State.PeekNextCharSameRow();
1929 if (ch2 != '[')
1930 {
1931 Text.Append('!');
1932 break;
1933 }
1934
1935 State.NextCharSameRow();
1936 }
1937 else
1938 {
1939 ch2 = State.PeekNextCharSameRow();
1940 if (ch2 == '[')
1941 {
1942 State.NextCharSameRow();
1943 this.AppendAnyText(Elements, Text);
1944 Elements.AddLast(new HtmlEntity(this, "LeftDoubleBracket"));
1945 break;
1946 }
1947 else if (ch2 == '%')
1948 {
1949 State.NextCharSameRow();
1950 this.AppendAnyText(Elements, Text);
1951
1952 while ((ch3 = State.NextCharSameRow()) != ']' && ch3 != 0)
1953 Text.Append(ch3);
1954
1955 if (ch3 == ']')
1956 {
1957 Url = Text.ToString();
1958 if (string.Compare(Url, "Details", true) == 0)
1959 Elements.AddLast(new DetailsReference(this, Url));
1960 else
1961 Elements.AddLast(new MetaReference(this, Url));
1962 Text.Clear();
1963 }
1964 else
1965 Text.Insert(0, "[%");
1966
1967 break;
1968 }
1969 else if (ch2 == '^')
1970 {
1971 State.NextCharSameRow();
1972 this.AppendAnyText(Elements, Text);
1973
1974 while ((ch3 = State.NextChar()) != ']' && ch3 != 0)
1975 Text.Append(ch3);
1976
1977 if (ch3 == ']')
1978 {
1979 Url = Text.ToString();
1980 Text.Clear();
1981
1982 if (this.footnoteNumberByKey is null)
1983 {
1984 this.footnoteNumberByKey = new Dictionary<string, int>();
1985 this.footnoteOrder = new List<string>();
1986 this.footnotes = new Dictionary<string, Footnote>();
1987 }
1988
1989 try
1990 {
1991 Title = Url.ToLower();
1992 Elements.AddLast(new FootnoteReference(this, XmlConvert.VerifyNCName(Title)));
1993 if (!this.footnoteNumberByKey.ContainsKey(Title))
1994 {
1995 this.footnoteNumberByKey[Title] = ++this.lastFootnote;
1996 this.footnoteOrder.Add(Title);
1997 }
1998 }
1999 catch
2000 {
2001 Title = Guid.NewGuid().ToString();
2002
2003 Elements.AddLast(new FootnoteReference(this, Title));
2004 this.footnoteNumberByKey[Title] = ++this.lastFootnote;
2005 this.footnoteOrder.Add(Title);
2006 this.footnotes[Title] = new Footnote(this, Title, new Paragraph(this, this.ParseBlock(new string[] { Url }, new int[] { State.CurrentPosition - 1 - Url.Length })));
2007 }
2008 }
2009 else
2010 Text.Insert(0, "[^");
2011
2012 break;
2013 }
2014 }
2015
2016 char[] chs;
2017
2018 if (FirstCharOnLine && (((chs = State.PeekNextChars(3))[0] == ' ' || chs[0] == 'x' || chs[0] == 'X') && chs[1] == ']' && ((chs[2] <= ' ' && chs[2] > 0) || chs[2] == 160)))
2019 {
2020 int CheckPosition = State.CurrentPosition;
2021
2022 State.NextChar();
2023 State.NextChar();
2024 State.NextChar();
2025
2026 this.AppendAnyText(Elements, Text);
2027
2028 while (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
2029 State.NextCharSameRow();
2030
2031 TaskItem Item;
2032 List<string> Rows = new List<string>()
2033 {
2034 State.RestOfRow()
2035 };
2036 List<int> Positions = new List<int>()
2037 {
2038 State.CurrentPosition
2039 };
2040 bool Checked = (chs[0] != ' ');
2041
2042 while (!State.EOF)
2043 {
2044 if ((chs = State.PeekNextChars(4))[0] == '[' &&
2045 (chs[1] == ' ' || chs[1] == 'x' || chs[1] == 'X') &&
2046 chs[2] == ']' && ((chs[3] <= ' ' && chs[3] > 0) || chs[3] == 160))
2047 {
2048 Item = new TaskItem(this, Checked, CheckPosition,
2049 new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
2050
2051 if (Elements.Last?.Value is TaskList TaskList)
2052 TaskList.AddChildren(Item);
2053 else
2054 Elements.AddLast(new TaskList(this, Item));
2055
2056 Rows.Clear();
2057 Positions.Clear();
2058
2059 State.NextChar();
2060
2061 CheckPosition = State.CurrentPosition;
2062
2063 State.NextChar();
2064 State.NextChar();
2065 State.NextChar();
2066
2067 while (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
2068 State.NextCharSameRow();
2069
2070 Positions.Add(State.CurrentPosition);
2071 Rows.Add(State.RestOfRow());
2072
2073 Checked = (chs[1] != ' ');
2074 }
2075 else
2076 {
2077 State.SkipWhitespaceSameRow(4);
2078 Positions.Add(State.CurrentPosition);
2079 Rows.Add(State.RestOfRow());
2080 }
2081 }
2082
2083 if (Rows.Count > 0)
2084 {
2085 Item = new TaskItem(this, Checked, CheckPosition,
2086 new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
2087
2088 if (Elements.Last?.Value is TaskList TaskList)
2089 TaskList.AddChildren(Item);
2090 else
2091 Elements.AddLast(new TaskList(this, Item));
2092 }
2093
2094 break;
2095 }
2096
2097 ChildElements = new LinkedList<MarkdownElement>();
2098 this.AppendAnyText(Elements, Text);
2099
2100 if (this.ParseBlock(State, ']', 1, ChildElements, false))
2101 {
2102 ch2 = State.PeekNextNonWhitespaceChar(false);
2103 if (ch2 == '(')
2104 {
2105 State.NextNonWhitespaceChar();
2106 Title = string.Empty;
2107
2108 while ((ch2 = State.PeekNextCharSameRow()) != 0 && ch2 > ' ' && ch2 != ')' && ch2 != 160)
2109 {
2110 State.NextChar();
2111 Text.Append(ch2);
2112 }
2113
2114 Url = Text.ToString();
2115 if (Url.StartsWith("abbr:", StringComparison.CurrentCultureIgnoreCase))
2116 {
2117 if (ch2 == ')')
2118 State.NextChar();
2119 else if (ch2 != 0)
2120 {
2121 while ((ch2 = State.NextCharSameRow()) != 0 && ch2 != ')')
2122 Text.Append(ch2);
2123 }
2124
2125 Url = Text.ToString();
2126 Text.Clear();
2127
2128 Elements.AddLast(new Abbreviation(this, ChildElements, Url.Substring(5).Trim()));
2129 }
2130 else
2131 {
2132 Text.Clear();
2133
2134 if (Url.StartsWith("<") && Url.EndsWith(">"))
2135 Url = Url.Substring(1, Url.Length - 2);
2136
2137 if (ch2 <= ' ' || ch2 == 160)
2138 {
2139 ch2 = State.PeekNextNonWhitespaceChar(true);
2140
2141 if (ch2 == '"' || ch2 == '\'')
2142 {
2143 State.NextNonWhitespaceChar();
2144 while ((ch3 = State.NextCharSameRow()) != 0 && ch3 != ch2)
2145 Text.Append(ch3);
2146
2147 Title = Text.ToString();
2148 Text.Clear();
2149
2150 ch2 = State.PeekNextNonWhitespaceChar(true);
2151 }
2152 else
2153 Title = string.Empty;
2154 }
2155
2156 if (ch == '!' && ch2 != ')')
2157 {
2158 ParseWidthHeight(State, out Width, out Height);
2159 ch2 = State.PeekNextCharSameRow();
2160 }
2161 else
2162 Width = Height = null;
2163
2164 while (ch2 != 0 && ch2 != ')')
2165 {
2166 State.NextCharSameRow();
2167 ch2 = State.PeekNextCharSameRow();
2168 }
2169
2170 if (ch2 == ')')
2171 State.NextChar();
2172
2173 if (ch == '!')
2174 {
2175 List<MultimediaItem> Items = new List<MultimediaItem>()
2176 {
2177 new MultimediaItem(this, Url, Title, Width, Height)
2178 };
2179
2180 if (!this.includesTableOfContents && string.Compare(Url, "ToC", true) == 0)
2181 this.includesTableOfContents = true;
2182
2183 State.BackupState();
2184 ch2 = State.PeekNextNonWhitespaceChar(false);
2185
2186 while (ch2 == '(')
2187 {
2188 State.NextNonWhitespaceChar();
2189 Title = string.Empty;
2190
2191 while ((ch2 = State.PeekNextCharSameRow()) != 0 && ch2 > ' ' && ch2 != ')' && ch2 != 160)
2192 {
2193 State.NextChar();
2194 Text.Append(ch2);
2195 }
2196
2197 Url = Text.ToString();
2198
2199 Text.Clear();
2200
2201 if (Url.StartsWith("<") && Url.EndsWith(">"))
2202 Url = Url.Substring(1, Url.Length - 2);
2203
2204 if (ch2 <= ' ' || ch2 == 160)
2205 {
2206 ch2 = State.PeekNextNonWhitespaceChar(true);
2207
2208 if (ch2 == '"' || ch2 == '\'')
2209 {
2210 State.NextNonWhitespaceChar();
2211 while ((ch3 = State.NextCharSameRow()) != 0 && ch3 != ch2)
2212 Text.Append(ch3);
2213
2214 Title = Text.ToString();
2215 Text.Clear();
2216
2217 ch2 = State.PeekNextNonWhitespaceChar(true);
2218 }
2219 else
2220 Title = string.Empty;
2221 }
2222
2223 if (ch2 != ')')
2224 {
2225 ParseWidthHeight(State, out Width, out Height);
2226
2227 ch2 = State.PeekNextCharSameRow();
2228
2229 while (ch2 != 0 && ch2 != ')')
2230 {
2231 State.NextCharSameRow();
2232 ch2 = State.PeekNextCharSameRow();
2233 }
2234 }
2235
2236 Items.Add(new MultimediaItem(this, Url, Title, Width, Height));
2237
2238 if (ch2 == ')')
2239 {
2240 State.NextChar();
2241 ch2 = State.PeekNextNonWhitespaceChar(true);
2242 }
2243
2244 State.DiscardBackup();
2245 State.BackupState();
2246 }
2247
2248 State.RestoreState();
2249
2250 Elements.AddLast(new Multimedia(this, ChildElements, Elements.First is null && State.PeekNextChar() == 0,
2251 Items.ToArray()));
2252 }
2253 else
2254 Elements.AddLast(new Link(this, ChildElements, Url, Title));
2255 }
2256 }
2257 else if (ch2 == ':' && FirstCharOnLine)
2258 {
2259 State.NextNonWhitespaceChar();
2260 ch2 = State.NextChar();
2261 while ((ch2 != 0 && ch2 <= ' ') || ch2 == 160)
2262 ch2 = State.NextChar();
2263
2264 if (ch2 > ' ' && ch2 != 160)
2265 {
2266 List<MultimediaItem> Items = new List<MultimediaItem>();
2267
2268 Text.Append(ch2);
2269
2270 while (ch2 > ' ' && ch2 != 160 && ch2 != '[')
2271 {
2272 ch2 = State.NextNonWhitespaceChar();
2273 while (ch2 != 0 && ch2 > ' ' && ch2 != 160)
2274 {
2275 Text.Append(ch2);
2276 ch2 = State.NextCharSameRow();
2277 }
2278
2279 Url = Text.ToString();
2280 Text.Clear();
2281
2282 if (Url.StartsWith("<") && Url.EndsWith(">"))
2283 Url = Url.Substring(1, Url.Length - 2);
2284
2285 ch2 = State.PeekNextNonWhitespaceChar(true);
2286
2287 if (ch2 == '"' || ch2 == '\'' || ch2 == '(')
2288 {
2289 State.NextNonWhitespaceChar();
2290 if (ch2 == '(')
2291 ch2 = ')';
2292
2293 while ((ch3 = State.NextCharSameRow()) != 0 && ch3 != ch2)
2294 Text.Append(ch3);
2295
2296 Title = Text.ToString();
2297 Text.Clear();
2298 }
2299 else
2300 Title = string.Empty;
2301
2302 ParseWidthHeight(State, out Width, out Height);
2303
2304 Items.Add(new MultimediaItem(this, Url, Title, Width, Height));
2305 if (!this.includesTableOfContents && string.Compare(Url, "ToC", true) == 0)
2306 this.includesTableOfContents = true;
2307
2308 ch2 = State.PeekNextNonWhitespaceChar(true);
2309 }
2310
2311 using (TextRenderer Renderer = new TextRenderer(Text))
2312 {
2313 foreach (MarkdownElement E in ChildElements)
2314 E.Render(Renderer);
2315 }
2316
2317 this.references[Text.ToString().ToLower()] = new Multimedia(this, null,
2318 Elements.First is null && State.PeekNextChar() == 0, Items.ToArray());
2319
2320 Text.Clear();
2321 }
2322 }
2323 else if (ch2 == '[')
2324 {
2325 State.NextNonWhitespaceChar();
2326 while ((ch2 = State.NextCharSameRow()) != 0 && ch2 != ']')
2327 Text.Append(ch2);
2328
2329 Title = Text.ToString();
2330 Text.Clear();
2331
2332 if (string.IsNullOrEmpty(Title))
2333 {
2334 using (TextRenderer Renderer = new TextRenderer(Text))
2335 {
2336 foreach (MarkdownElement E in ChildElements)
2337 E.Render(Renderer);
2338 }
2339
2340 Title = Text.ToString();
2341 Text.Clear();
2342 }
2343
2344 if (ch == '!')
2345 {
2346 Elements.AddLast(new MultimediaReference(this, ChildElements, Title,
2347 Elements.First is null && State.PeekNextChar() == 0));
2348 }
2349 else
2350 Elements.AddLast(new LinkReference(this, ChildElements, Title));
2351 }
2352 else if (ch != '!')
2353 Elements.AddLast(new SubScript(this, ChildElements));
2354 else
2355 {
2356 this.FixSyntaxError(Elements, "![", ChildElements);
2357
2358 if (ch2 == (char)0)
2359 Elements.AddLast(new InlineText(this, "]"));
2360 else
2361 Elements.AddLast(new InlineText(this, "]" + ch2));
2362 }
2363 }
2364 else
2365 this.FixSyntaxError(Elements, ch == '!' ? "![" : "[", ChildElements);
2366 break;
2367
2368 case ']':
2369 ch2 = State.PeekNextCharSameRow();
2370 if (ch2 == ']')
2371 {
2372 State.NextCharSameRow();
2373 this.AppendAnyText(Elements, Text);
2374 Elements.AddLast(new HtmlEntity(this, "RightDoubleBracket"));
2375 }
2376 else
2377 Text.Append(']');
2378 break;
2379
2380 case '<':
2381 ch2 = State.PeekNextCharSameRow();
2382 if (ch2 == '<')
2383 {
2384 State.NextCharSameRow();
2385 this.AppendAnyText(Elements, Text);
2386
2387 ch3 = State.PeekNextCharSameRow();
2388 if (ch3 == '<')
2389 {
2390 State.NextCharSameRow();
2391 Elements.AddLast(new HtmlEntity(this, "Ll"));
2392 }
2393 else
2394 Elements.AddLast(new HtmlEntity(this, "laquo"));
2395 break;
2396 }
2397 else if (ch2 == '-')
2398 {
2399 State.NextCharSameRow();
2400 ch3 = State.PeekNextCharSameRow();
2401
2402 if (ch3 == '-')
2403 {
2404 State.NextCharSameRow();
2405 this.AppendAnyText(Elements, Text);
2406
2407 ch3 = State.PeekNextCharSameRow();
2408 if (ch3 == '>')
2409 {
2410 State.NextCharSameRow();
2411 Elements.AddLast(new HtmlEntity(this, "harr"));
2412 }
2413 else
2414 Elements.AddLast(new HtmlEntity(this, "larr"));
2415 }
2416 else
2417 Text.Append("<-");
2418 break;
2419 }
2420 else if (ch2 == '=')
2421 {
2422 State.NextCharSameRow();
2423 this.AppendAnyText(Elements, Text);
2424 ch3 = State.PeekNextCharSameRow();
2425
2426 if (ch3 == '=')
2427 {
2428 State.NextCharSameRow();
2429
2430 ch3 = State.PeekNextCharSameRow();
2431 if (ch3 == '>')
2432 {
2433 State.NextCharSameRow();
2434 Elements.AddLast(new HtmlEntity(this, "hArr"));
2435 }
2436 else
2437 Elements.AddLast(new HtmlEntity(this, "lArr"));
2438 }
2439 else
2440 Elements.AddLast(new HtmlEntity(this, "leq"));
2441 break;
2442 }
2443 else if (ch2 == '>')
2444 {
2445 State.NextCharSameRow();
2446 this.AppendAnyText(Elements, Text);
2447 Elements.AddLast(new HtmlEntity(this, "ne"));
2448 break;
2449 }
2450 else if (ch2 == '3' && !(this.emojiSource is null))
2451 {
2452 State.NextCharSameRow();
2453 this.AppendAnyText(Elements, Text);
2455 break;
2456 }
2457 else if (ch2 == '/')
2458 {
2459 State.NextCharSameRow();
2460 if (!(this.emojiSource is null) && State.PeekNextCharSameRow() == '3')
2461 {
2462 State.NextCharSameRow();
2463 this.AppendAnyText(Elements, Text);
2465 break;
2466 }
2467 }
2468
2469 if ((!char.IsLetter(ch2) && ch2 != '/') || !this.settings.AllowHtml)
2470 {
2471 Text.Append(ch);
2472 break;
2473 }
2474
2475 this.AppendAnyText(Elements, Text);
2476 Text.Append(ch);
2477
2478 if (ch2 == '/')
2479 Text.Append(ch2);
2480
2481 while ((ch2 = State.NextChar()) != 0 && ch2 != '>')
2482 {
2483 if (ch2 == '\r')
2484 Text.AppendLine();
2485 else
2486 Text.Append(ch2);
2487 }
2488
2489 if (ch2 == 0)
2490 break;
2491
2492 Text.Append(ch2);
2493 Url = Text.ToString();
2494
2495 if (Url.StartsWith("</"))
2496 {
2497 if (Url.StartsWith("</script", StringComparison.CurrentCultureIgnoreCase))
2498 Elements.AddLast(new InlineCode(this, Url));
2499 else
2500 Elements.AddLast(new InlineHTML(this, Url));
2501 }
2502 else if (Url.StartsWith("<script", StringComparison.CurrentCultureIgnoreCase))
2503 {
2504 if (this.AllowScriptTag && this.settings.AllowScriptTag)
2505 {
2506 Text.Append(State.UntilToken("</SCRIPT>"));
2507 Text.Append("</");
2508 Text.Append(Url.Substring(1, 6));
2509 Text.Append('>');
2510
2511 Elements.AddLast(new InlineHTML(this, Text.ToString()));
2512 }
2513 else
2514 Elements.AddLast(new InlineCode(this, Url));
2515 }
2516 else if (Url.StartsWith("<textarea", StringComparison.CurrentCultureIgnoreCase))
2517 {
2518 Elements.AddLast(new InlineHTML(this, Url));
2519
2520 string s = State.UntilToken("</TEXTAREA>");
2521
2522 if (!string.IsNullOrEmpty(s))
2523 Elements.AddLast(new InlineText(this, s));
2524
2525 Elements.AddLast(new InlineHTML(this, "</" + Url.Substring(1, 8) + ">"));
2526 }
2527 else
2528 {
2529 int i = Url.IndexOf(' ');
2530
2531 if ((i < 0 && Url.IndexOf(':') >= 0) || (i > 0 && Url.LastIndexOf(':', i) >= 0))
2532 Elements.AddLast(new AutomaticLinkUrl(this, Url.Substring(1, Url.Length - 2)));
2533 else if ((i < 0 && Url.IndexOf('@') >= 0) || (i > 0 && Url.LastIndexOf('@', i) >= 0))
2534 Elements.AddLast(new AutomaticLinkMail(this, Url.Substring(1, Url.Length - 2)));
2535 else
2536 {
2537 Elements.AddLast(new InlineHTML(this, Url));
2538
2539 if (Url.StartsWith("<textarea", StringComparison.CurrentCultureIgnoreCase))
2540 {
2541 string s = State.UntilToken("</TEXTAREA>");
2542
2543 if (!string.IsNullOrEmpty(s))
2544 Elements.AddLast(new InlineText(this, s));
2545
2546 Elements.AddLast(new InlineHTML(this, "</" + Url.Substring(1, 8) + ">"));
2547 }
2548 }
2549 }
2550
2551 Text.Clear();
2552 break;
2553
2554 case '>':
2555 switch (State.PeekNextCharSameRow())
2556 {
2557 case '>':
2558 State.NextCharSameRow();
2559 this.AppendAnyText(Elements, Text);
2560
2561 ch3 = State.PeekNextCharSameRow();
2562 if (ch3 == '>')
2563 {
2564 State.NextCharSameRow();
2565 Elements.AddLast(new HtmlEntity(this, "Gg"));
2566 }
2567 else
2568 Elements.AddLast(new HtmlEntity(this, "raquo"));
2569 break;
2570
2571 case '=':
2572 this.AppendAnyText(Elements, Text);
2573 State.NextCharSameRow();
2574
2575 if (!(this.emojiSource is null) && State.PeekNextCharSameRow() == ')')
2576 {
2577 State.NextCharSameRow();
2579 }
2580 else
2581 Elements.AddLast(new HtmlEntity(this, "geq"));
2582 break;
2583
2584 case ':':
2585 if (!(this.emojiSource is null))
2586 {
2587 State.NextCharSameRow();
2588 switch (State.PeekNextCharSameRow())
2589 {
2590 case ')':
2591 State.NextCharSameRow();
2592 this.AppendAnyText(Elements, Text);
2594 break;
2595
2596 case '(':
2597 State.NextCharSameRow();
2598 this.AppendAnyText(Elements, Text);
2600 break;
2601
2602 case '[':
2603 State.NextCharSameRow();
2604 this.AppendAnyText(Elements, Text);
2606 break;
2607
2608 case 'O':
2609 State.NextCharSameRow();
2610 this.AppendAnyText(Elements, Text);
2612 break;
2613
2614 case 'P':
2615 case 'p':
2616 case 'b':
2617 case 'Þ':
2618 case 'þ':
2619 State.NextCharSameRow();
2620 this.AppendAnyText(Elements, Text);
2622 break;
2623
2624 case '/':
2625 case '\\':
2626 case 'L':
2627 State.NextCharSameRow();
2628 this.AppendAnyText(Elements, Text);
2630 break;
2631
2632 case 'X':
2633 case 'x':
2634 case '#':
2635 State.NextCharSameRow();
2636 this.AppendAnyText(Elements, Text);
2638 break;
2639
2640 case '-':
2641 State.NextCharSameRow();
2642 switch (State.PeekNextCharSameRow())
2643 {
2644 case ')':
2645 State.NextCharSameRow();
2646 this.AppendAnyText(Elements, Text);
2648 break;
2649
2650 case '(':
2651 State.NextCharSameRow();
2652 this.AppendAnyText(Elements, Text);
2654 break;
2655
2656 default:
2657 Text.Append(">:-");
2658 break;
2659 }
2660 break;
2661
2662 default:
2663 Text.Append(">:");
2664 break;
2665 }
2666 }
2667 else
2668 Text.Append('>');
2669 break;
2670
2671 case ';':
2672 if (!(this.emojiSource is null))
2673 {
2674 State.NextCharSameRow();
2675 if (State.PeekNextCharSameRow() == ')')
2676 {
2677 State.NextCharSameRow();
2678 this.AppendAnyText(Elements, Text);
2680 }
2681 else
2682 Text.Append(">;");
2683 }
2684 else
2685 Text.Append('>');
2686 break;
2687
2688 case '.':
2689 if (!(this.emojiSource is null))
2690 {
2691 State.NextCharSameRow();
2692 if (State.PeekNextCharSameRow() == '<')
2693 {
2694 State.NextCharSameRow();
2695 this.AppendAnyText(Elements, Text);
2697 }
2698 else
2699 Text.Append(">.");
2700 }
2701 else
2702 Text.Append('>');
2703 break;
2704
2705 default:
2706 Text.Append('>');
2707 break;
2708 }
2709 break;
2710
2711 case '{':
2712 if (!this.settings.AllowInlineScript || this.settings.Variables is null)
2713 {
2714 int Pos = State.CurrentPosition - 1;
2715 if (Pos < this.markdownText.Length && this.markdownText[Pos] == '{')
2716 {
2717 if (this.toInsert is null)
2718 this.toInsert = new SortedDictionary<int, string>(new ReversePosition());
2719
2720 this.toInsert[Pos] = "\\";
2721 if (Pos > 0 && ((ch2 = this.markdownText[Pos - 1]) == ':' || ch2 == '*' || ch2 == '='))
2722 this.toInsert[Pos - 1] = "\\"; // To avoid creating a smiley.
2723 }
2724 Text.Append(ch);
2725 break;
2726 }
2727
2728 this.AppendAnyText(Elements, Text);
2729 State.BackupState();
2730
2731 int StartPosition = State.CurrentPosition - 1;
2732
2733 while ((ch2 = State.NextChar()) != '}' && ch2 != 0)
2734 Text.Append(ch2);
2735
2736 int EndPosition = State.CurrentPosition;
2737
2738 if (ch2 == 0)
2739 {
2740 State.RestoreState();
2741 Text.Clear();
2742 Text.Append(ch);
2743 break;
2744 }
2745
2746 try
2747 {
2748 State.DiscardBackup();
2749 Expression Exp = new Expression(Text.ToString(), this.fileName);
2750 Elements.AddLast(new InlineScript(this, Exp, this.settings.Variables,
2751 Elements.First is null && State.PeekNextChar() == 0, StartPosition, EndPosition));
2752 Text.Clear();
2753 this.isDynamic = true;
2754 }
2755 catch (Exception ex)
2756 {
2757 ex = Log.UnnestException(ex);
2758
2759 Elements.AddLast(new HtmlBlock(this, new MarkdownElement[]
2760 {
2761 new InlineHTML(this, "<font class=\"error\">")
2762 }));
2763
2764 if (ex is AggregateException ex2)
2765 {
2766 foreach (Exception ex3 in ex2.InnerExceptions)
2767 {
2768 this.CheckException(ex3);
2769
2770 Log.Exception(ex3, this.fileName);
2771
2772 Elements.AddLast(new Paragraph(this, new MarkdownElement[]
2773 {
2774 new InlineText(this, ex3.Message)
2775 }));
2776 }
2777 }
2778 else
2779 {
2780 this.CheckException(ex);
2781
2782 Log.Exception(ex, this.fileName);
2783
2784 Elements.AddLast(new Paragraph(this, new MarkdownElement[]
2785 {
2786 new InlineText(this, ex.Message)
2787 }));
2788 }
2789
2790 Elements.AddLast(new HtmlBlock(this, new MarkdownElement[]
2791 {
2792 new InlineHTML(this, "</font>")
2793 }));
2794
2795 this.CheckException(ex);
2796 }
2797
2798 break;
2799
2800 case '-':
2801 ch2 = State.PeekNextCharSameRow();
2802 if (ch2 == '-')
2803 {
2804 State.NextCharSameRow();
2805 this.AppendAnyText(Elements, Text);
2806
2807 ch3 = State.PeekNextCharSameRow();
2808
2809 if (ch3 == '>')
2810 {
2811 State.NextCharSameRow();
2812 Elements.AddLast(new HtmlEntity(this, "rarr"));
2813 }
2814 else if (ch3 == '-')
2815 {
2816 State.NextCharSameRow();
2817 Elements.AddLast(new HtmlEntity(this, "mdash"));
2818 }
2819 else
2820 Elements.AddLast(new HtmlEntity(this, "ndash"));
2821 }
2822 else if (ch2 == '+')
2823 {
2824 State.NextCharSameRow();
2825 this.AppendAnyText(Elements, Text);
2826 Elements.AddLast(new HtmlEntity(this, "MinusPlus"));
2827 }
2828 else if (ch2 == '_')
2829 {
2830 if (!(this.emojiSource is null))
2831 {
2832 State.BackupState();
2833 while ((ch2 = State.NextCharSameRow()) == '_')
2834 ;
2835
2836 if (ch2 == '-')
2837 {
2838 State.DiscardBackup();
2839 State.NextCharSameRow();
2840 this.AppendAnyText(Elements, Text);
2842 }
2843 else
2844 {
2845 State.RestoreState();
2846 Text.Append(ch);
2847 }
2848 }
2849 else
2850 Text.Append(ch);
2851 }
2852 else if ((ch2 <= ' ' && ch2 > 0) || ch2 == 160)
2853 {
2854 if (State.IsFirstCharOnLine)
2855 {
2856 this.AppendAnyText(Elements, Text);
2857
2858 while (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
2859 State.NextCharSameRow();
2860
2861 UnnumberedItem Item;
2862 List<string> Rows = new List<string>();
2863 List<int> Positions = new List<int>()
2864 {
2865 State.CurrentPosition
2866 };
2867
2868 Rows.Add(State.RestOfRow());
2869
2870 while (!State.EOF)
2871 {
2872 if ((ch2 = State.PeekNextCharSameRow()) == '*' || ch2 == '+' || ch2 == '-')
2873 {
2874 Item = new UnnumberedItem(this, new string(ch, 1), new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
2875
2876 if (Elements.Last?.Value is BulletList BulletList)
2877 BulletList.AddChildren(Item);
2878 else
2879 Elements.AddLast(new BulletList(this, Item));
2880
2881 State.NextCharSameRow();
2882 State.SkipWhitespaceSameRow(3);
2883
2884 Rows.Clear();
2885 Positions.Clear();
2886
2887 Positions.Add(State.CurrentPosition);
2888 Rows.Add(State.RestOfRow());
2889 }
2890 else
2891 {
2892 State.SkipWhitespaceSameRow(4);
2893
2894 Positions.Add(State.CurrentPosition);
2895 Rows.Add(State.RestOfRow());
2896 }
2897 }
2898
2899 if (Rows.Count > 0)
2900 {
2901 Item = new UnnumberedItem(this, new string(ch, 1), new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
2902
2903 if (Elements.Last?.Value is BulletList BulletList)
2904 BulletList.AddChildren(Item);
2905 else
2906 Elements.AddLast(new BulletList(this, Item));
2907 }
2908 }
2909 else
2910 Text.Append('-');
2911 }
2912 else
2913 Text.Append('-');
2914 break;
2915
2916 case '+':
2917 ch2 = State.PeekNextCharSameRow();
2918 if (ch2 == '-')
2919 {
2920 State.NextCharSameRow();
2921 this.AppendAnyText(Elements, Text);
2922 Elements.AddLast(new HtmlEntity(this, "PlusMinus"));
2923 }
2924 else if ((ch2 <= ' ' && ch2 > 0) || ch2 == 160)
2925 {
2926 if (State.IsFirstCharOnLine)
2927 {
2928 this.AppendAnyText(Elements, Text);
2929
2930 while (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
2931 State.NextCharSameRow();
2932
2933 UnnumberedItem Item;
2934 List<string> Rows = new List<string>();
2935 List<int> Positions = new List<int>()
2936 {
2937 State.CurrentPosition
2938 };
2939
2940 Rows.Add(State.RestOfRow());
2941
2942 while (!State.EOF)
2943 {
2944 if ((ch2 = State.PeekNextCharSameRow()) == '*' || ch2 == '+' || ch2 == '-')
2945 {
2946 Item = new UnnumberedItem(this, new string(ch, 1), new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
2947
2948 if (Elements.Last?.Value is BulletList BulletList)
2949 BulletList.AddChildren(Item);
2950 else
2951 Elements.AddLast(new BulletList(this, Item));
2952
2953 State.NextCharSameRow();
2954 State.SkipWhitespaceSameRow(3);
2955
2956 Rows.Clear();
2957 Positions.Clear();
2958
2959 Positions.Add(State.CurrentPosition);
2960 Rows.Add(State.RestOfRow());
2961 }
2962 else
2963 {
2964 State.SkipWhitespaceSameRow(4);
2965
2966 Positions.Add(State.CurrentPosition);
2967 Rows.Add(State.RestOfRow());
2968 }
2969 }
2970
2971 if (Rows.Count > 0)
2972 {
2973 Item = new UnnumberedItem(this, new string(ch, 1), new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
2974
2975 if (Elements.Last?.Value is BulletList BulletList)
2976 BulletList.AddChildren(Item);
2977 else
2978 Elements.AddLast(new BulletList(this, Item));
2979 }
2980 }
2981 else
2982 Text.Append('+');
2983 }
2984 else
2985 Text.Append('+');
2986 break;
2987
2988 case '#':
2989 ch2 = State.PeekNextCharSameRow();
2990 if (char.IsLetterOrDigit(ch2))
2991 {
2992 this.AppendAnyText(Elements, Text);
2993
2994 Text.Append(ch2);
2995 State.NextCharSameRow();
2996
2997 while (char.IsLetterOrDigit(ch2 = State.PeekNextCharSameRow()))
2998 {
2999 Text.Append(ch2);
3000 State.NextCharSameRow();
3001 }
3002
3003 Elements.AddLast(new HashTag(this, Text.ToString()));
3004 Text.Clear();
3005 }
3006 else if (State.IsFirstCharOnLine && ch2 == '.')
3007 {
3008 State.NextCharSameRow();
3009
3010 if (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
3011 {
3012 this.AppendAnyText(Elements, Text);
3013
3014 while (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
3015 State.NextCharSameRow();
3016
3017 NumberedItem Item;
3018 List<string> Rows = new List<string>();
3019 List<int> Positions = new List<int>()
3020 {
3021 State.CurrentPosition
3022 };
3023
3024 Rows.Add(State.RestOfRow());
3025
3026 while (!State.EOF)
3027 {
3028 if (State.PeekNextCharSameRow() == '#')
3029 {
3030 State.NextCharSameRow();
3031 if (State.PeekNextCharSameRow() == '.')
3032 {
3033 Item = new NumberedItem(this, 1, false, new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
3034
3035 if (Elements.Last?.Value is NumberedList NumberedList)
3037 else
3038 Elements.AddLast(new NumberedList(this, Item));
3039
3040 State.NextCharSameRow();
3041 State.SkipWhitespaceSameRow(3);
3042
3043 Rows.Clear();
3044 Positions.Clear();
3045
3046 Positions.Add(State.CurrentPosition);
3047 Rows.Add(State.RestOfRow());
3048 }
3049 else
3050 {
3051 State.SkipWhitespaceSameRow(4);
3052
3053 Positions.Add(State.CurrentPosition - 1);
3054 Rows.Add("#" + State.RestOfRow());
3055 }
3056 }
3057 else
3058 {
3059 State.SkipWhitespaceSameRow(4);
3060
3061 Positions.Add(State.CurrentPosition);
3062 Rows.Add(State.RestOfRow());
3063 }
3064 }
3065
3066 if (Rows.Count > 0)
3067 {
3068 Item = new NumberedItem(this, 1, false, new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
3069
3070 if (Elements.Last?.Value is NumberedList NumberedList)
3072 else
3073 {
3074 if (!Item.NumberExplicit && Elements.Last?.Value is NumberedItem PrevItem)
3075 Item.Number = PrevItem.Number + 1;
3076
3077 Elements.AddLast(new NumberedList(this, Item));
3078 }
3079 }
3080 }
3081 else
3082 Text.Append("#.");
3083 }
3084 else if (!(this.emojiSource is null))
3085 {
3086 switch (State.PeekNextCharSameRow())
3087 {
3088 case '-':
3089 State.BackupState();
3090 State.NextCharSameRow();
3091 switch (State.PeekNextCharSameRow())
3092 {
3093 case ')':
3094 State.DiscardBackup();
3095 State.NextCharSameRow();
3096 this.AppendAnyText(Elements, Text);
3098 break;
3099
3100 default:
3101 State.RestoreState();
3102 Text.Append(ch);
3103 break;
3104 }
3105 break;
3106
3107 case ')':
3108 State.NextCharSameRow();
3109 this.AppendAnyText(Elements, Text);
3111 break;
3112
3113 default:
3114 Text.Append('#');
3115 break;
3116 }
3117 }
3118 else
3119 Text.Append('#');
3120 break;
3121
3122 case '0':
3123 case '1':
3124 case '2':
3125 case '3':
3126 case '4':
3127 case '5':
3128 case '6':
3129 case '7':
3130 case '8':
3131 case '9':
3132 if (!(this.emojiSource is null) && (ch == '8' || ch == '0') && (char.IsPunctuation(PrevChar) || char.IsWhiteSpace(PrevChar)))
3133 {
3134 if (ch == '0')
3135 {
3136 switch (ch2 = State.PeekNextCharSameRow())
3137 {
3138 case ':':
3139 State.BackupState();
3140 State.NextCharSameRow();
3141 switch (State.PeekNextCharSameRow())
3142 {
3143 case '3':
3144 case ')':
3145 State.DiscardBackup();
3146 State.NextCharSameRow();
3147 this.AppendAnyText(Elements, Text);
3149 ch2 = (char)0xffff;
3150 break;
3151
3152 case '-':
3153 State.NextCharSameRow();
3154 switch (State.PeekNextCharSameRow())
3155 {
3156 case '3':
3157 case ')':
3158 State.DiscardBackup();
3159 State.NextCharSameRow();
3160 this.AppendAnyText(Elements, Text);
3162 ch2 = (char)0xffff;
3163 break;
3164
3165 default:
3166 State.RestoreState();
3167 break;
3168 }
3169 break;
3170
3171 default:
3172 State.RestoreState();
3173 break;
3174 }
3175 break;
3176
3177 case ';':
3178 State.BackupState();
3179 State.NextCharSameRow();
3180 switch (State.PeekNextCharSameRow())
3181 {
3182 case '-':
3183 case '^':
3184 State.NextCharSameRow();
3185 switch (State.PeekNextCharSameRow())
3186 {
3187 case ')':
3188 State.DiscardBackup();
3189 State.NextCharSameRow();
3190 this.AppendAnyText(Elements, Text);
3192 ch2 = (char)0xffff;
3193 break;
3194
3195 default:
3196 State.RestoreState();
3197 break;
3198 }
3199 break;
3200
3201 default:
3202 State.RestoreState();
3203 break;
3204 }
3205 break;
3206
3207 default:
3208 break;
3209 }
3210 }
3211 else
3212 {
3213 switch (ch2 = State.PeekNextCharSameRow())
3214 {
3215 case '-':
3216 State.BackupState();
3217 State.NextCharSameRow();
3218 switch (State.PeekNextCharSameRow())
3219 {
3220 case ')':
3221 case 'D':
3222 State.DiscardBackup();
3223 State.NextCharSameRow();
3224 this.AppendAnyText(Elements, Text);
3226 ch2 = (char)0xffff;
3227 break;
3228
3229 default:
3230 State.RestoreState();
3231 break;
3232 }
3233 break;
3234
3235 case ')':
3236 State.NextCharSameRow();
3237 this.AppendAnyText(Elements, Text);
3239 ch2 = (char)0xffff;
3240 break;
3241
3242 default:
3243 break;
3244 }
3245 }
3246
3247 if (ch2 == (char)0xffff)
3248 break;
3249 }
3250
3251 if (State.IsFirstCharOnLine)
3252 {
3253 StringBuilder sb = new StringBuilder();
3254 sb.Append(ch);
3255
3256 while ((ch2 = State.PeekNextCharSameRow()) >= '0' && ch2 <= '9')
3257 {
3258 State.NextCharSameRow();
3259 sb.Append(ch2);
3260 }
3261
3262 if (ch2 == '.')
3263 {
3264 State.NextCharSameRow();
3265 if ((((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160) &&
3266 int.TryParse(sb.ToString(), out int Index))
3267 {
3268 this.AppendAnyText(Elements, Text);
3269
3270 while (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
3271 State.NextCharSameRow();
3272
3273 NumberedItem Item;
3274 List<string> Rows = new List<string>();
3275 List<int> Positions = new List<int>()
3276 {
3277 State.CurrentPosition
3278 };
3279
3280 Rows.Add(State.RestOfRow());
3281
3282 while (!State.EOF)
3283 {
3284 if ((ch2 = State.PeekNextCharSameRow()) >= '0' && ch2 <= '9')
3285 {
3286 sb.Clear();
3287 while ((ch2 = State.NextCharSameRow()) >= '0' && ch2 <= '9')
3288 sb.Append(ch2);
3289
3290 if (ch2 == '.' && int.TryParse(sb.ToString(), out int Index2))
3291 {
3292 Item = new NumberedItem(this, Index, true, new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
3293
3294 if (Elements.Last?.Value is NumberedList NumberedList)
3296 else
3297 Elements.AddLast(new NumberedList(this, Item));
3298
3299 State.NextCharSameRow();
3300 State.SkipWhitespaceSameRow(3);
3301
3302 Rows.Clear();
3303 Positions.Clear();
3304
3305 Positions.Add(State.CurrentPosition);
3306 Rows.Add(State.RestOfRow());
3307 Index = Index2;
3308 }
3309 else
3310 {
3311 State.SkipWhitespaceSameRow(4);
3312
3313 string s = sb.ToString();
3314 Positions.Add(State.CurrentPosition - 1 - s.Length);
3315 Rows.Add(s + ch2 + State.RestOfRow());
3316 }
3317 }
3318 else
3319 {
3320 State.SkipWhitespaceSameRow(4);
3321
3322 Positions.Add(State.CurrentPosition);
3323 Rows.Add(State.RestOfRow());
3324 }
3325 }
3326
3327 if (Rows.Count > 0)
3328 {
3329 Item = new NumberedItem(this, Index, true, new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray())));
3330
3331 if (Elements.Last?.Value is NumberedList NumberedList)
3333 else
3334 Elements.AddLast(new NumberedList(this, Item));
3335 }
3336 }
3337 else
3338 {
3339 Text.Append(sb.ToString());
3340 Text.Append('.');
3341 }
3342 }
3343 else
3344 Text.Append(sb.ToString());
3345 }
3346 else
3347 Text.Append(ch);
3348 break;
3349
3350 case '=':
3351 ch2 = State.PeekNextCharSameRow();
3352 if (this.emojiSource is null && ch2 != '=')
3353 ch2 = (char)0;
3354
3355 switch (ch2)
3356 {
3357 case '=':
3358 State.NextCharSameRow();
3359 this.AppendAnyText(Elements, Text);
3360
3361 ch3 = State.PeekNextCharSameRow();
3362 if (ch3 == '>')
3363 {
3364 State.NextCharSameRow();
3365 Elements.AddLast(new HtmlEntity(this, "rArr"));
3366 }
3367 else
3368 Elements.AddLast(new HtmlEntity(this, "equiv"));
3369 break;
3370
3371 case 'D':
3372 State.NextCharSameRow();
3373 this.AppendAnyText(Elements, Text);
3375 break;
3376
3377 case ')':
3378 case ']':
3379 State.NextCharSameRow();
3380 this.AppendAnyText(Elements, Text);
3382 break;
3383
3384 case '*':
3385 State.NextCharSameRow();
3386 this.AppendAnyText(Elements, Text);
3388 break;
3389
3390 case '(':
3391 case '[':
3392 State.NextCharSameRow();
3393 this.AppendAnyText(Elements, Text);
3395 break;
3396
3397 case '$':
3398 State.NextCharSameRow();
3399 this.AppendAnyText(Elements, Text);
3401 break;
3402
3403 case '/':
3404 case '\\':
3405 case 'L':
3406 State.NextCharSameRow();
3407 this.AppendAnyText(Elements, Text);
3409 break;
3410
3411 case 'P':
3412 case 'p':
3413 case 'b':
3414 case 'Þ':
3415 case 'þ':
3416 State.NextCharSameRow();
3417 this.AppendAnyText(Elements, Text);
3419 break;
3420
3421 case 'X':
3422 case 'x':
3423 case '#':
3424 State.NextCharSameRow();
3425 this.AppendAnyText(Elements, Text);
3427 break;
3428
3429 default:
3430 Text.Append('=');
3431 break;
3432 }
3433 break;
3434
3435 case '&':
3436 if (char.IsLetter(ch2 = State.PeekNextCharSameRow()))
3437 {
3438 this.AppendAnyText(Elements, Text);
3439
3440 Text.Append('&');
3441 while (char.IsLetter(ch2 = State.NextCharSameRow()))
3442 Text.Append(ch2);
3443
3444 if (ch2 != 0)
3445 Text.Append(ch2);
3446
3447 if (ch2 != ';')
3448 break;
3449
3450 Url = Text.ToString();
3451 Text.Clear();
3452
3453 Elements.AddLast(new HtmlEntity(this, Url.Substring(1, Url.Length - 2)));
3454 }
3455 else if (ch2 == '#')
3456 {
3457 int Code;
3458
3459 this.AppendAnyText(Elements, Text);
3460 State.NextCharSameRow();
3461
3462 Text.Append("&#");
3463
3464 if ((ch3 = State.PeekNextCharSameRow()) == 'x' || ch3 == 'X')
3465 {
3466 Text.Append(ch3);
3467 State.NextCharSameRow();
3468
3469 while (((ch3 = char.ToUpper(State.PeekNextCharSameRow())) >= '0' && ch3 <= '9') || (ch3 >= 'A' && ch3 <= 'F'))
3470 {
3471 State.NextCharSameRow();
3472 Text.Append(ch3);
3473 }
3474
3475 if (ch3 == ';' && int.TryParse(Text.ToString().Substring(3), System.Globalization.NumberStyles.HexNumber, null, out Code))
3476 {
3477 State.NextCharSameRow();
3478 Text.Clear();
3479
3480 Elements.AddLast(new HtmlEntityUnicode(this, Code));
3481 }
3482 }
3483 else if (char.IsDigit(ch3))
3484 {
3485 while (char.IsDigit(ch3 = State.PeekNextCharSameRow()))
3486 {
3487 State.NextCharSameRow();
3488 Text.Append(ch3);
3489 }
3490
3491 if (ch3 == ';' && int.TryParse(Text.ToString().Substring(2), out Code))
3492 {
3493 State.NextCharSameRow();
3494 Text.Clear();
3495
3496 Elements.AddLast(new HtmlEntityUnicode(this, Code));
3497 }
3498 }
3499 }
3500 else
3501 Text.Append(ch);
3502
3503 break;
3504
3505 case '"':
3506 this.AppendAnyText(Elements, Text);
3507 if (IsLeftQuote(PrevChar, State.PeekNextCharSameRow()))
3508 Elements.AddLast(new HtmlEntity(this, "ldquo"));
3509 else
3510 Elements.AddLast(new HtmlEntity(this, "rdquo"));
3511 break;
3512
3513 case '\'':
3514 this.AppendAnyText(Elements, Text);
3515
3516 if (!(this.emojiSource is null))
3517 ch2 = State.PeekNextCharSameRow();
3518 else
3519 ch2 = (char)0;
3520
3521 switch (ch2)
3522 {
3523 case ':':
3524 State.NextCharSameRow();
3525 switch (State.PeekNextCharSameRow())
3526 {
3527 case ')':
3528 case 'D':
3529 State.NextCharSameRow();
3531 break;
3532
3533 case '(':
3534 State.NextCharSameRow();
3536 break;
3537
3538 case '-':
3539 State.NextCharSameRow();
3540 switch (State.PeekNextCharSameRow())
3541 {
3542 case ')':
3543 case 'D':
3544 State.NextCharSameRow();
3546 break;
3547
3548 case '(':
3549 State.NextCharSameRow();
3551 break;
3552
3553 default:
3554 if (IsLeftQuote(PrevChar, State.PeekNextCharSameRow()))
3555 Elements.AddLast(new HtmlEntity(this, "lsquo"));
3556 else
3557 Elements.AddLast(new HtmlEntity(this, "rsquo"));
3558
3559 Text.Append(":-");
3560 break;
3561 }
3562 break;
3563
3564 default:
3565 if (IsLeftQuote(PrevChar, State.PeekNextCharSameRow()))
3566 Elements.AddLast(new HtmlEntity(this, "lsquo"));
3567 else
3568 Elements.AddLast(new HtmlEntity(this, "rsquo"));
3569
3570 Text.Append(':');
3571 break;
3572 }
3573 break;
3574
3575 case '=':
3576 State.NextCharSameRow();
3577 switch (State.PeekNextCharSameRow())
3578 {
3579 case ')':
3580 case 'D':
3581 State.NextCharSameRow();
3583 break;
3584
3585 case '(':
3586 State.NextCharSameRow();
3588 break;
3589
3590 default:
3591 if (IsLeftQuote(PrevChar, State.PeekNextCharSameRow()))
3592 Elements.AddLast(new HtmlEntity(this, "lsquo"));
3593 else
3594 Elements.AddLast(new HtmlEntity(this, "rsquo"));
3595
3596 Text.Append('=');
3597 break;
3598 }
3599 break;
3600
3601 default:
3602 if (IsLeftQuote(PrevChar, State.PeekNextCharSameRow()))
3603 Elements.AddLast(new HtmlEntity(this, "lsquo"));
3604 else
3605 Elements.AddLast(new HtmlEntity(this, "rsquo"));
3606 break;
3607 }
3608 break;
3609
3610 case '.':
3611 if (State.PeekNextCharSameRow() == '.')
3612 {
3613 State.NextCharSameRow();
3614 if (State.PeekNextCharSameRow() == '.')
3615 {
3616 State.NextCharSameRow();
3617 this.AppendAnyText(Elements, Text);
3618
3619 Elements.AddLast(new HtmlEntity(this, "hellip"));
3620 }
3621 else
3622 Text.Append("..");
3623 }
3624 else
3625 Text.Append('.');
3626 break;
3627
3628 case '(':
3629 ch2 = State.PeekNextCharSameRow();
3630 ch3 = char.ToLower(ch2);
3631 if (ch3 == 'c' || ch3 == 'r' || ch3 == 'p' || ch3 == 's')
3632 {
3633 State.NextCharSameRow();
3634 if (State.PeekNextCharSameRow() == ')')
3635 {
3636 State.NextCharSameRow();
3637
3638 this.AppendAnyText(Elements, Text);
3639 switch (ch2)
3640 {
3641 case 'c':
3642 Url = "copy";
3643 break;
3644
3645 case 'C':
3646 Url = "COPY";
3647 break;
3648
3649 case 'r':
3650 Url = "reg";
3651 break;
3652
3653 case 'R':
3654 Url = "REG";
3655 break;
3656
3657 case 'p':
3658 Url = "copysr";
3659 break;
3660
3661 case 'P':
3662 Url = "copysr";
3663 break;
3664
3665 case 's':
3666 Url = "oS";
3667 break;
3668
3669 case 'S':
3670 Url = "circledS";
3671 break;
3672
3673 default:
3674 Url = null;
3675 break;
3676 }
3677
3678 Elements.AddLast(new HtmlEntity(this, Url));
3679 }
3680 else
3681 {
3682 Text.Append('(');
3683 Text.Append(ch2);
3684 }
3685 }
3686 else
3687 Text.Append('(');
3688 break;
3689
3690 case '%':
3691 switch (State.PeekNextCharSameRow())
3692 {
3693 case '0':
3694 State.NextCharSameRow();
3695 this.AppendAnyText(Elements, Text);
3696
3697 ch3 = State.PeekNextCharSameRow();
3698 if (ch3 == '0')
3699 {
3700 State.NextCharSameRow();
3701 Elements.AddLast(new HtmlEntity(this, "pertenk"));
3702 }
3703 else
3704 Elements.AddLast(new HtmlEntity(this, "permil"));
3705 break;
3706
3707 case '-':
3708 if (!(this.emojiSource is null))
3709 {
3710 State.BackupState();
3711 State.NextCharSameRow();
3712 switch (State.PeekNextCharSameRow())
3713 {
3714 case ')':
3715 State.DiscardBackup();
3716 State.NextCharSameRow();
3717 this.AppendAnyText(Elements, Text);
3719 break;
3720
3721 default:
3722 State.RestoreState();
3723 Text.Append(ch);
3724 break;
3725 }
3726 }
3727 else
3728 Text.Append('%');
3729 break;
3730
3731 case ')':
3732 if (!(this.emojiSource is null))
3733 {
3734 State.NextCharSameRow();
3735 this.AppendAnyText(Elements, Text);
3737 }
3738 else
3739 Text.Append('%');
3740 break;
3741
3742 default:
3743 Text.Append('%');
3744 break;
3745 }
3746 break;
3747
3748 case '^':
3749 ch2 = State.PeekNextCharSameRow();
3750 switch (ch2)
3751 {
3752 case 'a':
3753 State.NextCharSameRow();
3754 this.AppendAnyText(Elements, Text);
3755 Elements.AddLast(new HtmlEntity(this, "ordf"));
3756 break;
3757
3758 case 'o':
3759 State.NextCharSameRow();
3760 this.AppendAnyText(Elements, Text);
3761 Elements.AddLast(new HtmlEntity(this, "ordm"));
3762 break;
3763
3764 case '0':
3765 State.NextCharSameRow();
3766 this.AppendAnyText(Elements, Text);
3767 Elements.AddLast(new HtmlEntity(this, "deg"));
3768 break;
3769
3770 case '1':
3771 State.NextCharSameRow();
3772 this.AppendAnyText(Elements, Text);
3773 Elements.AddLast(new HtmlEntityUnicode(this, 185));
3774 break;
3775
3776 case '2':
3777 State.NextCharSameRow();
3778 this.AppendAnyText(Elements, Text);
3779 Elements.AddLast(new HtmlEntityUnicode(this, 178));
3780 break;
3781
3782 case '3':
3783 State.NextCharSameRow();
3784 this.AppendAnyText(Elements, Text);
3785 Elements.AddLast(new HtmlEntityUnicode(this, 179));
3786 break;
3787
3788 case '4':
3789 case '5':
3790 case '6':
3791 case '7':
3792 case '8':
3793 case '9':
3794 case 'b':
3795 case 'c':
3796 case 'd':
3797 case 'e':
3798 case 'f':
3799 case 'g':
3800 case 'h':
3801 case 'i':
3802 case 'j':
3803 case 'k':
3804 case 'l':
3805 case 'm':
3806 case 'p':
3807 case 'q':
3808 case 'u':
3809 case 'v':
3810 case 'w':
3811 case 'x':
3812 case 'y':
3813 case 'z':
3814 case 'A':
3815 case 'B':
3816 case 'C':
3817 case 'D':
3818 case 'E':
3819 case 'F':
3820 case 'G':
3821 case 'H':
3822 case 'I':
3823 case 'J':
3824 case 'K':
3825 case 'L':
3826 case 'M':
3827 case 'N':
3828 case 'O':
3829 case 'P':
3830 case 'Q':
3831 case 'R':
3832 case 'S':
3833 case 'U':
3834 case 'V':
3835 case 'W':
3836 case 'X':
3837 case 'Y':
3838 case 'Z':
3839 State.NextCharSameRow();
3840 this.AppendAnyText(Elements, Text);
3841 Elements.AddLast(new SuperScript(this, new string(ch2, 1)));
3842 break;
3843
3844 case 'T':
3845 State.NextCharSameRow();
3846 this.AppendAnyText(Elements, Text);
3847
3848 if (State.PeekNextCharSameRow() == 'M')
3849 {
3850 State.NextCharSameRow();
3851 Elements.AddLast(new HtmlEntity(this, "trade"));
3852 }
3853 else
3854 Elements.AddLast(new SuperScript(this, "T"));
3855 break;
3856
3857 case 's':
3858 State.NextCharSameRow();
3859 this.AppendAnyText(Elements, Text);
3860
3861 if (State.PeekNextCharSameRow() == 't')
3862 {
3863 State.NextCharSameRow();
3864 Elements.AddLast(new SuperScript(this, "st"));
3865 }
3866 else
3867 Elements.AddLast(new SuperScript(this, "s"));
3868 break;
3869
3870 case 'n':
3871 State.NextCharSameRow();
3872 this.AppendAnyText(Elements, Text);
3873
3874 if (State.PeekNextCharSameRow() == 'd')
3875 {
3876 State.NextCharSameRow();
3877 Elements.AddLast(new SuperScript(this, "nd"));
3878 }
3879 else
3880 Elements.AddLast(new SuperScript(this, "n"));
3881 break;
3882
3883 case 'r':
3884 State.NextCharSameRow();
3885 this.AppendAnyText(Elements, Text);
3886
3887 if (State.PeekNextCharSameRow() == 'd')
3888 {
3889 State.NextCharSameRow();
3890 Elements.AddLast(new SuperScript(this, "rd"));
3891 }
3892 else
3893 Elements.AddLast(new SuperScript(this, "r"));
3894 break;
3895
3896 case 't':
3897 State.NextCharSameRow();
3898 this.AppendAnyText(Elements, Text);
3899
3900 if (State.PeekNextCharSameRow() == 'h')
3901 {
3902 State.NextCharSameRow();
3903 Elements.AddLast(new SuperScript(this, "th"));
3904 }
3905 else
3906 Elements.AddLast(new SuperScript(this, "t"));
3907 break;
3908
3909 case '(':
3910 State.NextCharSameRow();
3911 this.AppendAnyText(Elements, Text);
3912
3913 ChildElements = new LinkedList<MarkdownElement>();
3914
3915 this.ParseBlock(State, ')', 1, ChildElements, true);
3916 Elements.AddLast(new SuperScript(this, ChildElements));
3917 break;
3918
3919 case '[':
3920 State.NextCharSameRow();
3921 this.AppendAnyText(Elements, Text);
3922
3923 ChildElements = new LinkedList<MarkdownElement>();
3924
3925 this.ParseBlock(State, ']', 1, ChildElements, true);
3926 Elements.AddLast(new SuperScript(this, ChildElements));
3927 break;
3928
3929 default:
3930 Text.Append('^');
3931 break;
3932 }
3933 break;
3934
3935 case ':':
3936 if ((ch2 = State.PeekNextCharSameRow()) <= ' ' || ch2 == 160)
3937 {
3938 if (State.IsFirstCharOnLine && ch2 > 0)
3939 {
3940 LinkedList<MarkdownElement> TotItem = null;
3941 LinkedList<MarkdownElement> Item;
3943 int i;
3944
3945 for (i = State.Start; i < State.Current; i++)
3946 {
3947 Item = this.ParseBlock(State.Rows, State.Positions, i, i);
3948 if (Item.First is null)
3949 continue;
3950
3951 if (TotItem is null)
3952 {
3953 if (Item.First.Next is null)
3954 TotItem = Item;
3955 else
3956 TotItem.AddLast(Item.First.Value);
3957 }
3958 else
3959 {
3960 if (TotItem is null)
3961 TotItem = new LinkedList<MarkdownElement>();
3962
3963 TotItem.AddLast(new NestedBlock(this, Item));
3964 }
3965 }
3966
3967 if (TotItem is null)
3968 Text.Append(ch);
3969 else
3970 {
3971 DefinitionList.AddChildren(new DefinitionTerms(this, TotItem));
3972
3973 Text.Clear();
3974 Elements.Clear();
3975 Elements.AddLast(DefinitionList);
3976
3977 while (((ch2 = State.PeekNextCharSameRow()) <= ' ' && ch2 > 0) || ch2 == 160)
3978 State.NextCharSameRow();
3979
3980 List<string> Rows = new List<string>();
3981 List<int> Positions = new List<int>()
3982 {
3983 State.CurrentPosition
3984 };
3985
3986 Rows.Add(State.RestOfRow());
3987
3988 while (!State.EOF)
3989 {
3990 if (State.PeekNextCharSameRow() == ':')
3991 {
3992 DefinitionList.AddChildren(new DefinitionDescriptions(this, new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray()))));
3993
3994 State.NextCharSameRow();
3995 State.SkipWhitespaceSameRow(3);
3996
3997 Rows.Clear();
3998 Positions.Clear();
3999
4000 Positions.Add(State.CurrentPosition);
4001 Rows.Add(State.RestOfRow());
4002 }
4003 else
4004 {
4005 State.SkipWhitespaceSameRow(4);
4006
4007 Positions.Add(State.CurrentPosition);
4008 Rows.Add(State.RestOfRow());
4009 }
4010 }
4011
4012 if (Rows.Count > 0)
4013 DefinitionList.AddChildren(new DefinitionDescriptions(this, new NestedBlock(this, this.ParseBlock(Rows.ToArray(), Positions.ToArray()))));
4014 }
4015 }
4016 else
4017 Text.Append(ch);
4018 }
4019 else if (!(this.emojiSource is null))
4020 {
4021 int LeftLevel = 1;
4022 while (ch2 == ':')
4023 {
4024 LeftLevel++;
4025 State.NextCharSameRow();
4026 ch2 = State.PeekNextCharSameRow();
4027 }
4028
4029 if (char.IsLetter(ch2) || char.IsDigit(ch2) || ch2 == '+')
4030 {
4031 this.AppendAnyText(Elements, Text);
4032 State.NextCharSameRow();
4033
4034 ch3 = State.PeekNextCharSameRow();
4035 if (char.IsLetter(ch3) || char.IsDigit(ch3) || ch3 == '_' || ch3 == '-' || ch3 == ':')
4036 Text.Append(ch2);
4037 else
4038 {
4039 switch (ch2)
4040 {
4041 case 'D':
4042 State.NextCharSameRow();
4043 if (LeftLevel > 1)
4044 Text.Append(new string(':', LeftLevel - 1));
4045
4046 this.AppendAnyText(Elements, Text);
4048 break;
4049
4050 case 'L':
4051 State.NextCharSameRow();
4052 if (LeftLevel > 1)
4053 Text.Append(new string(':', LeftLevel - 1));
4054
4055 this.AppendAnyText(Elements, Text);
4057 break;
4058
4059 case 'P':
4060 case 'p':
4061 case 'b':
4062 case 'Þ':
4063 case 'þ':
4064 State.NextCharSameRow();
4065 if (LeftLevel > 1)
4066 Text.Append(new string(':', LeftLevel - 1));
4067
4068 this.AppendAnyText(Elements, Text);
4070 break;
4071
4072 case 'O':
4073 case 'o':
4074 State.NextCharSameRow();
4075 if (LeftLevel > 1)
4076 Text.Append(new string(':', LeftLevel - 1));
4077
4078 this.AppendAnyText(Elements, Text);
4080 break;
4081
4082 case 'X':
4083 case 'x':
4084 State.NextCharSameRow();
4085 if (LeftLevel > 1)
4086 Text.Append(new string(':', LeftLevel - 1));
4087
4088 this.AppendAnyText(Elements, Text);
4090 break;
4091
4092 default:
4093 Text.Append(ch2);
4094 ch2 = (char)0;
4095 break;
4096 }
4097
4098 if (ch2 != 0)
4099 break;
4100 }
4101
4102 while (char.IsLetter(ch3 = State.PeekNextCharSameRow()) || char.IsDigit(ch3) || ch3 == '_' || ch3 == '-')
4103 {
4104 State.NextCharSameRow();
4105 Text.Append(ch3);
4106 }
4107
4108 if (ch3 == ':')
4109 {
4110 int RightLevel = 0;
4111
4112 while (ch3 == ':' && RightLevel < LeftLevel)
4113 {
4114 RightLevel++;
4115 State.NextCharSameRow();
4116 ch3 = State.PeekNextCharSameRow();
4117 }
4118
4119 Title = Text.ToString().ToLower();
4120
4121 if (EmojiUtilities.TryGetEmoji(Title, out EmojiInfo Emoji))
4122 {
4123 if (LeftLevel > RightLevel)
4124 Elements.AddLast(new InlineText(this, new string(':', LeftLevel - RightLevel)));
4125
4126 Elements.AddLast(new EmojiReference(this, Emoji, RightLevel));
4127 Text.Clear();
4128 }
4129 else
4130 {
4131 Text.Insert(0, new string(':', LeftLevel));
4132 Text.Append(new string(':', RightLevel));
4133 }
4134 }
4135 else
4136 Text.Insert(0, new string(':', LeftLevel));
4137 }
4138 else
4139 {
4140 if (LeftLevel > 1)
4141 Text.Append(new string(':', LeftLevel - 1));
4142
4143 switch (ch2)
4144 {
4145 case '\'':
4146 State.NextCharSameRow();
4147
4148 switch (State.PeekNextCharSameRow())
4149 {
4150 case ')':
4151 State.NextCharSameRow();
4152 this.AppendAnyText(Elements, Text);
4153 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji_joy));
4154 break;
4155
4156 case '(':
4157 State.NextCharSameRow();
4158 this.AppendAnyText(Elements, Text);
4159 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji_cry));
4160 break;
4161
4162 case '-':
4163 State.NextCharSameRow();
4164 if ((ch3 = State.PeekNextCharSameRow()) == ')')
4165 {
4166 State.NextCharSameRow();
4167 this.AppendAnyText(Elements, Text);
4168 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji_joy));
4169 }
4170 else if (ch3 == '(')
4171 {
4172 State.NextCharSameRow();
4173 this.AppendAnyText(Elements, Text);
4174 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji_cry));
4175 }
4176 else
4177 Text.Append(":'-");
4178 break;
4179
4180 default:
4181 Text.Append(":'");
4182 break;
4183 }
4184 break;
4185
4186 case '-':
4187 State.NextCharSameRow();
4188
4189 switch (State.PeekNextCharSameRow())
4190 {
4191 case ')':
4192 case ']':
4193 State.NextCharSameRow();
4194 this.AppendAnyText(Elements, Text);
4196 break;
4197
4198 case '(':
4199 case '[':
4200 State.NextCharSameRow();
4201 this.AppendAnyText(Elements, Text);
4203 break;
4204
4205 case 'D':
4206 State.NextCharSameRow();
4207 this.AppendAnyText(Elements, Text);
4209 break;
4210
4211 case '*':
4212 State.NextCharSameRow();
4213 this.AppendAnyText(Elements, Text);
4215 break;
4216
4217 case '/':
4218 case '.':
4219 case '\\':
4220 case 'L':
4221 State.NextCharSameRow();
4222 this.AppendAnyText(Elements, Text);
4224 break;
4225
4226 case 'P':
4227 case 'p':
4228 case 'b':
4229 case 'Þ':
4230 case 'þ':
4231 State.NextCharSameRow();
4232 this.AppendAnyText(Elements, Text);
4234 break;
4235
4236 case 'O':
4237 case 'o':
4238 State.NextCharSameRow();
4239 this.AppendAnyText(Elements, Text);
4241 break;
4242
4243 case 'X':
4244 case 'x':
4245 case '#':
4246 State.NextCharSameRow();
4247 this.AppendAnyText(Elements, Text);
4249 break;
4250
4251 case '-':
4252 State.NextCharSameRow();
4253 if ((ch3 = State.PeekNextCharSameRow()) == ')')
4254 {
4255 State.NextCharSameRow();
4256 this.AppendAnyText(Elements, Text);
4257 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji_joy));
4258 }
4259 else if (ch3 == '(')
4260 {
4261 State.NextCharSameRow();
4262 this.AppendAnyText(Elements, Text);
4263 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji_cry));
4264 }
4265 else
4266 Text.Append(":--");
4267 break;
4268
4269 case '1':
4270 State.NextCharSameRow();
4271 if (State.PeekNextCharSameRow() == ':')
4272 {
4273 State.NextCharSameRow();
4274 this.AppendAnyText(Elements, Text);
4275 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji__1));
4276 }
4277 else
4278 Text.Append(":-1");
4279 break;
4280
4281 default:
4282 Text.Append(":-");
4283 break;
4284 }
4285 break;
4286
4287 case ')':
4288 case ']':
4289 State.NextCharSameRow();
4290 this.AppendAnyText(Elements, Text);
4292 break;
4293
4294 case '(':
4295 case '[':
4296 State.NextCharSameRow();
4297 this.AppendAnyText(Elements, Text);
4299 break;
4300
4301 case '*':
4302 State.NextCharSameRow();
4303 this.AppendAnyText(Elements, Text);
4305 break;
4306
4307 case '/':
4308 case '\\':
4309 State.NextCharSameRow();
4310 this.AppendAnyText(Elements, Text);
4312 break;
4313
4314 case '#':
4315 State.NextCharSameRow();
4316 this.AppendAnyText(Elements, Text);
4318 break;
4319
4320 case '@':
4321 State.NextCharSameRow();
4322 this.AppendAnyText(Elements, Text);
4324 break;
4325
4326 case '$':
4327 State.NextCharSameRow();
4328 this.AppendAnyText(Elements, Text);
4330 break;
4331
4332 case '^':
4333 State.NextCharSameRow();
4334 if (State.PeekNextCharSameRow() == '*')
4335 {
4336 State.NextCharSameRow();
4337 this.AppendAnyText(Elements, Text);
4339 }
4340 else
4341 Text.Append(":^");
4342 break;
4343
4344 default:
4345 Text.Append(ch);
4346 break;
4347 }
4348 }
4349 }
4350 else
4351 Text.Append(ch);
4352 break;
4353
4354 case ';':
4355 if (!(this.emojiSource is null))
4356 {
4357 switch (State.PeekNextCharSameRow())
4358 {
4359 case ')':
4360 case ']':
4361 case 'D':
4362 State.NextCharSameRow();
4363 this.AppendAnyText(Elements, Text);
4365 break;
4366
4367 case '(':
4368 case '[':
4369 State.NextCharSameRow();
4370 this.AppendAnyText(Elements, Text);
4371 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji_cry));
4372 break;
4373
4374 case '-':
4375 State.NextCharSameRow();
4376 switch (State.PeekNextCharSameRow())
4377 {
4378 case ')':
4379 case ']':
4380 State.NextCharSameRow();
4381 this.AppendAnyText(Elements, Text);
4383 break;
4384
4385 case '(':
4386 case '[':
4387 State.NextCharSameRow();
4388 this.AppendAnyText(Elements, Text);
4389 Elements.AddLast(new EmojiReference(this, EmojiUtilities.Emoji_cry));
4390 break;
4391
4392 default:
4393 Text.Append(";-");
4394 break;
4395 }
4396 break;
4397
4398 case '^':
4399 State.NextCharSameRow();
4400 if (State.PeekNextCharSameRow() == ')')
4401 {
4402 State.NextCharSameRow();
4403 this.AppendAnyText(Elements, Text);
4405 }
4406 else
4407 Text.Append(";^");
4408 break;
4409
4410 default:
4411 Text.Append(';');
4412 break;
4413 }
4414 }
4415 else
4416 Text.Append(ch);
4417 break;
4418
4419 case 'X':
4420 case 'x':
4421 if (!(this.emojiSource is null) && (char.IsPunctuation(PrevChar) || char.IsWhiteSpace(PrevChar)))
4422 {
4423 switch (State.PeekNextCharSameRow())
4424 {
4425 case '-':
4426 State.BackupState();
4427 State.NextCharSameRow();
4428 switch (State.PeekNextCharSameRow())
4429 {
4430 case 'P':
4431 case 'p':
4432 case 'b':
4433 case 'Þ':
4434 case 'þ':
4435 State.DiscardBackup();
4436 State.NextCharSameRow();
4437 this.AppendAnyText(Elements, Text);
4439 break;
4440
4441 case ')':
4442 State.DiscardBackup();
4443 State.NextCharSameRow();
4444 this.AppendAnyText(Elements, Text);
4446 break;
4447
4448 default:
4449 State.RestoreState();
4450 Text.Append(ch);
4451 break;
4452 }
4453 break;
4454
4455 case ')':
4456 State.NextCharSameRow();
4457 this.AppendAnyText(Elements, Text);
4459 break;
4460
4461 default:
4462 Text.Append(ch);
4463 break;
4464 }
4465 }
4466 else
4467 Text.Append(ch);
4468 break;
4469
4470 case 'B':
4471 if (!(this.emojiSource is null) && (char.IsPunctuation(PrevChar) || char.IsWhiteSpace(PrevChar)))
4472 {
4473 switch (State.PeekNextCharSameRow())
4474 {
4475 case '-':
4476 State.BackupState();
4477 State.NextCharSameRow();
4478 switch (State.PeekNextCharSameRow())
4479 {
4480 case ')':
4481 case 'D':
4482 State.DiscardBackup();
4483 State.NextCharSameRow();
4484 this.AppendAnyText(Elements, Text);
4486 break;
4487
4488 default:
4489 State.RestoreState();
4490 Text.Append(ch);
4491 break;
4492 }
4493 break;
4494
4495 case ')':
4496 State.NextCharSameRow();
4497 this.AppendAnyText(Elements, Text);
4499 break;
4500
4501 default:
4502 Text.Append(ch);
4503 break;
4504 }
4505 }
4506 else
4507 Text.Append(ch);
4508 break;
4509
4510 case 'd':
4511 if (!(this.emojiSource is null) && (char.IsPunctuation(PrevChar) || char.IsWhiteSpace(PrevChar)) && State.PeekNextCharSameRow() == ':')
4512 {
4513 State.NextCharSameRow();
4514 this.AppendAnyText(Elements, Text);
4516 }
4517 else
4518 Text.Append(ch);
4519 break;
4520
4521 case 'O':
4522 if (!(this.emojiSource is null) && (char.IsPunctuation(PrevChar) || char.IsWhiteSpace(PrevChar)))
4523 {
4524 switch (State.PeekNextCharSameRow())
4525 {
4526 case ':':
4527 State.BackupState();
4528 State.NextCharSameRow();
4529 switch (State.NextCharSameRow())
4530 {
4531 case ')':
4532 case '3':
4533 State.DiscardBackup();
4534 this.AppendAnyText(Elements, Text);
4536 break;
4537
4538 case '-':
4539 if ((ch3 = State.NextCharSameRow()) == ')' || ch3 == '3')
4540 {
4541 State.DiscardBackup();
4542 this.AppendAnyText(Elements, Text);
4544 }
4545 else
4546 {
4547 State.RestoreState();
4548 Text.Append(ch);
4549 }
4550 break;
4551
4552 default:
4553 State.RestoreState();
4554 Text.Append(ch);
4555 break;
4556 }
4557 break;
4558
4559 case ';':
4560 State.BackupState();
4561 State.NextCharSameRow();
4562 if (State.NextCharSameRow() == '-')
4563 {
4564 if (State.NextCharSameRow() == ')')
4565 {
4566 State.DiscardBackup();
4567 this.AppendAnyText(Elements, Text);
4569 break;
4570 }
4571 }
4572
4573 State.RestoreState();
4574 Text.Append(ch);
4575 break;
4576
4577 case '=':
4578 State.BackupState();
4579 State.NextCharSameRow();
4580 if (State.PeekNextCharSameRow() == ')')
4581 {
4582 State.NextCharSameRow();
4583 State.DiscardBackup();
4584 this.AppendAnyText(Elements, Text);
4586 }
4587 else
4588 {
4589 State.RestoreState();
4590 Text.Append(ch);
4591 }
4592 break;
4593
4594 case '_':
4595 State.BackupState();
4596 State.NextCharSameRow();
4597 if (State.PeekNextCharSameRow() == 'O')
4598 {
4599 State.NextCharSameRow();
4600 State.DiscardBackup();
4601 this.AppendAnyText(Elements, Text);
4603 }
4604 else
4605 {
4606 State.RestoreState();
4607 Text.Append(ch);
4608 }
4609 break;
4610
4611 default:
4612 Text.Append(ch);
4613 break;
4614 }
4615 }
4616 else
4617 Text.Append(ch);
4618 break;
4619
4620 case 'h':
4621 if (char.IsWhiteSpace(PrevChar) && State.PeekNextCharSameRow() == 't')
4622 {
4623 FirstCharOnLine = State.IsFirstCharOnLine;
4624
4625 chs = State.PeekNextChars(7);
4626 if (chs[1] == 't' && chs[2] == 'p' &&
4627 ((chs[3] == ':' && chs[4] == '/' && chs[5] == '/') ||
4628 (chs[3] == 's' && chs[4] == ':' && chs[5] == '/' && chs[6] == '/')))
4629 {
4630 this.AppendAnyText(Elements, Text);
4631
4632 Text.Clear();
4633 Text.Append('h');
4634
4635 if (chs[3] == ':')
4636 ch2 = (char)6;
4637 else
4638 ch2 = (char)7;
4639
4640 while (ch2 > 0)
4641 {
4642 Text.Append(State.NextChar());
4643 ch2--;
4644 }
4645
4646 while ((ch2 = State.PeekNextCharSameRow()) > ' ' && ch2 != 160)
4647 {
4648 Text.Append(ch2);
4649 State.NextChar();
4650 }
4651
4652 Url = Text.ToString();
4653 Text.Clear();
4654
4655 if (FirstCharOnLine && State.PeekNextNonWhitespaceCharSameRow(false) == 0)
4656 {
4657 IMultimediaContent Handler = Multimedia.GetMultimediaHandler<IMultimediaHtmlRenderer>(Url);
4658 if (!(Handler is null) && Handler.EmbedInlineLink(Url))
4659 {
4660 ChildElements = new LinkedList<MarkdownElement>();
4661 ChildElements.AddLast(new InlineText(this, Url));
4662
4663 Elements.AddLast(new Multimedia(this, ChildElements, true,
4664 new MultimediaItem(this, Url, string.Empty, null, null)));
4665
4666 break;
4667 }
4668 }
4669
4670 Elements.AddLast(new AutomaticLinkUrl(this, Url));
4671 }
4672 else
4673 Text.Append(ch);
4674 }
4675 else
4676 Text.Append(ch);
4677 break;
4678
4679 case '\\':
4680 switch (ch2 = State.PeekNextCharSameRow())
4681 {
4682 case '*':
4683 case '_':
4684 case '~':
4685 case '\\':
4686 case '`':
4687 case '{':
4688 case '}':
4689 case '[':
4690 case ']':
4691 case '(':
4692 case ')':
4693 case '<':
4694 case '>':
4695 case '#':
4696 case '+':
4697 case '-':
4698 case '.':
4699 case '!':
4700 case '\'':
4701 case '"':
4702 case '^':
4703 case '%':
4704 case '&':
4705 case '=':
4706 case ':':
4707 case '|':
4708 case 'h':
4709 Text.Append(ch2);
4710 State.NextCharSameRow();
4711 break;
4712
4713 case '0':
4714 case 'O':
4715 if (!(this.emojiSource is null))
4716 {
4717 State.BackupState();
4718 State.NextCharSameRow();
4719 if (State.PeekNextCharSameRow() == '/')
4720 {
4721 State.DiscardBackup();
4722 State.NextCharSameRow();
4723 this.AppendAnyText(Elements, Text);
4725 }
4726 else
4727 {
4728 State.RestoreState();
4729 Text.Append('\\');
4730 }
4731 }
4732 else
4733 Text.Append('\\');
4734 break;
4735
4736 default:
4737 Text.Append('\\');
4738 break;
4739 }
4740 break;
4741
4742 default:
4743 Text.Append(ch);
4744 break;
4745 }
4746
4747 PrevChar = State.LastCharacter;
4748 }
4749
4750 this.AppendAnyText(Elements, Text);
4751
4752 return (ch == TerminationCharacter) || AcceptIncomplete;
4753 }
4754
4755 private static bool IsLeftQuote(char PrevChar, char NextChar)
4756 {
4757 bool Left = (PrevChar <= ' ' || PrevChar == 160 || char.IsPunctuation(PrevChar) || char.IsSeparator(PrevChar));
4758 bool Right = (NextChar <= ' ' || NextChar == 160 || char.IsPunctuation(NextChar) || char.IsSeparator(NextChar));
4759
4760 if (Left && Right)
4761 {
4762 if (char.IsSeparator(PrevChar))
4763 return true;
4764 else if (char.IsSeparator(NextChar))
4765 return false;
4766 else if (PrevChar == ')' || PrevChar == ']' || PrevChar == '}')
4767 return false;
4768 else if (NextChar == '(' || NextChar == '[' || NextChar == '[')
4769 return true;
4770 else
4771 return false;
4772 }
4773 else
4774 return Left;
4775 }
4776
4777 private static void ParseWidthHeight(BlockParseState State, out int? Width, out int? Height)
4778 {
4779 Width = null;
4780 Height = null;
4781
4782 char ch = State.PeekNextNonWhitespaceCharSameRow(true);
4783 if (ch >= '0' && ch <= '9')
4784 {
4785 StringBuilder Text = new StringBuilder();
4786
4787 Text.Append(ch);
4788 State.NextNonWhitespaceCharSameRow();
4789
4790 ch = State.PeekNextCharSameRow();
4791 while (ch >= '0' && ch <= '9')
4792 {
4793 Text.Append(ch);
4794 State.NextCharSameRow();
4795 ch = State.PeekNextCharSameRow();
4796 }
4797
4798 if (int.TryParse(Text.ToString(), out int i))
4799 {
4800 Width = i;
4801 Text.Clear();
4802
4803 ch = State.PeekNextNonWhitespaceCharSameRow(true);
4804 if (ch >= '0' && ch <= '9')
4805 {
4806 Text.Append(ch);
4807 State.NextNonWhitespaceCharSameRow();
4808
4809 ch = State.PeekNextCharSameRow();
4810 while (ch >= '0' && ch <= '9')
4811 {
4812 Text.Append(ch);
4813 State.NextCharSameRow();
4814 ch = State.PeekNextCharSameRow();
4815 }
4816
4817 if (int.TryParse(Text.ToString(), out i))
4818 Height = i;
4819 }
4820 }
4821 }
4822 }
4823
4824 private void AppendAnyText(LinkedList<MarkdownElement> Elements, StringBuilder Text)
4825 {
4826 if (Text.Length > 0)
4827 {
4828 string s = Text.ToString();
4829 Text.Clear();
4830
4831 if (!(Elements.First is null) || !string.IsNullOrEmpty(s.Trim()))
4832 Elements.AddLast(new InlineText(this, s));
4833 }
4834 }
4835
4836 private void FixSyntaxError(LinkedList<MarkdownElement> Elements, string Prefix, LinkedList<MarkdownElement> ChildElements)
4837 {
4838 Elements.AddLast(new InlineText(this, Prefix));
4839 foreach (MarkdownElement E in ChildElements)
4840 Elements.AddLast(E);
4841 }
4842
4843 internal static bool IsPrefixedByNumber(string s, out int Numeral)
4844 {
4845 int i, c = s.Length;
4846 char ch;
4847
4848 i = 0;
4849 while (i < c && char.IsDigit(s[i]))
4850 i++;
4851
4852 if (i == 0)
4853 {
4854 Numeral = 0;
4855 return false;
4856 }
4857
4858 if (!int.TryParse(s.Substring(0, i), out Numeral) || i == c || s[i] != '.')
4859 return false;
4860
4861 i++;
4862 if (i < c && (ch = s[i]) > ' ' && ch != 160)
4863 return false;
4864
4865 return true;
4866 }
4867
4868 internal static bool IsPrefixedBy(string s, string Prefix, bool MustHaveWhiteSpaceAfter)
4869 {
4870 int i;
4871 char ch;
4872
4873 if (!s.StartsWith(Prefix))
4874 return false;
4875
4876 if (MustHaveWhiteSpaceAfter)
4877 {
4878 if (s.Length == (i = Prefix.Length))
4879 return false;
4880
4881 return (ch = s[i]) <= ' ' || ch == 160;
4882 }
4883 else
4884 return true;
4885 }
4886
4887 private static bool IsPrefixedBy(string s, char ch, out int Count, bool MustHaveWhiteSpaceAfter)
4888 {
4889 int c = s.Length;
4890
4891 Count = 0;
4892 while (Count < c && s[Count] == ch)
4893 Count++;
4894
4895 if (Count == 0)
4896 return false;
4897
4898 if (MustHaveWhiteSpaceAfter)
4899 {
4900 if (s.Length == Count)
4901 return false;
4902
4903 return (ch = s[Count]) <= ' ' || ch == 160;
4904 }
4905 else
4906 return true;
4907 }
4908
4909 internal static bool IsSuffixedBy(string s, string Suffix)
4910 {
4911 return s.EndsWith(Suffix);
4912 }
4913
4914 /*private static bool IsSuffixedBy(string s, char ch, out int Count)
4915 {
4916 int c = s.Length;
4917
4918 Count = 0;
4919 while (Count < c && s[c - Count - 1] == ch)
4920 Count++;
4921
4922 if (Count == 0)
4923 return false;
4924
4925 return true;
4926 }*/
4927
4928 private static bool IsUnderline(string s, char ch, bool AllowSpaces, bool OnlyOneSpace)
4929 {
4930 int i, c = s.Length;
4931 bool LastSpace = true;
4932 int Count = 0;
4933 char ch2;
4934
4935 for (i = 0; i < c; i++)
4936 {
4937 ch2 = s[i];
4938 if (ch2 == ch)
4939 {
4940 Count++;
4941 LastSpace = false;
4942 }
4943 else if (ch2 == ' ' || ch2 == 160)
4944 {
4945 if (OnlyOneSpace && (!AllowSpaces || LastSpace))
4946 return false;
4947
4948 LastSpace = true;
4949 }
4950 else
4951 return false;
4952 }
4953
4954 return Count >= 3;
4955 }
4956
4957 private static List<Block> ParseTextToBlocks(string MarkdownText)
4958 {
4959 List<Block> Blocks = new List<Block>();
4960 List<string> Rows = new List<string>();
4961 List<int> Positions = new List<int>();
4962 int FirstLineIndent = 0;
4963 int LineIndent = 0;
4964 int RowStart = 0;
4965 int RowEnd = 0;
4966 int Pos, Len;
4967 char ch;
4968 bool InBlock = false;
4969 bool InRow = false;
4970 bool NonWhitespaceInRow = false;
4971 bool StartsWithHashSigns = false;
4972 bool IsHeader = false;
4973 bool HasRows = false;
4974
4975 Len = MarkdownText.Length;
4976
4977 for (Pos = 0; Pos < Len; Pos++)
4978 {
4979 ch = MarkdownText[Pos];
4980
4981 if (ch == '\n')
4982 {
4983 if (InBlock)
4984 {
4985 if (InRow && NonWhitespaceInRow)
4986 {
4987 if (HasRows &&
4988 LineIndent < FirstLineIndent &&
4989 IsListPrefix(MarkdownText, RowStart))
4990 {
4991 Blocks.Add(new Block(Rows.ToArray(), Positions.ToArray(), FirstLineIndent / 4));
4992 Rows.Clear();
4993 Positions.Clear();
4994 FirstLineIndent = LineIndent;
4995 }
4996
4997 Positions.Add(RowStart);
4998 Rows.Add(MarkdownText.Substring(RowStart, RowEnd - RowStart + 1));
4999 InRow = false;
5000 HasRows = true;
5001
5002 if (IsHeader && Rows.Count == 1)
5003 {
5004 Blocks.Add(new Block(Rows.ToArray(), Positions.ToArray(), FirstLineIndent / 4));
5005 Rows.Clear();
5006 Positions.Clear();
5007 InBlock = false;
5008 HasRows = false;
5009 FirstLineIndent = 0;
5010 }
5011 }
5012 else
5013 {
5014 Blocks.Add(new Block(Rows.ToArray(), Positions.ToArray(), FirstLineIndent / 4));
5015 Rows.Clear();
5016 Positions.Clear();
5017 InBlock = false;
5018 InRow = false;
5019 HasRows = false;
5020 FirstLineIndent = 0;
5021 }
5022 }
5023 else
5024 FirstLineIndent = 0;
5025
5026 LineIndent = 0;
5027 NonWhitespaceInRow = false;
5028 StartsWithHashSigns = false;
5029 IsHeader = false;
5030 }
5031 else if (ch <= ' ' || ch == 160)
5032 {
5033 if (InBlock)
5034 {
5035 if (InRow)
5036 {
5037 RowEnd = Pos;
5038
5039 if (StartsWithHashSigns)
5040 IsHeader = true;
5041 }
5042 else
5043 {
5044 if (LineIndent >= FirstLineIndent)
5045 {
5046 InRow = true;
5047 RowStart = RowEnd = Pos;
5048 }
5049
5050 if (ch == '\t')
5051 LineIndent += 4;
5052 else if (ch == ' ' || ch == 160)
5053 LineIndent++;
5054 }
5055 }
5056 else if (ch == '\t')
5057 FirstLineIndent += 4;
5058 else if (ch == ' ' || ch == 160)
5059 FirstLineIndent++;
5060
5061 StartsWithHashSigns = false;
5062 }
5063 else
5064 {
5065 if (!InRow)
5066 {
5067 InRow = true;
5068 InBlock = true;
5069 RowStart = Pos;
5070
5071 if (ch == '#')
5072 StartsWithHashSigns = true;
5073 }
5074 else if (ch != '#')
5075 StartsWithHashSigns = false;
5076
5077 RowEnd = Pos;
5078 NonWhitespaceInRow = true;
5079 }
5080 }
5081
5082 if (InBlock)
5083 {
5084 if (InRow && NonWhitespaceInRow)
5085 {
5086 Positions.Add(RowStart);
5087 Rows.Add(MarkdownText.Substring(RowStart, RowEnd - RowStart + 1));
5088 //HasRows = true;
5089 }
5090
5091 Blocks.Add(new Block(Rows.ToArray(), Positions.ToArray(), FirstLineIndent / 4));
5092 }
5093
5094 return Blocks;
5095 }
5096
5097 private static bool IsListPrefix(string MarkdownText, int Pos)
5098 {
5099 int c = MarkdownText.Length;
5100 char ch = MarkdownText[Pos++];
5101 bool ExpectPeriod;
5102
5103 if (ch == '*' || ch == '+' || ch == '-')
5104 ExpectPeriod = false;
5105 else if (ch == '#')
5106 ExpectPeriod = true;
5107 else if (ch == '[')
5108 {
5109 ExpectPeriod = false;
5110 if (Pos >= c)
5111 return false;
5112
5113 ch = MarkdownText[Pos++];
5114 if (ch != ' ' && ch != 'x' && ch != 'X')
5115 return false;
5116
5117 if (Pos >= c)
5118 return false;
5119
5120 ch = MarkdownText[Pos++];
5121 if (ch != ']')
5122 return false;
5123 }
5124 else if (ch >= '0' && ch <= '9')
5125 {
5126 ExpectPeriod = true;
5127
5128 while (Pos < c && (ch = MarkdownText[Pos]) >= '0' && ch <= '9')
5129 Pos++;
5130 }
5131 else
5132 return false;
5133
5134 if (ExpectPeriod)
5135 {
5136 if (Pos >= c)
5137 return false;
5138
5139 ch = MarkdownText[Pos++];
5140 if (ch != '.')
5141 return false;
5142 }
5143
5144 if (Pos >= c)
5145 return false;
5146
5147 ch = MarkdownText[Pos++];
5148
5149 return ch <= ' ' || ch == 160;
5150 }
5151
5156 public async Task RenderDocument(IRenderer Output)
5157 {
5158 if (this.metaData.TryGetValue("MASTER", out KeyValuePair<string, bool>[] Master) && Master.Length == 1)
5159 {
5160 await this.LoadMasterIfNotLoaded(Master[0].Key);
5161 this.master.ClearFootnoteReferences();
5162 await Output.RenderDocument(this.master, false);
5163 }
5164 else
5165 {
5166 this.ClearFootnoteReferences();
5167 await Output.RenderDocument(this, false);
5168 }
5169
5170 this.ProcessAsyncTasks();
5171 }
5172
5176 private void ClearFootnoteReferences()
5177 {
5178 if (!(this.footnotes is null))
5179 {
5180 foreach (Footnote Footnote in this.footnotes.Values)
5181 Footnote.Referenced = false;
5182 }
5183 }
5184
5188 public IEnumerable<string> FootnoteOrder => this.footnoteOrder;
5189
5190 private async Task LoadMasterIfNotLoaded(string MasterMetaValue)
5191 {
5192 if (this.master is null)
5193 {
5194 string FileName;
5195
5196 if (!string.IsNullOrEmpty(this.fileName))
5197 FileName = this.settings.GetFileName(this.fileName, MasterMetaValue);
5198 else if (!string.IsNullOrEmpty(this.resourceName))
5199 {
5200 FileName = Path.Combine(this.resourceName, MasterMetaValue);
5201 if (!(this.settings.ResourceMap is null) && this.settings.ResourceMap.TryGetFileName(FileName, false, out string s))
5202 FileName = s;
5203 }
5204 else
5205 FileName = MasterMetaValue;
5206
5208 this.master = await CreateAsync(MarkdownText, this.settings);
5209 this.master.fileName = FileName;
5210 this.master.syntaxHighlighting |= this.syntaxHighlighting;
5211
5212 if (this.master.metaData.ContainsKey("MASTER"))
5213 {
5214 throw new GenericException("Master documents are not allowed to be embedded in other master documents.",
5215 EventType.Error, FileName, this.fileName);
5216 }
5217
5218 CopyMetaDataTags(this, this.master, true);
5219
5220 this.master.detail = this;
5221 }
5222 }
5223
5224 internal static void CopyMetaDataTags(MarkdownDocument Details, MarkdownDocument Master, bool UpdateMasterPaths)
5225 {
5226 if (UpdateMasterPaths && !string.IsNullOrEmpty(Details.fileName) && !string.IsNullOrEmpty(Master.fileName))
5227 {
5228 string DetailsFileName = Path.GetFullPath(Details.fileName);
5229 string MasterFileName = Path.GetFullPath(Master.fileName);
5230 string DetailsFolder = Path.GetDirectoryName(DetailsFileName);
5231 string MasterFolder = Path.GetDirectoryName(MasterFileName);
5232
5233 if (DetailsFolder != MasterFolder)
5234 {
5235 string Prefix = null;
5236
5237 foreach (KeyValuePair<string, KeyValuePair<string, bool>[]> Meta in Master.metaData)
5238 {
5239 switch (Meta.Key)
5240 {
5241 case "ALTERNATE":
5242 case "COPYRIGHT":
5243 case "CSS":
5244 case "ICON":
5245 case "HELP":
5246 case "IMAGE":
5247 case "INIT":
5248 case "JAVASCRIPT":
5249 case "LOGIN":
5250 case "NEXT":
5251 case "PREV":
5252 case "PREVIOUS":
5253 case "SCRIPT":
5254 case "WEB":
5255 int i, j, k, l, c, d;
5256
5257 for (k = 0, l = Meta.Value.Length; k < l; k++)
5258 {
5259 string s = Meta.Value[k].Key;
5260 if (string.IsNullOrEmpty(s))
5261 continue;
5262
5263 if (s[0] == Path.DirectorySeparatorChar || s[0] == '/')
5264 continue;
5265
5266 if (Prefix is null)
5267 {
5268 string[] DetailsParts = DetailsFolder.Split(Path.DirectorySeparatorChar);
5269 string[] MasterParts = MasterFolder.Split(Path.DirectorySeparatorChar);
5270
5271 i = 0;
5272 c = DetailsParts.Length;
5273 d = MasterParts.Length;
5274
5275 while (i < c && i < d && string.Compare(DetailsParts[i], MasterParts[i], true) == 0)
5276 i++;
5277
5278 StringBuilder sb = new StringBuilder();
5279
5280 j = c - i;
5281
5282 while (j-- > 0)
5283 sb.Append("../");
5284
5285 while (i < d)
5286 {
5287 sb.Append(MasterParts[i++]);
5288 sb.Append('/');
5289 }
5290
5291 Prefix = sb.ToString();
5292 }
5293
5294 Meta.Value[k] = new KeyValuePair<string, bool>(Prefix + s, Meta.Value[k].Value);
5295 }
5296 break;
5297 }
5298 }
5299 }
5300 }
5301
5302 foreach (KeyValuePair<string, KeyValuePair<string, bool>[]> Meta in Details.metaData)
5303 {
5304 if (Master.metaData.TryGetValue(Meta.Key, out KeyValuePair<string, bool>[] Meta0))
5305 Master.metaData[Meta.Key] = Concat(Meta0, Meta.Value);
5306 else
5307 Master.metaData[Meta.Key] = Meta.Value;
5308 }
5309 }
5310
5311 private static KeyValuePair<string, bool>[] Concat(KeyValuePair<string, bool>[] Meta1, KeyValuePair<string, bool>[] Meta2)
5312 {
5313 int c = Meta1.Length;
5314 int d = Meta2.Length;
5315 KeyValuePair<string, bool>[] Result = new KeyValuePair<string, bool>[c + d];
5316
5317 Array.Copy(Meta1, 0, Result, 0, c);
5318 Array.Copy(Meta2, 0, Result, c, d);
5319
5320 return Result;
5321 }
5322
5326 internal bool NeedsToDisplayFootnotes
5327 {
5328 get
5329 {
5330 if (this.footnotes is null)
5331 return false;
5332
5333 foreach (Footnote Footnote in this.footnotes.Values)
5334 {
5335 if (Footnote.Referenced)
5336 return true;
5337 }
5338
5339 return false;
5340 }
5341 }
5342
5350 public string CheckURL(string Url, string URL)
5351 {
5352 bool IsRelative = Url.IndexOf(':') < 0;
5353
5354 if (Url.StartsWith("httpx:", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(this.settings.HttpxProxy))
5355 {
5356 if (!string.IsNullOrEmpty(this.settings.LocalHttpxResourcePath) &&
5357 Url.StartsWith(this.settings.LocalHttpxResourcePath, StringComparison.OrdinalIgnoreCase))
5358 {
5359 Url = Url.Substring(this.settings.LocalHttpxResourcePath.Length);
5360 if (!Url.StartsWith("/") && this.settings.LocalHttpxResourcePath.EndsWith("/"))
5361 Url = "/" + Url;
5362
5363 IsRelative = true;
5364 }
5365 else
5366 {
5367 Url = this.settings.HttpxProxy.Replace("%URL%", Url);
5368 IsRelative = this.settings.HttpxProxy.IndexOf(':') < 0;
5369 }
5370 }
5371
5372 if (IsRelative && !string.IsNullOrEmpty(URL))
5373 {
5374 if (Uri.TryCreate(new Uri(URL), Url, out Uri AbsoluteUri))
5375 Url = AbsoluteUri.ToString();
5376 }
5377
5378 return Url;
5379 }
5380
5385 public Task<string> GenerateMarkdown()
5386 {
5387 return this.GenerateMarkdown(true);
5388 }
5389
5396 public async Task<string> GenerateMarkdown(bool PortableSyntax)
5397 {
5398 StringBuilder Output = new StringBuilder();
5399 await this.GenerateMarkdown(Output, PortableSyntax);
5400 return Output.ToString();
5401 }
5402
5407 public Task GenerateMarkdown(StringBuilder Output)
5408 {
5409 return this.GenerateMarkdown(Output, true);
5410 }
5411
5418 public async Task GenerateMarkdown(StringBuilder Output, bool PortableSyntax)
5419 {
5420 using (MarkdownRenderer Renderer = new MarkdownRenderer(Output)
5421 {
5422 PortableSyntax = PortableSyntax
5423 })
5424 {
5425 await this.RenderDocument(Renderer);
5426 }
5427 }
5428
5433 public async Task<string> GenerateHTML()
5434 {
5435 StringBuilder Output = new StringBuilder();
5436 await this.GenerateHTML(Output);
5437 return Output.ToString();
5438 }
5439
5444 public Task GenerateHTML(StringBuilder Output)
5445 {
5446 return this.GenerateHTML(Output, new HtmlSettings());
5447 }
5448
5454 public async Task<string> GenerateHTML(HtmlSettings HtmlSettings)
5455 {
5456 StringBuilder Output = new StringBuilder();
5457 await this.GenerateHTML(Output, HtmlSettings);
5458 return Output.ToString();
5459 }
5460
5466 public async Task GenerateHTML(StringBuilder Output, HtmlSettings HtmlSettings)
5467 {
5468 using (HtmlRenderer Renderer = new HtmlRenderer(Output, HtmlSettings))
5469 {
5470 await this.RenderDocument(Renderer);
5471 }
5472 }
5473
5478 public async Task<string> GeneratePlainText()
5479 {
5480 StringBuilder Output = new StringBuilder();
5481 await this.GeneratePlainText(Output);
5482 return Output.ToString();
5483 }
5484
5489 public async Task GeneratePlainText(StringBuilder Output)
5490 {
5491 using (TextRenderer Renderer = new TextRenderer(Output))
5492 {
5493 await this.RenderDocument(Renderer);
5494 }
5495 }
5496
5502 public Multimedia GetReference(string Label)
5503 {
5504 if (this.references.TryGetValue(Label.ToLower(), out Multimedia Result))
5505 return Result;
5506 else
5507 return null;
5508 }
5509
5510 private static readonly char[] whiteSpace = new char[]
5511 {
5512 (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9,(char)10,
5513 (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19,(char)20,
5514 (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29,(char)30,
5515 (char)31, (char)32
5516 };
5517
5521 public Header[] Headers => this.headers.ToArray();
5522
5529 public bool TryGetMetaData(string Key, out KeyValuePair<string, bool>[] Value)
5530 {
5531 return this.metaData.TryGetValue(Key.ToUpper(), out Value);
5532 }
5533
5539 public void AddMetaData(string Key, string Value)
5540 {
5541 if (this.metaData.TryGetValue(Key, out KeyValuePair<string, bool>[] Records))
5542 {
5543 List<KeyValuePair<string, bool>> Values = new List<KeyValuePair<string, bool>>();
5544 Values.AddRange(Records);
5545 Values.Add(new KeyValuePair<string, bool>(Value.Trim(), Value.EndsWith(" ")));
5546 }
5547 else
5548 this.metaData[Key] = new KeyValuePair<string, bool>[] { new KeyValuePair<string, bool>(Value.Trim(), Value.EndsWith(" ")) };
5549 }
5550
5556 public string[] GetMetaData(string Key)
5557 {
5558 if (!this.metaData.TryGetValue(Key.ToUpper(), out KeyValuePair<string, bool>[] Value))
5559 return new string[0];
5560
5561 int i, c = Value.Length;
5562 string[] Result = new string[c];
5563
5564 for (i = 0; i < c; i++)
5565 Result[i] = Value[i].Key;
5566
5567 return Result;
5568 }
5569
5573 public string[] MetaDataKeys
5574 {
5575 get
5576 {
5577 string[] Keys = new string[this.metaData.Count];
5578 this.metaData.Keys.CopyTo(Keys, 0);
5579 return Keys;
5580 }
5581 }
5582
5586 public IEnumerable<KeyValuePair<string, KeyValuePair<string, bool>[]>> MetaData => this.metaData;
5587
5591 public IEnumerable<KeyValuePair<string, Multimedia>> References => this.references;
5592
5596 public string[] Author => this.GetMetaData("Author");
5597
5601 public string[] Copyright => this.GetMetaData("Copyright");
5602
5606 public string[] Previous => this.Merge(this.GetMetaData("Previous"), this.GetMetaData("Prev"));
5607
5608 private string[] Merge(string[] L1, string[] L2)
5609 {
5610 int c1, c2;
5611
5612 if ((c1 = L1.Length) == 0)
5613 return L2;
5614 else if ((c2 = L2.Length) == 0)
5615 return L1;
5616
5617 string[] L = new string[c1 + c2];
5618
5619 Array.Copy(L1, 0, L, 0, c1);
5620 Array.Copy(L2, 0, L, c1, c2);
5621
5622 return L;
5623 }
5624
5628 public string[] Next => this.GetMetaData("Next");
5629
5633 public string[] CSS => this.GetMetaData("CSS");
5634
5638 public string[] JavaScript => this.GetMetaData("JAVASCRIPT");
5639
5643 public string[] Script => this.GetMetaData("SCRIPT");
5644
5650 public string[] InitializationScript => this.GetMetaData("INIT");
5651
5655 public string[] Parameters => this.GetMetaData("PARAMETER");
5656
5660 public string[] Date => this.GetMetaData("Date");
5661
5665 public string[] Description => this.GetMetaData("Description");
5666
5670 public string[] Image => this.GetMetaData("Image");
5671
5675 public string[] Keywords => this.GetMetaData("Keywords");
5676
5680 public string[] Subtitle => this.GetMetaData("Subtitle");
5681
5685 public string[] Title => this.GetMetaData("Title");
5686
5690 public string[] Web => this.GetMetaData("Web");
5691
5695 public string[] Refresh => this.GetMetaData("Refresh");
5696
5700 public string[] UserVariable => this.GetMetaData("UserVariable");
5701
5705 public string[] Login => this.GetMetaData("Login");
5706
5710 public string[] Privileges => this.GetMetaData("Privileges");
5711
5718 public bool TryGetFootnoteNumber(string Key, out int Number)
5719 {
5720 if (this.footnoteNumberByKey is null)
5721 {
5722 Number = 0;
5723 return false;
5724 }
5725 else
5726 return this.footnoteNumberByKey.TryGetValue(Key, out Number);
5727 }
5728
5735 public bool TryGetFootnote(string Key, out Footnote Footnote)
5736 {
5737 if (this.footnotes is null)
5738 {
5739 Footnote = null;
5740 return false;
5741 }
5742 else
5743 return this.footnotes.TryGetValue(Key, out Footnote);
5744 }
5745
5750 public string[] Footnotes
5751 {
5752 get
5753 {
5754 if (this.footnoteOrder is null)
5755 return new string[0];
5756 else
5757 return this.footnoteOrder.ToArray();
5758 }
5759 }
5760
5764 public IEmojiSource EmojiSource => this.emojiSource;
5765
5771 public static string Encode(string s)
5772 {
5773 return Functions.MarkdownEncode.EscapeText(s);
5774 }
5775
5779 public bool SyntaxHighlighting => this.syntaxHighlighting;
5780
5784 public string FileName
5785 {
5786 get => this.fileName;
5787 set => this.fileName = value;
5788 }
5789
5794 public string ResourceName
5795 {
5796 get => this.resourceName;
5797 set => this.resourceName = value;
5798 }
5799
5803 public string URL
5804 {
5805 get => this.url;
5806 set => this.url = value;
5807 }
5808
5813 {
5814 get => this.master;
5815 set => this.master = value;
5816 }
5817
5822 {
5823 get
5824 {
5825 if (!(this.detail is null))
5826 return this.detail;
5827
5828 if (this.master is null)
5829 return null;
5830
5831 MarkdownDocument Doc = this.master.Detail;
5832 if (Doc != this)
5833 return Doc;
5834
5835 return null;
5836 }
5837 set => this.detail = value;
5838 }
5839
5843 public MarkdownSettings Settings => this.settings;
5844
5848 public bool IncludesTableOfContents => this.includesTableOfContents;
5849
5853 public bool IsDynamic => this.isDynamic || (this.master?.isDynamic ?? false);
5854
5858 public object Tag
5859 {
5860 get => this.tag;
5861 set => this.tag = value;
5862 }
5863
5867 public bool AllowScriptTag
5868 {
5869 get
5870 {
5871 if (!this.allowScriptTag.HasValue)
5872 {
5873 this.allowScriptTag = this.metaData.TryGetValue("ALLOWSCRIPTTAG", out KeyValuePair<string, bool>[] Value) &&
5874 Value.Length > 0 &&
5875 CommonTypes.TryParse(Value[0].Key, out bool b) &&
5876 b;
5877 }
5878
5879 return this.allowScriptTag.Value;
5880 }
5881 }
5882
5889 public bool ForEach(MarkdownElementHandler Callback, object State)
5890 {
5891 if (!(this.elements is null))
5892 {
5893 foreach (MarkdownElement E in this.elements)
5894 {
5895 if (!E.ForEach(Callback, State))
5896 return false;
5897 }
5898 }
5899
5900 if (!(this.references is null))
5901 {
5902 foreach (MarkdownElement E in this.references.Values)
5903 {
5904 if (!E.ForEach(Callback, State))
5905 return false;
5906 }
5907 }
5908
5909 if (!(this.footnotes is null))
5910 {
5911 foreach (MarkdownElement E in this.footnotes.Values)
5912 {
5913 if (!E.ForEach(Callback, State))
5914 return false;
5915 }
5916 }
5917
5918 return true;
5919 }
5920
5925 public string[] FindLinks()
5926 {
5927 return this.FindLinks(true, true, true);
5928 }
5929
5937 public string[] FindLinks(bool IncludeAutomaticLinks, bool IncludeLinks, bool IncludeMultimedia)
5938 {
5939 Dictionary<string, bool> Links = new Dictionary<string, bool>();
5940
5941 this.ForEach((E, Obj) =>
5942 {
5944 {
5945 if (IncludeAutomaticLinks)
5946 Links[AutomaticLinkUrl.URL] = true;
5947 }
5948 else if (E is Link Link)
5949 {
5950 if (IncludeLinks)
5951 Links[Link.Url] = true;
5952 }
5953 else if (E is Multimedia Multimedia)
5954 {
5955 if (IncludeMultimedia)
5956 {
5957 foreach (MultimediaItem Item in Multimedia.Items)
5958 Links[Item.Url] = true;
5959 }
5960 }
5961
5962 return true;
5963 }, null);
5964
5965 string[] Result = new string[Links.Count];
5966 Links.Keys.CopyTo(Result, 0);
5967 return Result;
5968 }
5969
5974 public string[] FindHashTags()
5975 {
5976 SortedDictionary<string, bool> Tags = new SortedDictionary<string, bool>();
5977
5978 this.ForEach((E, Obj) =>
5979 {
5980 if (E is HashTag Tag)
5981 Tags[Tag.Tag] = true;
5982
5983 return true;
5984 }, null);
5985
5986 string[] Result = new string[Tags.Count];
5987 Tags.Keys.CopyTo(Result, 0);
5988 return Result;
5989 }
5990
5994 public IEnumerable<MarkdownElement> Elements => this.elements;
5995
5999 public IEnumerator<MarkdownElement> GetEnumerator()
6000 {
6001 return this.elements.GetEnumerator();
6002 }
6003
6004 IEnumerator IEnumerable.GetEnumerator()
6005 {
6006 return this.elements.GetEnumerator();
6007 }
6008
6009 private class ReversePosition : IComparer<int>
6010 {
6011 public int Compare(int x, int y)
6012 {
6013 return y - x;
6014 }
6015 }
6016
6024 public static Task<MarkdownDocument> Compare(MarkdownDocument Old, MarkdownDocument New, bool KeepUnchanged)
6025 {
6026 return New.Compare(Old, KeepUnchanged);
6027 }
6028
6039 public static async Task<string> Compare(string Old, string New, MarkdownSettings Settings, bool KeepUnchanged,
6040 params Type[] TransparentExceptionTypes)
6041 {
6044 MarkdownDocument DiffDoc = await Compare(OldDoc, NewDoc, KeepUnchanged);
6045
6046 return await DiffDoc.GenerateMarkdown(false);
6047 }
6048
6055 public async Task<MarkdownDocument> Compare(MarkdownDocument Previous, bool KeepUnchanged)
6056 {
6057 // TODO: Meta-data
6058
6059 MarkdownDocument Result = await CreateAsync(string.Empty, this.settings, this.transparentExceptionTypes);
6060 IEnumerable<MarkdownElement> Edit = Compare(Previous.elements, this.elements, KeepUnchanged, Result);
6061
6062 foreach (MarkdownElement E in Edit)
6063 Result.elements.AddLast(E);
6064
6065 // TODO: Footnotes
6066
6067 Result.markdownText = null; // Triggers export, if needed.
6068 return Result;
6069 }
6070
6071 private static IEnumerable<MarkdownElement> Atomize(IEnumerable<MarkdownElement> Elements, out bool Reassemble)
6072 {
6073 if (ContainsEditableText(Elements))
6074 {
6075 Reassemble = true;
6076 return Atomize(Elements);
6077 }
6078 else
6079 {
6080 Reassemble = false;
6081 return Elements;
6082 }
6083 }
6084
6085 private static IEnumerable<MarkdownElement> Atomize(IEnumerable<MarkdownElement> Elements)
6086 {
6087 LinkedList<MarkdownElement> Result = new LinkedList<MarkdownElement>();
6088
6089 foreach (MarkdownElement E in Elements)
6090 {
6091 if (E is IEditableText EditableText)
6092 {
6093 foreach (MarkdownElement E2 in EditableText.Atomize())
6094 Result.AddLast(E2);
6095 }
6096 else
6097 Result.AddLast(E);
6098 }
6099
6100 return Result;
6101 }
6102
6103 private static bool ContainsEditableText(IEnumerable<MarkdownElement> Elements)
6104 {
6105 foreach (MarkdownElement E in Elements)
6106 {
6107 if (E is IEditableText)
6108 return true;
6109 }
6110
6111 return false;
6112 }
6113
6114 private static MarkdownElement[] ToArray(IEnumerable<MarkdownElement> Elements)
6115 {
6116 if (Elements is MarkdownElement[] Array)
6117 return Array;
6118
6119 if (Elements is ICollection<MarkdownElement> Collection)
6120 {
6121 Array = new MarkdownElement[Collection.Count];
6122 Collection.CopyTo(Array, 0);
6123 return Array;
6124 }
6125
6126 int c = 0;
6127
6128 foreach (MarkdownElement E in Elements)
6129 c++;
6130
6131 Array = new MarkdownElement[c];
6132
6133 c = 0;
6134
6135 foreach (MarkdownElement E in Elements)
6136 Array[c++] = E;
6137
6138 return Array;
6139 }
6140
6141 private static IEnumerable<MarkdownElement> Compare(IEnumerable<MarkdownElement> Elements1,
6142 IEnumerable<MarkdownElement> Elements2, bool KeepUnchanged, MarkdownDocument Document)
6143 {
6144 LinkedList<MarkdownElement> Result = new LinkedList<MarkdownElement>();
6145 MarkdownElement[] S1 = ToArray(Atomize(Elements1, out bool Reassemble1));
6146 MarkdownElement[] S2 = ToArray(Atomize(Elements2, out bool Reassemble2));
6149 int i, c = Script.Steps.Length;
6150
6151 if (Reassemble1 || Reassemble2)
6152 {
6153 List<MarkdownElement> Reassembled = new List<MarkdownElement>();
6154 StringBuilder sb = new StringBuilder();
6155
6156 for (i = 0; i < c; i++)
6157 {
6158 Step = Script.Steps[i];
6159
6160 switch (Step.Operation)
6161 {
6162 case EditOperation.Keep:
6163 case EditOperation.Delete:
6164 if (!Reassemble1)
6165 continue;
6166 break;
6167
6168 case EditOperation.Insert:
6169 if (!Reassemble2)
6170 continue;
6171 break;
6172
6173 default:
6174 continue;
6175 }
6176
6177 Type LastAtomType = null;
6178 Atom LastAtom = null;
6179 Type AtomType;
6180
6181 foreach (MarkdownElement E in Step.Symbols)
6182 {
6183 if (E is Atom Atom)
6184 {
6185 AtomType = Atom.GetType();
6186 if (AtomType != LastAtomType)
6187 {
6188 if (!(LastAtom is null))
6189 {
6190 Reassembled.Add(LastAtom.Source.Assemble(Document, sb.ToString()));
6191 sb.Clear();
6192 }
6193
6194 LastAtom = Atom;
6195 LastAtomType = AtomType;
6196 }
6197
6198 sb.Append(Atom.Charater);
6199 }
6200 else
6201 {
6202 if (!(LastAtom is null))
6203 {
6204 Reassembled.Add(LastAtom.Source.Assemble(Document, sb.ToString()));
6205 sb.Clear();
6206 LastAtom = null;
6207 LastAtomType = null;
6208 }
6209
6210 Reassembled.Add(E);
6211 }
6212 }
6213
6214 if (!(LastAtom is null))
6215 {
6216 Reassembled.Add(LastAtom.Source.Assemble(Document, sb.ToString()));
6217 sb.Clear();
6218 }
6219
6220 Step.Symbols = Reassembled.ToArray();
6221 Reassembled.Clear();
6222 }
6223 }
6224
6225 for (i = 0; i < c; i++)
6226 {
6227 Step = Script.Steps[i];
6228
6229 if (Step.Operation == EditOperation.Keep)
6230 {
6231 if (!KeepUnchanged)
6232 continue;
6233
6234 foreach (MarkdownElement E in Step.Symbols)
6235 Result.AddLast(E);
6236 }
6237 else
6238 {
6239 if (i + 1 < c &&
6240 (Step2 = Script.Steps[i + 1]).Operation != EditOperation.Keep &&
6241 Step2.Operation != Step.Operation &&
6242 SameBlockTypes(Step.Symbols, Step2.Symbols))
6243 {
6244 MarkdownElement E1, E2;
6245 int j, d = Step.Symbols.Length;
6246
6247 for (j = 0; j < d; j++)
6248 {
6249 if (Step.Operation == EditOperation.Insert)
6250 {
6251 E2 = Step.Symbols[j];
6252 E1 = Step2.Symbols[j];
6253 }
6254 else
6255 {
6256 E1 = Step.Symbols[j];
6257 E2 = Step2.Symbols[j];
6258 }
6259
6260 if (E1 is MarkdownElementChildren Children1 &&
6261 E2 is MarkdownElementChildren Children2)
6262 {
6263 IEnumerable<MarkdownElement> Diff = Compare(Children1.Children, Children2.Children,
6264 KeepUnchanged || d > 1, Document);
6265
6266 Result.AddLast(Children1.Create(Diff, Document));
6267 }
6268 else if (E1 is MarkdownElementSingleChild Child1 &&
6269 E2 is MarkdownElementSingleChild Child2 &&
6270 Child1.Child.SameMetaData(Child2.Child) &&
6271 Child1.Child is MarkdownElementChildren GrandChildren1 &&
6272 Child2.Child is MarkdownElementChildren GrandChildren2)
6273 {
6274 IEnumerable<MarkdownElement> Diff = Compare(GrandChildren1.Children, GrandChildren2.Children,
6275 KeepUnchanged || d > 1, Document);
6276
6277 Result.AddLast(Child1.Create(GrandChildren1.Create(Diff, Document), Document));
6278 }
6279 else
6280 {
6281 Result.AddLast(GetElement(Step.Operation, Document, E1));
6282 Result.AddLast(GetElement(Step2.Operation, Document, E2));
6283 }
6284 }
6285
6286 i++;
6287 }
6288 else
6289 Result.AddLast(GetElement(Step.Operation, Document, Step.Symbols));
6290 }
6291 }
6292
6293 return Result;
6294 }
6295
6296 private static MarkdownElement GetElement(EditOperation Operation, MarkdownDocument Document, params MarkdownElement[] Symbols)
6297 {
6298 if (Symbols[0].IsBlockElement)
6299 {
6300 switch (Operation)
6301 {
6302 case EditOperation.Insert:
6303 return new InsertBlocks(Document, Symbols);
6304
6305 case EditOperation.Delete:
6306 return new DeleteBlocks(Document, Symbols);
6307 }
6308 }
6309 else
6310 {
6311 switch (Operation)
6312 {
6313 case EditOperation.Insert:
6314 return new Insert(Document, Symbols);
6315
6316 case EditOperation.Delete:
6317 return new Delete(Document, Symbols);
6318 }
6319 }
6320
6321 return new InvisibleBreak(Document, string.Empty);
6322 }
6323
6324 private static bool SameBlockTypes(MarkdownElement[] E1, MarkdownElement[] E2)
6325 {
6326 int i, c = E1.Length;
6327 if (E2.Length != c)
6328 return false;
6329
6331
6332 for (i = 0; i < c; i++)
6333 {
6334 if (!(e = E1[i]).IsBlockElement)
6335 return false;
6336
6337 if (!e.SameMetaData(E2[i]))
6338 return false;
6339 }
6340
6341 return true;
6342 }
6343
6350 public void QueueAsyncTask(AsyncMarkdownProcessing Callback, object State)
6351 {
6352 lock (this.asyncTasks)
6353 {
6354 this.asyncTasks.Add(new KeyValuePair<AsyncMarkdownProcessing, object>(Callback, State));
6355 }
6356 }
6357
6361 public IEnumerable<KeyValuePair<AsyncMarkdownProcessing, object>> AsyncTasks
6362 {
6363 get
6364 {
6365 lock (this.asyncTasks)
6366 {
6367 return this.asyncTasks.ToArray();
6368 }
6369 }
6370 }
6371
6375 public void ProcessAsyncTasks()
6376 {
6377 KeyValuePair<AsyncMarkdownProcessing, object>[] Tasks;
6378
6379 lock (this.asyncTasks)
6380 {
6381 if (this.asyncTasks.Count == 0)
6382 return;
6383
6384 Tasks = this.asyncTasks.ToArray();
6385 this.asyncTasks.Clear();
6386 }
6387
6388 Task.Run(async () =>
6389 {
6390 foreach (KeyValuePair<AsyncMarkdownProcessing, object> P in Tasks)
6391 {
6392 try
6393 {
6394 await P.Key(P.Value);
6395 }
6396 catch (Exception ex)
6397 {
6398 Log.Exception(ex);
6399 }
6400 }
6401 });
6402 }
6403
6410 public static async Task<object> TransformXml(XmlDocument Xml, Variables Variables)
6411 {
6412 try
6413 {
6414 IXmlVisualizer Visualizer = CodeBlock.GetXmlVisualizerHandler(Xml);
6415 if (Visualizer is null)
6416 return Xml;
6417
6418 return (await Visualizer.TransformXml(Xml, Variables)) ?? Xml;
6419 }
6420 catch (Exception ex)
6421 {
6422 return ex;
6423 }
6424 }
6425
6431 {
6433
6434 this.ForEach((Element, _) =>
6435 {
6436 Result.NrElements++;
6437 Element.IncrementStatistics(Result);
6438 return true;
6439 }, null);
6440
6441 Result.MailHyperlinks = Result.IntMailHyperlinks?.ToArray();
6442 Result.UrlHyperlinks = Result.IntUrlHyperlinks?.ToArray();
6443
6444 this.GenerateStatDictionary(Result.IntMultimediaPerContentCategory,
6445 out Dictionary<string, string[]> AsArrays, out Dictionary<string, int> AsCounts);
6446
6447 Result.MultimediaPerContentCategory = AsArrays;
6448 Result.NrMultimediaPerContentCategory = AsCounts;
6449
6450 this.GenerateStatDictionary(Result.IntMultimediaPerContentType, out AsArrays, out AsCounts);
6451 Result.MultimediaPerContentType = AsArrays;
6452 Result.NrMultimediaPerContentType = AsCounts;
6453
6454 this.GenerateStatDictionary(Result.IntMultimediaPerExtension, out AsArrays, out AsCounts);
6455 Result.MultimediaPerExtension = AsArrays;
6456 Result.NrMultimediaPerExtension = AsCounts;
6457
6458 return Result;
6459 }
6460
6461 private void GenerateStatDictionary(Dictionary<string, List<string>> Temp, out Dictionary<string, string[]> AsArrays, out Dictionary<string, int> AsCounts)
6462 {
6463 if (Temp is null)
6464 {
6465 AsArrays = null;
6466 AsCounts = null;
6467 return;
6468 }
6469
6470 AsArrays = new Dictionary<string, string[]>();
6471 AsCounts = new Dictionary<string, int>();
6472
6473 foreach (KeyValuePair<string, List<string>> P in Temp)
6474 {
6475 AsArrays[P.Key] = P.Value.ToArray();
6476 AsCounts[P.Key] = P.Value.Count;
6477 }
6478 }
6479
6485 public static string AppendRows(string[] Rows)
6486 {
6487 return AppendRows(Rows, false);
6488 }
6489
6496 public static string AppendRows(string[] Rows, bool SingleRow)
6497 {
6498 if (SingleRow && Rows.Length == 1)
6499 return Rows[0].Trim();
6500
6501 StringBuilder sb = new StringBuilder();
6502
6503 foreach (string Row in Rows)
6504 {
6505 if (SingleRow)
6506 sb.Append(Row.Trim());
6507 else
6508 sb.AppendLine(Row);
6509 }
6510
6511 return sb.ToString();
6512 }
6513
6517 public Grade CanEncodeJson => Grade.NotAtAll; // Document reference from child nodes create a stack overflow.
6518
6519 // TODO: Footnotes in included markdown files.
6520 }
6521}
6522
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static readonly char[] CRLF
Contains the CR LF character sequence.
Definition: CommonTypes.cs:17
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:46
Contains information about an emoji.
Static class that provide methods for managing emojis.
static readonly EmojiInfo Emoji_sweat
:sweat: 😓 face with cold sweat
static readonly EmojiInfo Emoji_joy
:joy: 😂 face with tears of joy
static readonly EmojiInfo Emoji_no_mouth
:no_mouth: 😶 face without mouth
static readonly EmojiInfo Emoji_broken_heart
:broken_heart: 💔 broken heart
static readonly EmojiInfo Emoji_kissing_heart
:kissing_heart: 😘 face throwing a kiss
static readonly EmojiInfo Emoji_wink
:wink: 😉 winking face
static readonly EmojiInfo Emoji_stuck_out_tongue_winking_eye
:stuck_out_tongue_winking_eye: 😜 face with stuck-out tongue and winking eye
static readonly EmojiInfo Emoji_heart
:heart: ❤ heavy black heart
static readonly EmojiInfo Emoji_cry
:cry: 😢 crying face
static bool TryGetEmoji(string ShortName, out EmojiInfo Emoji)
Tries to get information about an emoji, given its short name.
static readonly EmojiInfo Emoji_angry
:angry: 😠 angry face
static readonly EmojiInfo Emoji_expressionless
:expressionless: 😑 expressionless face
static readonly EmojiInfo Emoji_disappointed
:disappointed: 😞 disappointed face
static readonly EmojiInfo Emoji_ok_woman
:ok_woman: 🙆 face with ok gesture
static readonly EmojiInfo Emoji_sweat_smile
:sweat_smile: 😅 smiling face with open mouth and cold sweat
static readonly EmojiInfo Emoji_stuck_out_tongue
:stuck_out_tongue: 😛 face with stuck-out tongue
static readonly EmojiInfo Emoji_smile
:smile: 😄 smiling face with open mouth and smiling eyes
static readonly EmojiInfo Emoji_innocent
:innocent: 😇 smiling face with halo
static readonly EmojiInfo Emoji_persevere
:persevere: 😣 persevering face
static readonly EmojiInfo Emoji__1
:-1: 👎 thumbs down sign
static readonly EmojiInfo Emoji_flushed
:flushed: 😳 flushed face
static readonly EmojiInfo Emoji_open_mouth
:open_mouth: 😮 face with open mouth
static readonly EmojiInfo Emoji_smiley
:smiley: 😃 smiling face with open mouth
static readonly EmojiInfo Emoji_dizzy_face
:dizzy_face: 😵 dizzy face
static readonly EmojiInfo Emoji_sunglasses
:sunglasses: 😎 smiling face with sunglasses
static readonly EmojiInfo Emoji_laughing
:laughing: 😆 smiling face with open mouth and tightly-closed eyes
static readonly EmojiInfo Emoji_confused
:confused: 😕 confused face
Executes script from a file, if not executed before, or if file timestamp has changed....
static async Task< bool > NeedsExecution(string FileName)
Checks if an init-file needs to be executed.
Class that can be used to encapsulate Markdown to be returned from a Web Service, bypassing any encod...
Contains a markdown document. This markdown document class supports original markdown,...
string[] Script
Links to server-side script files that should be included before processing the page.
static async Task< object > TransformXml(XmlDocument Xml, Variables Variables)
Transforms XML to an object that is easier to visualize.
static string AppendRows(string[] Rows)
Appends a set of rows into a single string with newlines between rows.
string[] InitializationScript
Links to server-side script files that should be executed before before processing the page....
MarkdownSettings Settings
Markdown settings.
IEnumerable< KeyValuePair< AsyncMarkdownProcessing, object > > AsyncTasks
Enumerable set of asynchronous tasks that have been registered.
async Task< MarkdownDocument > Compare(MarkdownDocument Previous, bool KeepUnchanged)
Calculates the difference of the current Markdown document, and a previous version of the Markdown do...
bool IncludesTableOfContents
If the document contains a Table of Contents.
Task GenerateHTML(StringBuilder Output)
Generates HTML from the markdown text.
string CheckURL(string Url, string URL)
Checks the URL if it needs redirection to a proxy.
string[] FindLinks(bool IncludeAutomaticLinks, bool IncludeLinks, bool IncludeMultimedia)
Finds all links in the document.
bool ForEach(MarkdownElementHandler Callback, object State)
Loops through all elements in the document.
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
bool TryGetFootnoteNumber(string Key, out int Number)
Tries to get the number of a footnote, given its key.
string[] Author
Author(s) of document.
string MarkdownText
Markdown text. This text might differ slightly from the original text passed to the document.
async Task RenderDocument(IRenderer Output)
Renders the document using provided output format.
Task< string > GenerateMarkdown()
Generates Markdown from the markdown text.
bool TryGetFootnote(string Key, out Footnote Footnote)
Tries to get a footnote, given its key.
string[] Next
Link to next document, in a paginated set of documents.
IEnumerator< MarkdownElement > GetEnumerator()
Gets an enumerator of root markdown elements in the document.
string[] Subtitle
Subtitle of document.
async Task GenerateHTML(StringBuilder Output, HtmlSettings HtmlSettings)
Generates HTML from the markdown text.
static Task< MarkdownDocument > Compare(MarkdownDocument Old, MarkdownDocument New, bool KeepUnchanged)
Calculates the difference of two Markdown documents.
Grade CanEncodeJson
To what extent the object supports JSON encoding.
bool SyntaxHighlighting
If syntax highlighting is used in the document.
IEnumerable< KeyValuePair< string, KeyValuePair< string, bool >[]> > MetaData
Meta-data
string[] FindHashTags()
Finds hashtags in the document.
string[] Image
Link to image for page.
bool AllowScriptTag
If client-side script tags are allowed in the document.
void QueueAsyncTask(AsyncMarkdownProcessing Callback, object State)
Queues an asynchronous task to be executed. Asynchronous tasks will be executed after the main docume...
bool TryGetMetaData(string Key, out KeyValuePair< string, bool >[] Value)
Tries to get a meta-data value given its key.
IEnumerable< MarkdownElement > Elements
Markdown elements making up the document.
string[] Privileges
Requered user privileges to display page.
void AddMetaData(string Key, string Value)
Adds meta-data to the document.
async Task GenerateMarkdown(StringBuilder Output, bool PortableSyntax)
Generates Markdown from the markdown text.
MarkdownDocument Master
Master document responsible for the current document.
MarkdownStatistics GetStatistics()
Returns some basic statistics about the contents of the Markdown object.
string[] Copyright
Link to copyright statement.
IEnumerable< string > FootnoteOrder
Order of footnotes.
bool IsDynamic
If the contents of the document is dynamic (i.e. includes script), or not (i.e. is static).
string[] Parameters
Name of a query parameter recognized by the page.
Task GenerateMarkdown(StringBuilder Output)
Generates Markdown from the markdown text.
string[] Previous
Link to previous document, in a paginated set of documents.
Type[] TransparentExceptionTypes
If an exception is thrown when processing script in markdown, and the exception is of any of these ty...
IEmojiSource EmojiSource
Source for emojis in the document.
string[] FindLinks()
Finds all links in the document.
static ? int HeaderEndPosition(string Markdown)
Gets the end position of the header, if one is found, null otherwise.
void ProcessAsyncTasks()
Processes any registered asynchronous tasks. This method is normally only called from renderers of do...
string[] GetMetaData(string Key)
Gets the meta-data values given a meta-data key. If meta-data is not found, an empty array is returne...
const string MarkdownSettingsVariableName
Variable name used for storing Markdown settings.
async Task GeneratePlainText(StringBuilder Output)
Generates Plain Text from the markdown text.
string[] Refresh
Tells the browser to refresh the page after a given number of seconds.
string[] MetaDataKeys
Meta-data keys availale in document.
static async Task< string > Compare(string Old, string New, MarkdownSettings Settings, bool KeepUnchanged, params Type[] TransparentExceptionTypes)
Calculates the difference of two Markdown documents.
string[] Footnotes
Gets the keys of the footnotes in the order that they are referenced in the document....
Header[] Headers
Headers in document.
string[] CSS
Link(s) to Cascading Style Sheet(s) that should be used for visual formatting of the generated HTML p...
static Task< MarkdownDocument > CreateAsync(string MarkdownText, MarkdownSettings Settings, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
string[] JavaScript
Link(s) to JavaScript files(s) that should be includedin the generated HTML page.
string[] UserVariable
Name of the variable that will hold a reference to the IUser interface for the currently logged in us...
string ResourceName
Local resource name of Markdown document, if referenced through a web server. Master documents use th...
object Tag
Property can be used to tag document with client-specific information.
string FileName
Filename of Markdown document. Markdown inclusion will be made relative to this filename.
string[] Description
Description of document.
string URL
Absolute URL of Markdown document, if referenced through a web server.
async Task< string > GenerateMarkdown(bool PortableSyntax)
Generates Markdown from the markdown text.
static Task< KeyValuePair< string, bool > > Preprocess(string Markdown, MarkdownSettings Settings, string FileName, params Type[] TransparentExceptionTypes)
Preprocesses markdown text.
MarkdownDocument Detail
Detail document of a master document.
static string AppendRows(string[] Rows, bool SingleRow)
Appends a set of rows into a single string with newlines between rows.
IEnumerable< KeyValuePair< string, Multimedia > > References
Multimedia references
async Task< string > GenerateHTML(HtmlSettings HtmlSettings)
Generates HTML from the markdown text.
static async Task< string > Preprocess(string Markdown, MarkdownSettings Settings, params Type[] TransparentExceptionTypes)
Preprocesses markdown text.
static async Task< KeyValuePair< string, bool > > Preprocess(string Markdown, MarkdownSettings Settings, string FileName, bool FromScript, params Type[] TransparentExceptionTypes)
Preprocesses markdown text.
static async Task< MarkdownDocument > CreateAsync(string MarkdownText, MarkdownSettings Settings, string FileName, string ResourceName, string URL, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
Multimedia GetReference(string Label)
Gets the multimedia information referenced by a label.
string[] Date
(Publication) date of document.
async Task< string > GeneratePlainText()
Generates Plain Text from the markdown text.
async Task< string > GenerateHTML()
Generates HTML from the markdown text.
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
string[] Login
Link to a login page. This page will be shown if the user variable does not contain a user.
Contains settings that the Markdown parser uses to customize its behavior.
string GetFileName(string DocumentFileName, string FileNameReference)
Evaluates a file name from a file reference.
Variables Variables
Collection of variables. Providing such a collection enables script execution inside markdown documen...
IEmojiSource EmojiSource
Optional Emoji source. Emojis and smileys are only available if an emoji source is provided.
AuthorizeExpression AuthorizeExpression
Optional method to call to authorize execution of script expressions.
bool ParseMetaData
If meta-data should be parsed or not.
Contains some basic statistical information about a Markdown document.
int NrElements
Number of elements in Markdown document (total).
Represents an atom of editable text (i.e. typed character).
Definition: Atom.cs:11
IEditableText Source
Source
Definition: Atom.cs:33
Represents a block quote in a markdown document.
Definition: BlockQuote.cs:11
Represents a bullet list in a markdown document.
Definition: BulletList.cs:11
Represents a center-aligned set of blocks in a markdown document.
Represents a code block in a markdown document.
Definition: CodeBlock.cs:16
Represents a comment block in a markdown document.
Definition: CommentBlock.cs:10
Represents a definition list in a markdown document.
override void AddChildren(IEnumerable< MarkdownElement > NewChildren)
Adds children to the element.
Represents inserted blocks in a markdown document.
Definition: DeleteBlocks.cs:11
bool Referenced
If the Footnote has been referenced during rendering, and therefore needs to be shown at the end of t...
Definition: Footnote.cs:51
Represents a header in a markdown document.
Definition: Header.cs:15
Represents a block of HTML in a markdown document.
Definition: HtmlBlock.cs:11
Represents inserted blocks in a markdown document.
Definition: InsertBlocks.cs:11
Represents a left-aligned set of blocks in a markdown document.
Definition: LeftAligned.cs:11
Represents a margin-aligned set of blocks in a markdown document.
Represents a nested block with no special formatting rules in a markdown document.
Definition: NestedBlock.cs:11
Represents a numbered item in an ordered list.
Definition: NumberedItem.cs:10
bool NumberExplicit
If number is explicitly provided (true) or inferred (false).
Definition: NumberedItem.cs:40
Represents a numbered list in a markdown document.
Definition: NumberedList.cs:11
override void AddChildren(IEnumerable< MarkdownElement > NewChildren)
Adds children to the element.
Definition: NumberedList.cs:70
Represents a paragraph in a markdown document.
Definition: Paragraph.cs:11
Represents a right-aligned set of blocks in a markdown document.
Definition: RightAligned.cs:11
Represents a sequence of sections.
Definition: Sections.cs:11
Represents a table in a markdown document.
Definition: Table.cs:11
Represents a task item in a task list.
Definition: TaskItem.cs:10
Represents a task list in a markdown document.
Definition: TaskList.cs:11
Represents an unnumbered item in an ordered list.
Abstract base class for all markdown elements with a variable number of child elements.
override IEnumerable< MarkdownElement > Children
Any children of the element.
void AddChildren(params MarkdownElement[] NewChildren)
Adds children to the element.
bool HasOneChild
If the element has only one child.
MarkdownElement FirstChild
First child, or null if none.
Abstract base class for all markdown elements.
virtual bool ForEach(MarkdownElementHandler Callback, object State)
Loops through all child-elements for the element.
virtual bool SameMetaData(MarkdownElement E)
If the current object has same meta-data as E (but not necessarily same content).
virtual bool IsBlockElement
If the element is a block element.
abstract Task Render(IRenderer Output)
Renders the element.
virtual IEnumerable< MarkdownElement > Children
Any children of the element.
Abstract base class for all markdown elements with one child element.
Represents an HTML entity in Unicode format.
MultimediaItem[] Items
Multimedia items.
Definition: Multimedia.cs:40
Renders HTML from a Markdown document.
Definition: HtmlRenderer.cs:24
Contains settings that the HTML export uses to customize HTML output.
Definition: HtmlSettings.cs:7
Renders portable Markdown from a Markdown document.
Abstract base class for Markdown renderers.
Definition: Renderer.cs:14
override string ToString()
Returns the renderer output.
Definition: Renderer.cs:130
Renders plain text from a Markdown document.
Definition: TextRenderer.cs:16
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:15
static async Task< string > ReadAllTextAsync(string FileName)
Reads a text file asynchronously.
Definition: Resources.cs:205
Helps with common XML-related tasks.
Definition: XML.cs:19
static string HtmlValueEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe or the quote.
Definition: XML.cs:209
Generic exception, with meta-data for logging.
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:818
Computes the difference between two sequences of symbols.
Definition: Difference.cs:12
Represents an Edit-script, converting one sequence of symbols to another.
Definition: EditScript.cs:11
Represents a sub-sequence of symbols.
Definition: Step.cs:12
EditOperation Operation
Edit operation being performed.
Definition: Step.cs:55
T[] Symbols
Sequence of symbols.
Definition: Step.cs:37
Base class for all types of elements.
Definition: Element.cs:13
Class managing a script expression.
Definition: Expression.cs:39
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4275
bool ContainsImplicitPrint
If the expression contains implicit print operations.
Definition: Expression.cs:4319
bool ReferencesImplicitPrint(Variables Variables)
If the expression, or any function call references, contain implicit print operations.
Definition: Expression.cs:4326
Base class for graphs.
Definition: Graph.cs:79
Contains pixel information
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
string SubExpression
Sub-expression defining the node.
Definition: ScriptNode.cs:183
Collection of variables.
Definition: Variables.cs:25
TextWriter ConsoleOut
Console out interface. Can be used by functions and script to output data to the console.
Definition: Variables.cs:219
virtual Variable Add(string Name, object Value)
Adds a variable to the collection.
Definition: Variables.cs:122
Task LockAsync()
Locks the collection. The collection is by default thread safe. But if longer transactions require un...
Definition: Variables.cs:265
ValuePrinter Printer
Delegate that converts values to strings for (implicit) printing. Default is null,...
Definition: Variables.cs:238
void Release()
Releases the collection, previously locked through a call to Lock().
Definition: Variables.cs:308
Interface for Emoji sources. Emoji sources provide emojis to content providers.
Definition: IEmojiSource.cs:12
Basic interface for resources having a FileName property.
Provides a JSON Encoding hint for an object that implements this interface.
Interface for elements containing editable text.
MarkdownElement Assemble(MarkdownDocument Document, string Text)
Assembles a markdown element from a sequence of atoms.
Interface for all markdown handlers of multimedia content.
bool EmbedInlineLink(string Url)
If the link provided should be embedded in a multi-media construct automatically.
Interface for all XML visalizers.
Task< object > TransformXml(XmlDocument Xml, Variables Variables)
Transforms the XML document before visualizing it.
Interface for multimedia content HTML renderers.
Interface for Markdown renderers.
Definition: IRenderer.cs:12
Task RenderDocument(MarkdownDocument Document, bool Inclusion)
Renders a document.
Basic interface for matrices.
Definition: IMatrix.cs:9
Interface for objects that can be converted into matrices.
Definition: IToMatrix.cs:9
TextAlignment
Text alignment of contents.
delegate bool MarkdownElementHandler(MarkdownElement Element, object State)
Delegate for markdown element callback methods.
delegate Task AsyncMarkdownProcessing(object State)
Delegate used for callback methods performing asynchronous Markdown processing
EventType
Type of event.
Definition: EventType.cs:7
Grade
Grade enumeration
Definition: Grade.cs:7
EditOperation
Type of edit-operation
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html
Definition: Prefixes.cs:11
delegate Task< string > ValuePrinter(object Value, Variables Variables)
Converts a value to a printable string.