Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
HtmlDocument.cs
1using System;
3using System.Globalization;
4using System.Text;
5using System.Xml;
8
9namespace Waher.Content.Html
10{
14 public class HtmlDocument
15 {
16 private readonly string htmlText;
17 private HtmlElement root = null;
18 private Elements.Html html = null;
19 private Title title = null;
20 private Body body = null;
21 private Head head = null;
22 private ChunkedList<Main> main = null;
23 private ChunkedList<Header> header = null;
24 private ChunkedList<Footer> footer = null;
25 private ChunkedList<Details> details = null;
26 private ChunkedList<Summary> summary = null;
27 private ChunkedList<Article> article = null;
28 private ChunkedList<DtdInstruction> dtd = null;
29 private ChunkedList<ProcessingInstruction> processingInstructions = null;
30 private ChunkedList<Link> link = null;
31 private ChunkedList<Meta> meta = null;
32 private ChunkedList<Style> style = null;
33 private ChunkedList<Address> address = null;
34 private ChunkedList<Aside> aside = null;
35 private ChunkedList<Nav> nav = null;
36 private ChunkedList<Section> section = null;
37 private ChunkedList<Dialog> dialog = null;
38 private ChunkedList<Figure> figure = null;
39 private ChunkedList<Elements.Audio> audio = null;
40 private ChunkedList<Elements.Video> video = null;
41 private ChunkedList<Img> img = null;
42 private ChunkedList<Picture> picture = null;
43 private ChunkedList<Cite> cite = null;
44 private ChunkedList<Data> data = null;
45 private ChunkedList<Time> time = null;
46 private ChunkedList<Elements.Script> script = null;
47 private ChunkedList<Form> form = null;
48
53 public HtmlDocument(string Html)
54 {
55 this.htmlText = Html;
56 }
57
61 public string HtmlText => this.htmlText;
62
63 private void AssertParsed()
64 {
65 if (this.root is null)
66 this.Parse();
67 }
68
73 {
74 get
75 {
76 this.AssertParsed();
77 return this.root;
78 }
79 }
80
85 {
86 get
87 {
88 this.AssertParsed();
89 return this.html;
90 }
91 }
92
96 public Title Title
97 {
98 get
99 {
100 this.AssertParsed();
101 return this.title;
102 }
103 }
104
108 public Head Head
109 {
110 get
111 {
112 this.AssertParsed();
113 return this.head;
114 }
115 }
116
120 public Body Body
121 {
122 get
123 {
124 this.AssertParsed();
125 return this.body;
126 }
127 }
128
132 public IEnumerable<Main> Main
133 {
134 get
135 {
136 this.AssertParsed();
137 return this.main;
138 }
139 }
140
144 public IEnumerable<Header> Header
145 {
146 get
147 {
148 this.AssertParsed();
149 return this.header;
150 }
151 }
152
156 public IEnumerable<Footer> Footer
157 {
158 get
159 {
160 this.AssertParsed();
161 return this.footer;
162 }
163 }
164
168 public IEnumerable<Details> Details
169 {
170 get
171 {
172 this.AssertParsed();
173 return this.details;
174 }
175 }
176
180 public IEnumerable<Summary> Summary
181 {
182 get
183 {
184 this.AssertParsed();
185 return this.summary;
186 }
187 }
188
192 public IEnumerable<Article> Article
193 {
194 get
195 {
196 this.AssertParsed();
197 return this.article;
198 }
199 }
200
204 public IEnumerable<Link> Link
205 {
206 get
207 {
208 this.AssertParsed();
209 return this.link;
210 }
211 }
212
216 public IEnumerable<Meta> Meta
217 {
218 get
219 {
220 this.AssertParsed();
221 return this.meta;
222 }
223 }
224
228 public IEnumerable<Style> Style
229 {
230 get
231 {
232 this.AssertParsed();
233 return this.style;
234 }
235 }
236
240 public IEnumerable<Address> Address
241 {
242 get
243 {
244 this.AssertParsed();
245 return this.address;
246 }
247 }
248
252 public IEnumerable<Aside> Aside
253 {
254 get
255 {
256 this.AssertParsed();
257 return this.aside;
258 }
259 }
260
264 public IEnumerable<Nav> Nav
265 {
266 get
267 {
268 this.AssertParsed();
269 return this.nav;
270 }
271 }
272
276 public IEnumerable<Section> Section
277 {
278 get
279 {
280 this.AssertParsed();
281 return this.section;
282 }
283 }
284
288 public IEnumerable<Dialog> Dialog
289 {
290 get
291 {
292 this.AssertParsed();
293 return this.dialog;
294 }
295 }
296
300 public IEnumerable<Figure> Figure
301 {
302 get
303 {
304 this.AssertParsed();
305 return this.figure;
306 }
307 }
308
312 public IEnumerable<Elements.Audio> Audio
313 {
314 get
315 {
316 this.AssertParsed();
317 return this.audio;
318 }
319 }
320
324 public IEnumerable<Elements.Video> Video
325 {
326 get
327 {
328 this.AssertParsed();
329 return this.video;
330 }
331 }
332
336 public IEnumerable<Img> Img
337 {
338 get
339 {
340 this.AssertParsed();
341 return this.img;
342 }
343 }
344
348 public IEnumerable<Picture> Picture
349 {
350 get
351 {
352 this.AssertParsed();
353 return this.picture;
354 }
355 }
356
360 public IEnumerable<Cite> Cite
361 {
362 get
363 {
364 this.AssertParsed();
365 return this.cite;
366 }
367 }
368
372 public IEnumerable<Data> Data
373 {
374 get
375 {
376 this.AssertParsed();
377 return this.data;
378 }
379 }
380
384 public IEnumerable<Time> Time
385 {
386 get
387 {
388 this.AssertParsed();
389 return this.time;
390 }
391 }
392
396 public IEnumerable<Elements.Script> Script
397 {
398 get
399 {
400 this.AssertParsed();
401 return this.script;
402 }
403 }
404
408 public IEnumerable<Form> Form
409 {
410 get
411 {
412 this.AssertParsed();
413 return this.form;
414 }
415 }
416
420 public IEnumerable<DtdInstruction> Dtd
421 {
422 get
423 {
424 this.AssertParsed();
425 return this.dtd;
426 }
427 }
428
432 public IEnumerable<ProcessingInstruction> ProcessingInstructions
433 {
434 get
435 {
436 this.AssertParsed();
437 return this.processingInstructions;
438 }
439 }
440
441 private void Parse()
442 {
443 StringBuilder sb = new StringBuilder();
444 HtmlElement CurrentElement = null;
445 HtmlElement EmptyElement;
446 HtmlAttribute CurrentAttribute = null;
447 string Name = string.Empty;
448 string s;
449 int State = 0;
450 char EndChar = '\x00';
451 char ch;
452 int Pos;
453 int StartOfElement = 0;
454 int StartOfText = 0;
455 int StartOfAttribute = 0;
456 int Len = this.htmlText.Length;
457 bool Empty = true;
458 bool CurrentElementIsScript = false;
459
460 for (Pos = 0; Pos < Len; Pos++)
461 {
462 ch = this.htmlText[Pos];
463
464 switch (State)
465 {
466 case 0: // Waiting for <
467 if (ch == '<')
468 {
469 if (!Empty)
470 {
471 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
472 CurrentElement = CurrentElement.Parent as HtmlElement;
473
474 CurrentElement?.Add(new HtmlText(this, CurrentElement, StartOfText, Pos - 1, sb.ToString()));
475
476 sb.Clear();
477 Empty = true;
478 }
479
480 StartOfElement = Pos;
481 State++;
482 }
483 else if (ch == '&')
484 {
485 if (!Empty)
486 {
487 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
488 CurrentElement = CurrentElement.Parent as HtmlElement;
489
490 CurrentElement?.Add(new HtmlText(this, CurrentElement, StartOfText, Pos - 1, sb.ToString()));
491
492 sb.Clear();
493 Empty = true;
494 }
495
496 State = 10;
497 }
498 else
499 {
500 sb.Append(ch);
501 Empty = false;
502 }
503 break;
504
505 case 1: // Waiting for ?, !, /, attributes or >
506 if (ch == '/')
507 {
508 if (Empty) // Closing tag
509 State = 4;
510 else if (CurrentElementIsScript)
511 {
512 sb.Insert(0, '<');
513 sb.Append('/');
514 StartOfText = Pos + 1 - sb.Length;
515 State = 0;
516 }
517 else
518 State = 3;
519 }
520 else if (ch == '!' && Empty)
521 State++;
522 else if (CurrentElementIsScript)
523 {
524 sb.Insert(0, '<');
525 sb.Append(ch);
526 StartOfText = Pos + 1 - sb.Length;
527 State = 0;
528 }
529 else if (ch == '>')
530 {
531 if (Empty)
532 {
533 sb.Append("<>");
534 Empty = false;
535 StartOfText = Pos + 1 - sb.Length;
536 State = 0;
537 }
538 else
539 {
540 CurrentElement = this.CreateElement(CurrentElement, sb.ToString(), StartOfElement, Pos);
541 CurrentElementIsScript = CurrentElement is Elements.Script;
542
543 sb.Clear();
544 Empty = true;
545
546 StartOfText = Pos + 1;
547 State = 0;
548 }
549 }
550 else if (ch <= ' ' || ch == 160)
551 {
552 if (Empty)
553 {
554 sb.Append('<');
555 sb.Append(ch);
556 Empty = false;
557 StartOfText = Pos + 1 - sb.Length;
558 State = 0;
559 }
560 else if (CurrentElementIsScript)
561 {
562 sb.Insert(0, '<');
563 sb.Append(ch);
564 StartOfText = Pos + 1 - sb.Length;
565 State = 0;
566 }
567 else
568 {
569 CurrentElement = this.CreateElement(CurrentElement, sb.ToString(), StartOfElement, Pos);
570 CurrentElementIsScript = CurrentElement is Elements.Script;
571
572 sb.Clear();
573 Empty = true;
574
575 State = 5;
576 }
577 }
578 else if (ch == '?')
579 State = 19;
580 else if (IsNameCharacter(ch))
581 {
582 sb.Append(ch);
583 Empty = false;
584 }
585 else
586 {
587 sb.Insert(0, '<');
588 sb.Append(ch);
589 Empty = false;
590 StartOfText = Pos + 1 - sb.Length;
591 State = 0;
592 }
593 break;
594
595 case 2: // DTD, comment or CDATA?
596 if (ch == '[')
597 State = 21; // CDATA?
598 else if (CurrentElementIsScript)
599 {
600 sb.Append("<!");
601 sb.Append(ch);
602 Empty = false;
603 StartOfText = Pos + 1 - sb.Length;
604 State = 0;
605 }
606 else if (ch == '>')
607 {
608 if (this.dtd is null)
609 this.dtd = new ChunkedList<DtdInstruction>();
610
611 DtdInstruction Dtd = new DtdInstruction(this, CurrentElement, Pos - 2, Pos, string.Empty);
612
613 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
614 CurrentElement = CurrentElement.Parent as HtmlElement;
615
616 CurrentElement?.Add(Dtd);
617 this.dtd.Add(Dtd);
618
619 StartOfText = Pos + 1;
620 State = 0;
621 }
622 else if (ch == '-')
623 State = 15; // Comment?
624 else
625 {
626 sb.Append(ch);
627 Empty = false;
628
629 State = 14; // DTD
630 }
631 break;
632
633 case 3: // Wait for > at end of empty element.
634 if (ch == '>')
635 {
636 EmptyElement = this.CreateElement(CurrentElement, sb.ToString(), StartOfElement, Pos);
637 EmptyElement.EndPosition = Pos;
638
639 sb.Clear();
640 Empty = true;
641
642 StartOfText = Pos + 1;
643 State = 0;
644 }
645 else
646 {
647 sb.Append('/');
648 sb.Append(ch);
649 Empty = false;
650 State = 1;
651 }
652 break;
653
654 case 4: // Closing tag
655 if (ch == '>')
656 {
657 s = sb.ToString().ToUpper();
658 sb.Clear();
659 Empty = true;
660
661 if (!(CurrentElement is null))
662 {
663 if (CurrentElement.EndPosition < Pos)
664 CurrentElement.EndPosition = Pos;
665
666 if (CurrentElement.FullName == s)
667 {
668 CurrentElement = CurrentElement.Parent as HtmlElement;
669 CurrentElementIsScript = CurrentElement is Elements.Script;
670 }
671 else
672 {
673 HtmlElement Loop = CurrentElement.Parent as HtmlElement;
674
675 while (!(Loop is null) && Loop.FullName != s)
676 Loop = Loop.Parent as HtmlElement;
677
678 if (!(Loop is null))
679 {
680 Loop = CurrentElement.Parent as HtmlElement;
681
682 while (!(Loop is null) && Loop.FullName != s)
683 {
684 if (Loop.EndPosition < Pos)
685 Loop.EndPosition = Pos;
686
687 Loop = Loop.Parent as HtmlElement;
688 }
689
690 if (Loop.EndPosition < Pos)
691 Loop.EndPosition = Pos;
692
693 CurrentElement = Loop.Parent as HtmlElement;
694 CurrentElementIsScript = CurrentElement is Elements.Script;
695 }
696 }
697 }
698
699 StartOfText = Pos + 1;
700 State = 0;
701 }
702 else
703 {
704 sb.Append(ch);
705 Empty = false;
706 }
707 break;
708
709 case 5: // Waiting for attribute
710 if (ch == '>')
711 {
712 if (CurrentElement.IsEmptyElement)
713 CurrentElement.EndPosition = Pos;
714
715 StartOfText = Pos + 1;
716 State = 0;
717 }
718 else if (ch == '/')
719 State = 9;
720 else if (ch == '=')
721 {
722 StartOfAttribute = Pos;
723 Name = string.Empty;
724 State = 7;
725 }
726 else if (ch > ' ' && ch != 160)
727 {
728 if (IsNameCharacter(ch))
729 {
730 StartOfAttribute = Pos;
731 sb.Append(ch);
732 Empty = false;
733 State++;
734 }
735 else
736 {
737 sb.Clear();
738 Empty = true;
739
740 State = 13;
741 }
742 }
743 break;
744
745 case 6: // Attribute name
746 if (ch == '>')
747 {
748 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, sb.ToString(), string.Empty));
749
750 sb.Clear();
751 Empty = true;
752
753 StartOfText = Pos + 1;
754 State = 0;
755 }
756 else if (ch == '/')
757 {
758 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, sb.ToString(), string.Empty));
759
760 sb.Clear();
761 Empty = true;
762
763 State = 9;
764 }
765 else if (ch == '=')
766 {
767 Name = sb.ToString();
768
769 sb.Clear();
770 Empty = true;
771
772 State = 7;
773 }
774 else
775 {
776 if (ch <= ' ' || ch == 160)
777 {
778 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, sb.ToString(), string.Empty));
779
780 sb.Clear();
781 Empty = true;
782
783 State--;
784 }
785 else if (IsNameCharacter(ch))
786 {
787 sb.Append(ch);
788 Empty = false;
789 }
790 else
791 {
792 sb.Clear();
793 Empty = true;
794
795 State = 13;
796 }
797 }
798 break;
799
800 case 7: // Wait for value.
801 if (ch == '"' || ch == '\'')
802 {
803 CurrentAttribute = new HtmlAttribute(this, CurrentElement, StartOfAttribute, Name);
804 CurrentElement.AddAttribute(CurrentAttribute);
805
806 EndChar = ch;
807 State = 11;
808 }
809 else if (ch == '>')
810 {
811 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, Name, Name));
812 StartOfText = Pos + 1;
813 State = 0;
814 }
815 else if (ch == '/')
816 {
817 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, Name, Name));
818 State = 9;
819 }
820 else if (ch > ' ' && ch != 160)
821 {
822 sb.Append(ch);
823 Empty = false;
824 State++;
825 }
826 else // Empty attribute
827 {
828 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, Name, Name));
829
830 sb.Clear();
831 Empty = true;
832
833 State = 5;
834 }
835 break;
836
837 case 8: // Non-encapsulated attribute value
838 if (ch <= ' ' || ch == 160)
839 {
840 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, Name, sb.ToString()));
841
842 sb.Clear();
843 Empty = true;
844
845 State = 5;
846 }
847 else if (ch == '>')
848 {
849 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, Name, sb.ToString()));
850
851 sb.Clear();
852 Empty = true;
853
854 StartOfText = Pos + 1;
855 State = 0;
856 }
857 else if (ch == '/')
858 {
859 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, Name, sb.ToString()));
860
861 sb.Clear();
862 Empty = true;
863
864 State = 9;
865 }
866 else
867 {
868 sb.Append(ch);
869 Empty = false;
870 }
871 break;
872
873 case 9: // Waiting for > at end of empty element
874 if (ch == '>')
875 {
876 CurrentElement.EndPosition = Pos;
877 CurrentElement = CurrentElement.Parent as HtmlElement;
878 CurrentElementIsScript = CurrentElement is Elements.Script;
879 StartOfText = Pos + 1;
880 State = 0;
881 }
882 break;
883
884 case 10: // First character of entity
885 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))
886 {
887 sb.Append(ch);
888 Empty = false;
889 State = 30;
890 }
891 else if (ch == '#')
892 State = 31;
893 else
894 {
895 sb.Insert(0, '&');
896 sb.Append(ch);
897 Empty = false;
898 StartOfText = Pos + 1 - sb.Length;
899 State = 0;
900 }
901 break;
902
903 case 11: // Encapsulated attribute value
904 if (ch == EndChar)
905 {
906 if (!Empty)
907 {
908 s = sb.ToString();
909
910 if (CurrentAttribute.HasSegments)
911 CurrentAttribute.Add(new HtmlText(this, CurrentAttribute, Pos - s.Length, Pos - 1, s));
912 else
913 CurrentAttribute.Value = s;
914
915 CurrentAttribute.EndPosition = Pos;
916
917 sb.Clear();
918 Empty = true;
919 }
920
921 State = 5;
922 }
923 else if (ch == '&')
924 {
925 if (!Empty)
926 {
927 s = sb.ToString();
928
929 CurrentAttribute.Add(new HtmlText(this, CurrentAttribute, Pos - s.Length, Pos - 1, s));
930
931 sb.Clear();
932 Empty = true;
933 }
934
935 State = 12;
936 }
937 else
938 {
939 sb.Append(ch);
940 Empty = false;
941 }
942 break;
943
944 case 12: // First character of entity in attribute value
945 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))
946 {
947 sb.Append(ch);
948 Empty = false;
949 State = 34;
950 }
951 else if (ch == '#')
952 State = 35;
953 else
954 {
955 sb.Insert(0, '&');
956 sb.Append(ch);
957 Empty = false;
958 State = 11;
959 }
960 break;
961
962 case 13: // Ignore everything until end of tag.
963 if (ch == '>')
964 {
965 StartOfText = Pos + 1;
966 State = 0;
967 }
968 else if (ch == '/')
969 State = 9;
970 break;
971
972 case 14: // Skip DTD
973 if (ch == '>')
974 {
975 if (this.dtd is null)
976 this.dtd = new ChunkedList<DtdInstruction>();
977
978 s = sb.ToString();
979 DtdInstruction Dtd = new DtdInstruction(this, CurrentElement, Pos - s.Length - 2, Pos, s);
980
981 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
982 CurrentElement = CurrentElement.Parent as HtmlElement;
983
984 CurrentElement?.Add(Dtd);
985 this.dtd.Add(Dtd);
986
987 sb.Clear();
988 Empty = true;
989
990 StartOfText = Pos + 1;
991 State = 0;
992 }
993 else
994 {
995 sb.Append(ch);
996 Empty = false;
997 }
998 break;
999
1000 case 15: // Second hyphen in start of comment?
1001 if (ch == '-')
1002 State++;
1003 else
1004 {
1005 sb.Append("<!-");
1006 sb.Append(ch);
1007 Empty = false;
1008
1009 StartOfText = Pos + 1 - sb.Length;
1010 State = 0;
1011 }
1012 break;
1013
1014 case 16: // In comment
1015 if (ch == '-')
1016 State++;
1017 else
1018 {
1019 sb.Append(ch);
1020 Empty = false;
1021 }
1022 break;
1023
1024 case 17: // Second hyphen?
1025 if (ch == '-')
1026 State++;
1027 else
1028 {
1029 sb.Append('-');
1030 sb.Append(ch);
1031 Empty = false;
1032
1033 State--;
1034 }
1035 break;
1036
1037 case 18: // End of comment
1038 if (ch == '>')
1039 {
1040 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1041 CurrentElement = CurrentElement.Parent as HtmlElement;
1042
1043 s = sb.ToString();
1044 CurrentElement?.Add(new Comment(this, CurrentElement, Pos - s.Length - 5, Pos, s));
1045
1046 sb.Clear();
1047 Empty = true;
1048
1049 StartOfText = Pos + 1;
1050 State = 0;
1051 }
1052 else if (ch == '-')
1053 {
1054 sb.Append('-');
1055 Empty = false;
1056 }
1057 else
1058 {
1059 sb.Append("--");
1060 sb.Append(ch);
1061 Empty = false;
1062
1063 State -= 2;
1064 }
1065 break;
1066
1067 case 19: // In processing instruction
1068 if (ch == '?')
1069 State++;
1070 else
1071 {
1072 sb.Append(ch);
1073 Empty = false;
1074 }
1075 break;
1076
1077 case 20: // End of processing instruction?
1078 if (ch == '>')
1079 {
1080 if (this.processingInstructions is null)
1081 this.processingInstructions = new ChunkedList<ProcessingInstruction>();
1082
1083 s = sb.ToString();
1084 ProcessingInstruction PI = new ProcessingInstruction(this, CurrentElement, Pos - s.Length - 3, Pos, s);
1085
1086 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1087 CurrentElement = CurrentElement.Parent as HtmlElement;
1088
1089 CurrentElement?.Add(PI);
1090 this.processingInstructions.Add(PI);
1091
1092 sb.Clear();
1093 Empty = true;
1094
1095 StartOfText = Pos + 1;
1096 State = 0;
1097 }
1098 else
1099 {
1100 sb.Append('?');
1101 sb.Append(ch);
1102 Empty = false;
1103
1104 State--;
1105 }
1106 break;
1107
1108 case 21: // <![ received
1109 if (ch == 'C')
1110 State++;
1111 else
1112 {
1113 sb.Append("<![");
1114 sb.Append(ch);
1115 Empty = false;
1116 StartOfText = Pos + 1 - sb.Length;
1117 State = 0;
1118 }
1119 break;
1120
1121 case 22: // <![C received
1122 if (ch == 'D')
1123 State++;
1124 else
1125 {
1126 sb.Append("<![C");
1127 sb.Append(ch);
1128 Empty = false;
1129 StartOfText = Pos + 1 - sb.Length;
1130 State = 0;
1131 }
1132 break;
1133
1134 case 23: // <![CD received
1135 if (ch == 'A')
1136 State++;
1137 else
1138 {
1139 sb.Append("<![CD");
1140 sb.Append(ch);
1141 Empty = false;
1142 StartOfText = Pos + 1 - sb.Length;
1143 State = 0;
1144 }
1145 break;
1146
1147 case 24: // <![CDA received
1148 if (ch == 'T')
1149 State++;
1150 else
1151 {
1152 sb.Append("<![CDA");
1153 sb.Append(ch);
1154 Empty = false;
1155 StartOfText = Pos + 1 - sb.Length;
1156 State = 0;
1157 }
1158 break;
1159
1160 case 25: // <![CDAT received
1161 if (ch == 'A')
1162 State++;
1163 else
1164 {
1165 sb.Append("<![CDAT");
1166 sb.Append(ch);
1167 Empty = false;
1168 StartOfText = Pos + 1 - sb.Length;
1169 State = 0;
1170 }
1171 break;
1172
1173 case 26: // <![CDATA received
1174 if (ch == '[')
1175 State++;
1176 else
1177 {
1178 sb.Append("<![CDATA");
1179 sb.Append(ch);
1180 Empty = false;
1181 StartOfText = Pos + 1 - sb.Length;
1182 State = 0;
1183 }
1184 break;
1185
1186 case 27: // In CDATA
1187 if (ch == ']')
1188 State++;
1189 else
1190 {
1191 sb.Append(ch);
1192 Empty = false;
1193 }
1194 break;
1195
1196 case 28:
1197 if (ch == ']')
1198 State++;
1199 else
1200 {
1201 sb.Append('[');
1202 sb.Append(ch);
1203 Empty = false;
1204 State--;
1205 }
1206 break;
1207
1208 case 29:
1209 if (ch == '>')
1210 {
1211 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1212 CurrentElement = CurrentElement.Parent as HtmlElement;
1213
1214 s = sb.ToString();
1215 CurrentElement?.Add(new CDATA(this, CurrentElement, Pos - s.Length - 10, Pos, s));
1216
1217 sb.Clear();
1218 Empty = true;
1219
1220 StartOfText = Pos + 1;
1221 State = 0;
1222 }
1223 else
1224 {
1225 sb.Append("[[");
1226 sb.Append(ch);
1227 Empty = false;
1228 State -= 2;
1229 }
1230 break;
1231
1232 case 30: // Entity name
1233 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))
1234 sb.Append(ch);
1235 else if (ch == ';')
1236 {
1237 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1238 CurrentElement = CurrentElement.Parent as HtmlElement;
1239
1240 s = sb.ToString();
1241 CurrentElement?.Add(new HtmlEntity(this, CurrentElement, Pos - s.Length - 1, Pos, s));
1242
1243 sb.Clear();
1244 Empty = true;
1245
1246 StartOfText = Pos + 1;
1247 State = 0;
1248 }
1249 else
1250 {
1251 sb.Insert(0, '&');
1252 sb.Append(ch);
1253 Empty = false;
1254 StartOfText = Pos + 1 - sb.Length;
1255 State = 0;
1256 }
1257 break;
1258
1259 case 31: // First entity number
1260 if (ch >= '0' && ch <= '9')
1261 {
1262 State++;
1263 sb.Append(ch);
1264 }
1265 else if (ch == 'x' || ch == 'X')
1266 State += 2;
1267 else if (ch == ';' && int.TryParse(s = sb.ToString(), out int Code))
1268 {
1269 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1270 CurrentElement = CurrentElement.Parent as HtmlElement;
1271
1272 CurrentElement?.Add(new HtmlEntityUnicode(this, CurrentElement, Pos - s.Length - 1, Pos, "#" + s, Code));
1273
1274 sb.Clear();
1275 Empty = true;
1276
1277 StartOfText = Pos + 1;
1278 State = 0;
1279 }
1280 else
1281 {
1282 sb.Insert(0, "&#");
1283 sb.Append(ch);
1284 Empty = false;
1285 StartOfText = Pos + 1 - sb.Length;
1286 State = 0;
1287 }
1288 break;
1289
1290 case 32: // Entity number
1291 if (ch >= '0' && ch <= '9')
1292 sb.Append(ch);
1293 else if (ch == ';' && int.TryParse(s = sb.ToString(), out int Code))
1294 {
1295 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1296 CurrentElement = CurrentElement.Parent as HtmlElement;
1297
1298 CurrentElement?.Add(new HtmlEntityUnicode(this, CurrentElement, Pos - s.Length - 1, Pos, "#" + s, Code));
1299
1300 sb.Clear();
1301 Empty = true;
1302
1303 StartOfText = Pos + 1;
1304 State = 0;
1305 }
1306 else
1307 {
1308 sb.Insert(0, "&#");
1309 sb.Append(ch);
1310 Empty = false;
1311 StartOfText = Pos + 1 - sb.Length;
1312 State = 0;
1313 }
1314 break;
1315
1316 case 33: // Hexadecimal entity number
1317 if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))
1318 sb.Append(ch);
1319 else if (ch == ';' && int.TryParse(s = sb.ToString(), NumberStyles.HexNumber, null, out int Code))
1320 {
1321 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1322 CurrentElement = CurrentElement.Parent as HtmlElement;
1323
1324 CurrentElement?.Add(new HtmlEntityUnicode(this, CurrentElement, Pos - s.Length - 1, Pos, "#x" + s, Code));
1325
1326 sb.Clear();
1327 Empty = true;
1328
1329 StartOfText = Pos + 1;
1330 State = 0;
1331 }
1332 else
1333 {
1334 sb.Insert(0, "&#x");
1335 sb.Append(ch);
1336 Empty = false;
1337 StartOfText = Pos + 1 - sb.Length;
1338 State = 0;
1339 }
1340 break;
1341
1342 case 34: // Entity name in attribute value
1343 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))
1344 sb.Append(ch);
1345 else if (ch == ';')
1346 {
1347 s = sb.ToString();
1348 CurrentAttribute.Add(new HtmlEntity(this, CurrentAttribute, Pos - s.Length - 1, Pos, s));
1349
1350 sb.Clear();
1351 Empty = true;
1352
1353 StartOfText = Pos + 1;
1354 State = 11;
1355 }
1356 else
1357 {
1358 sb.Insert(0, '&');
1359 sb.Append(ch);
1360 Empty = false;
1361 StartOfText = Pos + 1 - sb.Length;
1362 State = 11;
1363 }
1364 break;
1365
1366 case 35: // First entity number in attribute value
1367 if (ch >= '0' && ch <= '9')
1368 {
1369 State++;
1370 sb.Append(ch);
1371 }
1372 else if (ch == 'x' || ch == 'X')
1373 State += 2;
1374 else if (ch == ';' && int.TryParse(s = sb.ToString(), out int Code))
1375 {
1376 CurrentAttribute.Add(new HtmlEntityUnicode(this, CurrentAttribute, Pos - s.Length - 1, Pos, s, Code));
1377
1378 sb.Clear();
1379 Empty = true;
1380
1381 StartOfText = Pos + 1;
1382 State = 11;
1383 }
1384 else
1385 {
1386 sb.Insert(0, "&#");
1387 sb.Append(ch);
1388 Empty = false;
1389 StartOfText = Pos + 1 - sb.Length;
1390 State = 11;
1391 }
1392 break;
1393
1394 case 36: // Entity number in attribute value
1395 if (ch >= '0' && ch <= '9')
1396 sb.Append(ch);
1397 else if (ch == ';' && int.TryParse(s = sb.ToString(), out int Code))
1398 {
1399 CurrentAttribute.Add(new HtmlEntityUnicode(this, CurrentAttribute, Pos - s.Length - 1, Pos, s, Code));
1400
1401 sb.Clear();
1402 Empty = true;
1403
1404 StartOfText = Pos + 1;
1405 State = 11;
1406 }
1407 else
1408 {
1409 sb.Insert(0, "&#");
1410 sb.Append(ch);
1411 Empty = false;
1412 StartOfText = Pos + 1 - sb.Length;
1413 State = 11;
1414 }
1415 break;
1416
1417 case 37: // Hexadecimal entity number
1418 if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))
1419 sb.Append(ch);
1420 else if (ch == ';' && int.TryParse(s = sb.ToString(), NumberStyles.HexNumber, null, out int Code))
1421 {
1422 CurrentAttribute.Add(new HtmlEntityUnicode(this, CurrentAttribute, Pos - s.Length - 1, Pos, s, Code));
1423
1424 sb.Clear();
1425 Empty = true;
1426
1427 StartOfText = Pos + 1;
1428 State = 11;
1429 }
1430 else
1431 {
1432 sb.Insert(0, "&#x");
1433 sb.Append(ch);
1434 Empty = false;
1435 StartOfText = Pos + 1 - sb.Length;
1436 State = 11;
1437 }
1438 break;
1439
1440 default:
1441 throw new Exception("Internal error: Unrecognized state.");
1442 }
1443 }
1444
1445 if (!Empty)
1446 {
1447 switch (State)
1448 {
1449 case 0: // Waiting for <
1450 case 1: // Waiting for !, /, attributes or >
1451 case 3: // Wait for > at end of empty element.
1452 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1453 CurrentElement = CurrentElement.Parent as HtmlElement;
1454
1455 s = sb.ToString();
1456 CurrentElement?.Add(new HtmlText(this, CurrentElement, Pos - s.Length, Pos - 1, s));
1457 break;
1458
1459 case 6: // Attribute name
1460 s = sb.ToString();
1461 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, s, s));
1462 break;
1463
1464 case 7: // Waiting for value
1465 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, Name, Name));
1466 break;
1467
1468 case 8: // Non-encapsulated attribute value
1469 CurrentElement.AddAttribute(new HtmlAttribute(this, CurrentElement, StartOfAttribute, Pos - 1, Name, sb.ToString()));
1470 break;
1471
1472 case 10: // First character of entity
1473 case 30: // Entity name
1474 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1475 CurrentElement = CurrentElement.Parent as HtmlElement;
1476
1477 sb.Insert(0, '&');
1478 s = sb.ToString();
1479 CurrentElement?.Add(new HtmlText(this, CurrentElement, Pos - s.Length, Pos - 1, s));
1480 break;
1481
1482 case 11: // Encapsulated attribute value
1483 s = sb.ToString();
1484
1485 if (CurrentAttribute.HasSegments)
1486 CurrentAttribute.Add(new HtmlText(this, CurrentAttribute, Pos - s.Length, Pos - 1, s));
1487 else
1488 CurrentAttribute.Value = s;
1489
1490 CurrentAttribute.EndPosition = Pos - 1;
1491 break;
1492
1493 case 12: // First character of entity in attribute value
1494 case 34: // Entity name in attribute value
1495 sb.Insert(0, '&');
1496 s = sb.ToString();
1497 CurrentAttribute.Add(new HtmlText(this, CurrentElement, Pos - s.Length, Pos - 1, s));
1498 break;
1499
1500 case 16: // In comment
1501 case 17: // Second hyphen?
1502 case 18: // End of comment
1503 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1504 CurrentElement = CurrentElement.Parent as HtmlElement;
1505
1506 s = sb.ToString();
1507 CurrentElement?.Add(new Comment(this, CurrentElement, Pos - s.Length - 5, Pos, s));
1508 break;
1509
1510 case 19: // In processing instruction
1511 case 20: // End of processing instruction?
1512 if (this.processingInstructions is null)
1513 this.processingInstructions = new ChunkedList<ProcessingInstruction>();
1514
1515 s = sb.ToString();
1516 ProcessingInstruction PI = new ProcessingInstruction(this, CurrentElement, Pos - s.Length - 3, Pos, s);
1517
1518 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1519 CurrentElement = CurrentElement.Parent as HtmlElement;
1520
1521 CurrentElement?.Add(PI);
1522 this.processingInstructions.Add(PI);
1523 break;
1524
1525 case 27: // In CDATA
1526 case 28:
1527 case 29:
1528 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1529 CurrentElement = CurrentElement.Parent as HtmlElement;
1530
1531 s = sb.ToString();
1532 CurrentElement?.Add(new CDATA(this, CurrentElement, Pos - s.Length, Pos - 1, s));
1533 break;
1534
1535 case 31: // First entity number
1536 case 32: // Entity number
1537 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1538 CurrentElement = CurrentElement.Parent as HtmlElement;
1539
1540 sb.Insert(0, "&#");
1541 s = sb.ToString();
1542 CurrentElement?.Add(new HtmlText(this, CurrentElement, Pos - s.Length, Pos - 1, s));
1543 break;
1544
1545 case 33: // Hexadecimal entity number
1546 if (!(CurrentElement is null) && CurrentElement.IsEmptyElement)
1547 CurrentElement = CurrentElement.Parent as HtmlElement;
1548
1549 sb.Insert(0, "&#x");
1550 s = sb.ToString();
1551 CurrentElement?.Add(new HtmlText(this, CurrentElement, Pos - s.Length, Pos - 1, s));
1552 break;
1553
1554
1555 case 35: // First entity number in attribute value
1556 case 36: // Entity number in attribute value
1557 sb.Insert(0, "&#");
1558 s = sb.ToString();
1559 CurrentAttribute.Add(new HtmlText(this, CurrentElement, Pos - s.Length, Pos - 1, s));
1560 break;
1561
1562 case 37: // Hexadecimal entity number
1563 sb.Insert(0, "&#x");
1564 s = sb.ToString();
1565 CurrentAttribute.Add(new HtmlText(this, CurrentElement, Pos - s.Length, Pos - 1, s));
1566 break;
1567
1568 }
1569 }
1570
1571 while (!(CurrentElement is null))
1572 {
1573 if (CurrentElement.EndPosition < Pos - 1)
1574 CurrentElement.EndPosition = Pos - 1;
1575
1576 CurrentElement = CurrentElement.Parent as HtmlElement;
1577 }
1578 }
1579
1580 private static bool IsNameCharacter(char ch)
1581 {
1582 if (ch == '-' || ch == '.')
1583 return true;
1584
1585 if (ch < '0')
1586 return false;
1587
1588 if (ch <= '9')
1589 return true;
1590
1591 if (ch == ':')
1592 return true;
1593
1594 if (ch < 'A')
1595 return false;
1596
1597 if (ch <= 'Z')
1598 return true;
1599
1600 if (ch == '_')
1601 return true;
1602
1603 if (ch < 'a')
1604 return false;
1605
1606 if (ch <= 'z')
1607 return true;
1608
1609 if (ch == '\xb7')
1610 return true;
1611
1612 if (ch < '\xc0')
1613 return false;
1614
1615 if (ch == '\xd7' || ch == '\xf7')
1616 return false;
1617
1618 if (ch == '\x037e')
1619 return false;
1620
1621 if (ch <= '\x1fff')
1622 return true;
1623
1624 if (ch == '\x200c' || ch == '\x200d' || ch == '\x203f' || ch == '\x2040')
1625 return true;
1626
1627 if (ch < '\x2070')
1628 return false;
1629
1630 if (ch <= '\x218f')
1631 return true;
1632
1633 if (ch < '\x2c00')
1634 return false;
1635
1636 if (ch <= '\x2fef')
1637 return true;
1638
1639 if (ch < '\x3001')
1640 return false;
1641
1642 if (ch <= '\xd7ff')
1643 return true;
1644
1645 if (ch < '\xf900')
1646 return false;
1647
1648 if (ch <= '\xfdcf')
1649 return true;
1650
1651 if (ch < '\xfdf0')
1652 return false;
1653
1654 if (ch <= '\xfffd')
1655 return true;
1656
1657 return false;
1658 }
1659
1660 private HtmlElement CreateElement(HtmlElement Parent, string TagName, int Start, int Pos)
1661 {
1662 HtmlElement Result;
1663
1664 if (!(Parent is null) && Parent.IsEmptyElement)
1665 Parent = Parent.Parent as HtmlElement;
1666
1667 TagName = TagName.ToUpper();
1668
1669 switch (TagName)
1670 {
1671 case "A": Result = new A(this, Parent, Start); break;
1672 case "ABBR": Result = new Abbr(this, Parent, Start); break;
1673 case "ACRONYM": Result = new Acronym(this, Parent, Start); break;
1674 case "ADDRESS":
1675 Address Address = new Address(this, Parent, Start);
1676 Result = Address;
1677 if (this.address is null)
1678 this.address = new ChunkedList<Address>();
1679 this.address.Add(Address);
1680 break;
1681
1682 case "APPLET": Result = new Applet(this, Parent, Start); break;
1683 case "AREA": Result = new Area(this, Parent, Start); break;
1684 case "ARTICLE":
1685 Article Article = new Article(this, Parent, Start);
1686 Result = Article;
1687 if (this.article is null)
1688 this.article = new ChunkedList<Article>();
1689 this.article.Add(Article);
1690 break;
1691
1692 case "ASIDE":
1693 Aside Aside = new Aside(this, Parent, Start);
1694 Result = Aside;
1695 if (this.aside is null)
1696 this.aside = new ChunkedList<Aside>();
1697 this.aside.Add(Aside);
1698 break;
1699
1700 case "AUDIO":
1701 Elements.Audio Audio = new Elements.Audio(this, Parent, Start);
1702 Result = Audio;
1703 if (this.audio is null)
1704 this.audio = new ChunkedList<Elements.Audio>();
1705 this.audio.Add(Audio);
1706 break;
1707
1708 case "B": Result = new B(this, Parent, Start); break;
1709 case "BASE": Result = new Base(this, Parent, Start); break;
1710 case "BASEFONT": Result = new BaseFont(this, Parent, Start); break;
1711 case "BDI": Result = new Bdi(this, Parent, Start); break;
1712 case "BDO": Result = new Bdo(this, Parent, Start); break;
1713 case "BGSOUND": Result = new BgSound(this, Parent, Start); break;
1714 case "BIG": Result = new Big(this, Parent, Start); break;
1715 case "BLINK": Result = new BLink(this, Parent, Start); break;
1716 case "BLOCKQUOTE": Result = new BlockQuote(this, Parent, Start); break;
1717 case "BODY":
1718 Body Body = new Body(this, Parent, Start);
1719 Result = Body;
1720 if (this.body is null)
1721 this.body = Body;
1722 break;
1723
1724 case "BR": Result = new Br(this, Parent, Start); break;
1725 case "BUTTON": Result = new Button(this, Parent, Start); break;
1726 case "CANVAS": Result = new Canvas(this, Parent, Start); break;
1727 case "CAPTION": Result = new Caption(this, Parent, Start); break;
1728 case "CENTER": Result = new Center(this, Parent, Start); break;
1729 case "CITE":
1730 Cite Cite = new Cite(this, Parent, Start);
1731 Result = Cite;
1732 if (this.cite is null)
1733 this.cite = new ChunkedList<Cite>();
1734 this.cite.Add(Cite);
1735 break;
1736
1737 case "CODE": Result = new Code(this, Parent, Start); break;
1738 case "COL": Result = new Col(this, Parent, Start); break;
1739 case "COLGROUP": Result = new ColGroup(this, Parent, Start); break;
1740 case "COMMAND": Result = new Command(this, Parent, Start); break;
1741 case "CONTENT": Result = new Elements.Content(this, Parent, Start); break;
1742 case "DATA":
1743 Data Data = new Data(this, Parent, Start);
1744 Result = Data;
1745 if (this.data is null)
1746 this.data = new ChunkedList<Data>();
1747 this.data.Add(Data);
1748 break;
1749
1750 case "DATALIST": Result = new DataList(this, Parent, Start); break;
1751 case "DD": Result = new Dd(this, Parent, Start); break;
1752 case "DEL": Result = new Del(this, Parent, Start); break;
1753 case "DETAILS":
1754 Details Details = new Details(this, Parent, Start);
1755 Result = Details;
1756 if (this.details is null)
1757 this.details = new ChunkedList<Details>();
1758 this.details.Add(Details);
1759 break;
1760
1761 case "DFN": Result = new Dfn(this, Parent, Start); break;
1762 case "DIALOG":
1763 Dialog Dialog = new Dialog(this, Parent, Start);
1764 Result = Dialog;
1765 if (this.dialog is null)
1766 this.dialog = new ChunkedList<Dialog>();
1767 this.dialog.Add(Dialog);
1768 break;
1769
1770 case "DIR": Result = new Dir(this, Parent, Start); break;
1771 case "DIV": Result = new Div(this, Parent, Start); break;
1772 case "DL": Result = new Dl(this, Parent, Start); break;
1773 case "DT": Result = new Dt(this, Parent, Start); break;
1774 case "ELEMENT": Result = new Element(this, Parent, Start); break;
1775 case "EM": Result = new Em(this, Parent, Start); break;
1776 case "EMBED": Result = new Embed(this, Parent, Start); break;
1777 case "FIELDSET": Result = new FieldSet(this, Parent, Start); break;
1778 case "FIGCAPTION": Result = new FigCaption(this, Parent, Start); break;
1779 case "FIGURE":
1780 Figure Figure = new Figure(this, Parent, Start);
1781 Result = Figure;
1782 if (this.figure is null)
1783 this.figure = new ChunkedList<Figure>();
1784 this.figure.Add(Figure);
1785 break;
1786
1787 case "FONT": Result = new Font(this, Parent, Start); break;
1788 case "FOOTER":
1789 Footer Footer = new Footer(this, Parent, Start);
1790 Result = Footer;
1791 if (this.footer is null)
1792 this.footer = new ChunkedList<Footer>();
1793 this.footer.Add(Footer);
1794 break;
1795
1796 case "FORM":
1797 Form Form = new Form(this, Parent, Start);
1798 Result = Form;
1799 if (this.form is null)
1800 this.form = new ChunkedList<Form>();
1801 this.form.Add(Form);
1802 break;
1803
1804 case "FRAME": Result = new Frame(this, Parent, Start); break;
1805 case "FRAMESET": Result = new FrameSet(this, Parent, Start); break;
1806 case "H1": Result = new Hn(this, Parent, Start, 1); break;
1807 case "H2": Result = new Hn(this, Parent, Start, 2); break;
1808 case "H3": Result = new Hn(this, Parent, Start, 3); break;
1809 case "H4": Result = new Hn(this, Parent, Start, 4); break;
1810 case "H5": Result = new Hn(this, Parent, Start, 5); break;
1811 case "H6": Result = new Hn(this, Parent, Start, 6); break;
1812 case "H7": Result = new Hn(this, Parent, Start, 7); break;
1813 case "H8": Result = new Hn(this, Parent, Start, 8); break;
1814 case "H9": Result = new Hn(this, Parent, Start, 9); break;
1815 case "HEAD":
1816 Head Head = new Head(this, Parent, Start);
1817 Result = Head;
1818 if (this.head is null)
1819 this.head = Head;
1820 break;
1821
1822 case "HEADER":
1823 Header Header = new Header(this, Parent, Start);
1824 Result = Header;
1825 if (this.header is null)
1826 this.header = new ChunkedList<Header>();
1827 this.header.Add(Header);
1828 break;
1829
1830 case "HGROUP": Result = new HGroup(this, Parent, Start); break;
1831 case "HR": Result = new Hr(this, Parent, Start); break;
1832 case "HTML":
1833 Elements.Html Html = new Elements.Html(this, Parent, Start);
1834 Result = Html;
1835 if (this.html is null)
1836 this.html = Html;
1837 break;
1838
1839 case "I": Result = new I(this, Parent, Start); break;
1840 case "IFRAME": Result = new IFrame(this, Parent, Start); break;
1841 case "IMAGE": Result = new Image(this, Parent, Start); break;
1842 case "IMG":
1843 Img Img = new Img(this, Parent, Start);
1844 Result = Img;
1845 if (this.img is null)
1846 this.img = new ChunkedList<Img>();
1847 this.img.Add(Img);
1848 break;
1849
1850 case "INPUT": Result = new Input(this, Parent, Start); break;
1851 case "INS": Result = new Ins(this, Parent, Start); break;
1852 case "ISINDEX": Result = new IsIndex(this, Parent, Start); break;
1853 case "KBD": Result = new Kbd(this, Parent, Start); break;
1854 case "KEYGEN": Result = new Keygen(this, Parent, Start); break;
1855 case "LABEL": Result = new Label(this, Parent, Start); break;
1856 case "LEGEND": Result = new Legend(this, Parent, Start); break;
1857 case "LI": Result = new Li(this, Parent, Start); break;
1858 case "LINK":
1859 Link Link = new Link(this, Parent, Start);
1860 Result = Link;
1861 if (this.link is null)
1862 this.link = new ChunkedList<Link>();
1863 this.link.Add(Link);
1864 break;
1865
1866 case "LISTING": Result = new Listing(this, Parent, Start); break;
1867 case "MAIN":
1868 Main Main = new Main(this, Parent, Start);
1869 Result = Main;
1870 if (this.main is null)
1871 this.main = new ChunkedList<Main>();
1872 this.main.Add(Main);
1873 break;
1874
1875 case "MAP": Result = new Map(this, Parent, Start); break;
1876 case "MARK": Result = new Mark(this, Parent, Start); break;
1877 case "MARQUEE": Result = new Marquee(this, Parent, Start); break;
1878 case "MENU": Result = new Menu(this, Parent, Start); break;
1879 case "MENUITEM": Result = new MenuItem(this, Parent, Start); break;
1880 case "META":
1881 Meta Meta = new Meta(this, Parent, Start);
1882 Result = Meta;
1883 if (this.meta is null)
1884 this.meta = new ChunkedList<Meta>();
1885 this.meta.Add(Meta);
1886 break;
1887
1888 case "METER": Result = new Meter(this, Parent, Start); break;
1889 case "MULTICOL": Result = new MultiCol(this, Parent, Start); break;
1890 case "NAV":
1891 Nav Nav = new Nav(this, Parent, Start);
1892 Result = Nav;
1893 if (this.nav is null)
1894 this.nav = new ChunkedList<Nav>();
1895 this.nav.Add(Nav);
1896 break;
1897
1898 case "NEXTID": Result = new NextId(this, Parent, Start); break;
1899 case "NOBR": Result = new NoBr(this, Parent, Start); break;
1900 case "NOEMBED": Result = new NoEmbed(this, Parent, Start); break;
1901 case "NOFRAMES": Result = new NoFrames(this, Parent, Start); break;
1902 case "NOSCRIPT": Result = new NoScript(this, Parent, Start); break;
1903 case "OBJECT": Result = new Elements.Object(this, Parent, Start); break;
1904 case "OL": Result = new Ol(this, Parent, Start); break;
1905 case "OPTGROUP": Result = new OptGroup(this, Parent, Start); break;
1906 case "OPTION": Result = new Option(this, Parent, Start); break;
1907 case "OUTPUT": Result = new Output(this, Parent, Start); break;
1908 case "P": Result = new P(this, Parent, Start); break;
1909 case "PARAM": Result = new Param(this, Parent, Start); break;
1910 case "PICTURE":
1911 Picture Picture = new Picture(this, Parent, Start);
1912 Result = Picture;
1913 if (this.picture is null)
1914 this.picture = new ChunkedList<Picture>();
1915 this.picture.Add(Picture);
1916 break;
1917
1918 case "PLAINTEXT": Result = new PlainText(this, Parent, Start); break;
1919 case "PRE": Result = new Pre(this, Parent, Start); break;
1920 case "PROGRESS": Result = new Progress(this, Parent, Start); break;
1921 case "Q": Result = new Q(this, Parent, Start); break;
1922 case "RP": Result = new Rp(this, Parent, Start); break;
1923 case "RT": Result = new Rt(this, Parent, Start); break;
1924 case "RTC": Result = new Rtc(this, Parent, Start); break;
1925 case "RUBY": Result = new Ruby(this, Parent, Start); break;
1926 case "S": Result = new S(this, Parent, Start); break;
1927 case "SAMP": Result = new Samp(this, Parent, Start); break;
1928 case "SCRIPT":
1929 Elements.Script Script = new Elements.Script(this, Parent, Start);
1930 Result = Script;
1931 if (this.script is null)
1932 this.script = new ChunkedList<Elements.Script>();
1933 this.script.Add(Script);
1934 break;
1935
1936 case "SECTION":
1937 Section Section = new Section(this, Parent, Start);
1938 Result = Section;
1939 if (this.section is null)
1940 this.section = new ChunkedList<Section>();
1941 this.section.Add(Section);
1942 break;
1943
1944 case "SELECT": Result = new Select(this, Parent, Start); break;
1945 case "SHADOW": Result = new Shadow(this, Parent, Start); break;
1946 case "SLOT": Result = new Slot(this, Parent, Start); break;
1947 case "SMALL": Result = new Small(this, Parent, Start); break;
1948 case "SOURCE": Result = new Source(this, Parent, Start); break;
1949 case "SPACER": Result = new Spacer(this, Parent, Start); break;
1950 case "SPAN": Result = new Span(this, Parent, Start); break;
1951 case "STRIKE": Result = new Strike(this, Parent, Start); break;
1952 case "STRONG": Result = new Strong(this, Parent, Start); break;
1953 case "STYLE":
1954 Style Style = new Style(this, Parent, Start);
1955 Result = Style;
1956 if (this.style is null)
1957 this.style = new ChunkedList<Style>();
1958 this.style.Add(Style);
1959 break;
1960
1961 case "SUB": Result = new Sub(this, Parent, Start); break;
1962 case "SUMMARY":
1963 Summary Summary = new Summary(this, Parent, Start);
1964 Result = Summary;
1965 if (this.summary is null)
1966 this.summary = new ChunkedList<Summary>();
1967 this.summary.Add(Summary);
1968 break;
1969
1970 case "SUP": Result = new Sup(this, Parent, Start); break;
1971 case "TABLE": Result = new Table(this, Parent, Start); break;
1972 case "TBODY": Result = new TBody(this, Parent, Start); break;
1973 case "TD": Result = new Td(this, Parent, Start); break;
1974 case "TEMPLATE": Result = new Template(this, Parent, Start); break;
1975 case "TEXTAREA": Result = new TextArea(this, Parent, Start); break;
1976 case "TFOOT": Result = new TFoot(this, Parent, Start); break;
1977 case "TH": Result = new Th(this, Parent, Start); break;
1978 case "THEAD": Result = new THead(this, Parent, Start); break;
1979 case "TIME":
1980 Time Time = new Time(this, Parent, Start);
1981 Result = Time;
1982 if (this.time is null)
1983 this.time = new ChunkedList<Time>();
1984 this.time.Add(Time);
1985 break;
1986
1987 case "TITLE":
1988 Title Title = new Title(this, Parent, Start);
1989 Result = Title;
1990 if (this.title is null)
1991 this.title = Title;
1992 break;
1993
1994 case "TR": Result = new Tr(this, Parent, Start); break;
1995 case "TRACK": Result = new Track(this, Parent, Start); break;
1996 case "TT": Result = new Tt(this, Parent, Start); break;
1997 case "U": Result = new U(this, Parent, Start); break;
1998 case "UL": Result = new Ul(this, Parent, Start); break;
1999 case "VAR": Result = new Var(this, Parent, Start); break;
2000 case "VIDEO":
2001 Elements.Video Video = new Elements.Video(this, Parent, Start);
2002 Result = Video;
2003 if (this.video is null)
2004 this.video = new ChunkedList<Elements.Video>();
2005 this.video.Add(Video);
2006 break;
2007
2008 case "WBR": Result = new Wbr(this, Parent, Start); break;
2009 case "XMP": Result = new Xmp(this, Parent, Start); break;
2010 default: Result = new HtmlElement(this, Parent, Start, TagName); break;
2011 }
2012
2013 if (Parent is null)
2014 {
2015 if (this.root is null)
2016 this.root = Result;
2017 }
2018 else
2019 Parent?.Add(Result);
2020
2021 if (Result.IsEmptyElement)
2022 Result.EndPosition = Pos;
2023
2024 return Result;
2025 }
2026
2031 public void Export(XmlWriter Output)
2032 {
2033 this.root?.Export(Output, new Dictionary<string, string>());
2034 }
2035
2040 public void Export(StringBuilder Output)
2041 {
2042 this.root?.Export(Output);
2043 }
2044
2050 {
2051 return new PageMetaData(this);
2052 }
2053
2059 public static string GetBody(string Html)
2060 {
2061 Html = GetContents(Html, "<body", "</body>");
2062 if (string.IsNullOrEmpty(Html))
2063 return Html;
2064
2065 if (Html.StartsWith("<section", StringComparison.CurrentCultureIgnoreCase) &&
2066 Html.EndsWith("</section>", StringComparison.CurrentCultureIgnoreCase))
2067 {
2068 string Html2 = Html.Substring(9, Html.Length - 19).Trim();
2069
2070 if (Html2.IndexOf("</section>", StringComparison.CurrentCultureIgnoreCase) < 0)
2071 Html = Html2;
2072 }
2073
2074 return Html;
2075 }
2076
2077 private static string GetContents(string Html, string StartTag, string EndTag)
2078 {
2079 int i = Html.IndexOf(StartTag, StringComparison.CurrentCultureIgnoreCase);
2080 if (i < 0)
2081 return string.Empty;
2082
2083 i += StartTag.Length;
2084
2085 i = Html.IndexOf('>', i);
2086 if (i < 0)
2087 return string.Empty;
2088
2089 i++;
2090 int j = Html.IndexOf(EndTag, i, StringComparison.CurrentCultureIgnoreCase);
2091
2092 if (j < 0)
2093 return Html.Substring(i).TrimStart();
2094 else
2095 return Html.Substring(i, j - i).Trim();
2096 }
2097
2103 public static string GetHead(string Html)
2104 {
2105 Html = GetContents(Html, "<head", "</head>");
2106 if (string.IsNullOrEmpty(Html))
2107 return Html;
2108
2109 if (Html.StartsWith("<section>", StringComparison.CurrentCultureIgnoreCase) &&
2110 Html.EndsWith("</section>", StringComparison.CurrentCultureIgnoreCase))
2111 {
2112 Html = Html.Substring(9, Html.Length - 19).Trim();
2113 }
2114
2115 return Html;
2116 }
2117
2118 }
2119}
CDATA content.
Definition: CDATA.cs:11
Inline comment found in the document.
Definition: Comment.cs:11
Represents a DTD instruction inside the document.
ARTICLE element
Definition: Article.cs:7
Audio(HtmlDocument Document, HtmlElement Parent, int StartPosition)
AUDIO element
Definition: Audio.cs:14
Html(HtmlDocument Document, HtmlElement Parent, int StartPosition)
HTML element
Definition: Html.cs:14
Script(HtmlDocument Document, HtmlElement Parent, int StartPosition)
SCRIPT element
Definition: Script.cs:14
Video(HtmlDocument Document, HtmlElement Parent, int StartPosition)
VIDEO element
Definition: Video.cs:14
static string GetHead(string Html)
Extracts the contents of the HEAD element in a HTML string.
IEnumerable< Elements.Audio > Audio
AUDIO elements found in document, or null if none found.
IEnumerable< Main > Main
HEADER elements found in document, or null if none found.
Head Head
First HEAD element of document, if found, null otherwise.
HtmlDocument(string Html)
HTML document.
Definition: HtmlDocument.cs:53
HtmlElement Root
Root element.
Definition: HtmlDocument.cs:73
IEnumerable< Style > Style
STYLE elements found in document, or null if none found.
IEnumerable< Nav > Nav
NAV elements found in document, or null if none found.
void Export(StringBuilder Output)
Exports the HTML document to XML.
IEnumerable< Figure > Figure
FIGURE elements found in document, or null if none found.
static string GetBody(string Html)
Extracts the contents of the BODY element in a HTML string.
void Export(XmlWriter Output)
Exports the HTML document to XML.
IEnumerable< Time > Time
TIME elements found in document, or null if none found.
IEnumerable< Article > Article
ARTICLE elements found in document, or null if none found.
IEnumerable< Picture > Picture
PICTURE elements found in document, or null if none found.
IEnumerable< ProcessingInstruction > ProcessingInstructions
Processing instructions found in document, or null if none found.
Title Title
First TITLE element of document, if found, null otherwise.
Definition: HtmlDocument.cs:97
IEnumerable< Address > Address
ADDRESS elements found in document, or null if none found.
IEnumerable< Details > Details
DETAILS elements found in document, or null if none found.
IEnumerable< Aside > Aside
ASIDE elements found in document, or null if none found.
IEnumerable< Dialog > Dialog
DIALOG elements found in document, or null if none found.
IEnumerable< Elements.Script > Script
SCRIPT elements found in document, or null if none found.
PageMetaData GetMetaData()
Gets meta-data about the page.
IEnumerable< Elements.Video > Video
VIDEO elements found in document, or null if none found.
IEnumerable< Header > Header
HEADER elements found in document, or null if none found.
IEnumerable< DtdInstruction > Dtd
DTD instructions found in document, or null if none found.
IEnumerable< Meta > Meta
META elements found in document, or null if none found.
IEnumerable< Summary > Summary
SUMMARY elements found in document, or null if none found.
Elements.Html Html
First HTML element of document, if found, null otherwise.
Definition: HtmlDocument.cs:85
IEnumerable< Img > Img
IMG elements found in document, or null if none found.
Body Body
First BODY element of document, if found, null otherwise.
IEnumerable< Link > Link
LINK elements found in document, or null if none found.
IEnumerable< Form > Form
FORM elements found in document, or null if none found.
IEnumerable< Footer > Footer
FOOTER elements found in document, or null if none found.
IEnumerable< Data > Data
DATA elements found in document, or null if none found.
IEnumerable< Section > Section
SECTION elements found in document, or null if none found.
IEnumerable< Cite > Cite
CITE elements found in document, or null if none found.
Base class for all HTML elements.
Definition: HtmlElement.cs:13
virtual bool IsEmptyElement
If the element is an empty element.
Definition: HtmlElement.cs:351
override void Export(XmlWriter Output, Dictionary< string, string > Namespaces)
Exports the HTML document to XML.
Definition: HtmlElement.cs:240
string FullName
Element full name (including prefix).
Definition: HtmlElement.cs:42
override string ToString()
Definition: HtmlElement.cs:230
HTML Entity, as a unicode number string.
int EndPosition
End position of element.
Definition: HtmlNode.cs:69
Contains meta-data about a page.
Definition: PageMetaData.cs:15
Represents a Processing instruction inside the document.
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272