public virtual void Parse(Lexer lexer, Node element, short mode)
{
Node node;
TagCollection tt = lexer.Options.TagTable;
if ((element.Tag.Model & ContentModel.EMPTY) != 0)
{
return;
}
if (element.Tag == tt.TagA)
{
if (element.Attributes == null)
{
Report.Warning(lexer, element.Parent, element, Report.DISCARDING_UNEXPECTED);
Node.DiscardElement(element);
return;
}
}
/*
ParseInline is used for some block level elements like H1 to H6
For such elements we need to insert inline emphasis tags currently
on the inline stack. For Inline elements, we normally push them
onto the inline stack provided they aren't implicit or OBJECT/APPLET.
This test is carried out in PushInline and PopInline, see istack.c
We don't push A or SPAN to replicate current browser behavior
*/
if (((element.Tag.Model & ContentModel.BLOCK) != 0) || (element.Tag == tt.TagDt))
{
lexer.InlineDup(null);
}
else if ((element.Tag.Model & ContentModel.INLINE) != 0 && element.Tag != tt.TagA &&
element.Tag != tt.TagSpan)
{
lexer.PushInline(element);
}
if (element.Tag == tt.TagNobr)
{
lexer.BadLayout |= Report.USING_NOBR;
}
else if (element.Tag == tt.TagFont)
{
lexer.BadLayout |= Report.USING_FONT;
}
/* Inline elements may or may not be within a preformatted element */
if (mode != Lexer.PREFORMATTED)
{
mode = Lexer.MIXED_CONTENT;
}
while (true)
{
node = lexer.GetToken(mode);
if (node == null)
{
break;
}
/* end tag for current element */
if (node.Tag == element.Tag && node.Type == Node.END_TAG)
{
if ((element.Tag.Model & ContentModel.INLINE) != 0 && element.Tag != tt.TagA)
{
lexer.PopInline(node);
}
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
/*
if a font element wraps an anchor and nothing else
then move the font element inside the anchor since
otherwise it won't alter the anchor text color
*/
if (element.Tag == tt.TagFont && element.Content != null && element.Content == element.Last)
{
Node child = element.Content;
if (child.Tag == tt.TagA)
{
child.Parent = element.Parent;
child.Next = element.Next;
child.Prev = element.Prev;
if (child.Prev != null)
{
child.Prev.Next = child;
}
else
{
child.Parent.Content = child;
}
if (child.Next != null)
{
child.Next.Prev = child;
}
else
{
child.Parent.Last = child;
}
element.Next = null;
element.Prev = null;
element.Parent = child;
element.Content = child.Content;
element.Last = child.Last;
child.Content = element;
child.Last = element;
for (child = element.Content; child != null; child = child.Next)
{
child.Parent = element;
}
}
}
element.Closed = true;
Node.TrimSpaces(lexer, element);
Node.TrimEmptyElement(lexer, element);
return;
}
/* <u>...<u> map 2nd <u> to </u> if 1st is explicit */
/* otherwise emphasis nesting is probably unintentional */
/* big and small have cumulative effect to leave them alone */
if (node.Type == Node.START_TAG && node.Tag == element.Tag && lexer.IsPushed(node) &&
!node.Isimplicit && !element.Isimplicit && node.Tag != null &&
((node.Tag.Model & ContentModel.INLINE) != 0) && node.Tag != tt.TagA && node.Tag != tt.TagFont &&
node.Tag != tt.TagBig && node.Tag != tt.TagSmall)
{
if (element.Content != null && node.Attributes == null)
{
Report.Warning(lexer, element, node, Report.COERCE_TO_ENDTAG);
node.Type = Node.END_TAG;
lexer.UngetToken();
continue;
}
Report.Warning(lexer, element, node, Report.NESTED_EMPHASIS);
}
if (node.Type == Node.TEXT_NODE)
{
/* only called for 1st child */
if (element.Content == null && (mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
if (node.Start >= node.End)
{
continue;
}
Node.InsertNodeAtEnd(element, node);
continue;
}
/* mixed content model so allow text */
if (Node.InsertMisc(element, node))
{
continue;
}
/* deal with HTML tags */
if (node.Tag == tt.TagHtml)
{
if (node.Type == Node.START_TAG || node.Type == Node.START_END_TAG)
{
Report.Warning(lexer, element, node, Report.DISCARDING_UNEXPECTED);
continue;
}
/* otherwise infer end of inline element */
lexer.UngetToken();
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
Node.TrimEmptyElement(lexer, element);
return;
}
/* within <dt> or <pre> map <p> to <br> */
if (node.Tag == tt.TagP && node.Type == Node.START_TAG &&
((mode & Lexer.PREFORMATTED) != 0 || element.Tag == tt.TagDt || element.IsDescendantOf(tt.TagDt)))
{
node.Tag = tt.TagBr;
node.Element = "br";
Node.TrimSpaces(lexer, element);
Node.InsertNodeAtEnd(element, node);
continue;
}
/* ignore unknown and PARAM tags */
if (node.Tag == null || node.Tag == tt.TagParam)
{
Report.Warning(lexer, element, node, Report.DISCARDING_UNEXPECTED);
continue;
}
if (node.Tag == tt.TagBr && node.Type == Node.END_TAG)
{
node.Type = Node.START_TAG;
}
if (node.Type == Node.END_TAG)
{
/* coerce </br> to <br> */
if (node.Tag == tt.TagBr)
{
node.Type = Node.START_TAG;
}
else if (node.Tag == tt.TagP)
{
/* coerce unmatched </p> to <br><br> */
if (!element.IsDescendantOf(tt.TagP))
{
Node.CoerceNode(lexer, node, tt.TagBr);
Node.TrimSpaces(lexer, element);
Node.InsertNodeAtEnd(element, node);
//node = lexer.InferredTag("br");
continue;
}
}
else if ((node.Tag.Model & ContentModel.INLINE) != 0 && node.Tag != tt.TagA &&
(node.Tag.Model & ContentModel.OBJECT) == 0 &&
(element.Tag.Model & ContentModel.INLINE) != 0)
{
/* allow any inline end tag to end current element */
lexer.PopInline(element);
if (element.Tag != tt.TagA)
{
if (node.Tag == tt.TagA && node.Tag != element.Tag)
{
Report.Warning(lexer, element, node, Report.MISSING_ENDTAG_BEFORE);
lexer.UngetToken();
}
else
{
Report.Warning(lexer, element, node, Report.NON_MATCHING_ENDTAG);
}
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
Node.TrimEmptyElement(lexer, element);
return;
}
/* if parent is <a> then discard unexpected inline end tag */
Report.Warning(lexer, element, node, Report.DISCARDING_UNEXPECTED);
continue;
}
/* special case </tr> etc. for stuff moved in front of table */
else if (lexer.Exiled && node.Tag.Model != 0 && (node.Tag.Model & ContentModel.TABLE) != 0)
{
lexer.UngetToken();
Node.TrimSpaces(lexer, element);
Node.TrimEmptyElement(lexer, element);
return;
}
}
/* allow any header tag to end current header */
if ((node.Tag.Model & ContentModel.HEADING) != 0 && (element.Tag.Model & ContentModel.HEADING) != 0)
{
if (node.Tag == element.Tag)
{
Report.Warning(lexer, element, node, Report.NON_MATCHING_ENDTAG);
}
else
{
Report.Warning(lexer, element, node, Report.MISSING_ENDTAG_BEFORE);
lexer.UngetToken();
}
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
Node.TrimEmptyElement(lexer, element);
return;
}
/*
an <A> tag to ends any open <A> element
but <A href=...> is mapped to </A><A href=...>
*/
if (node.Tag == tt.TagA && !node.Isimplicit && lexer.IsPushed(node))
{
/* coerce <a> to </a> unless it has some attributes */
if (node.Attributes == null)
{
node.Type = Node.END_TAG;
Report.Warning(lexer, element, node, Report.COERCE_TO_ENDTAG);
lexer.PopInline(node);
lexer.UngetToken();
continue;
}
lexer.UngetToken();
Report.Warning(lexer, element, node, Report.MISSING_ENDTAG_BEFORE);
lexer.PopInline(element);
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
Node.TrimEmptyElement(lexer, element);
return;
}
if ((element.Tag.Model & ContentModel.HEADING) != 0)
{
if (node.Tag == tt.TagCenter || node.Tag == tt.TagDiv)
{
if (node.Type != Node.START_TAG && node.Type != Node.START_END_TAG)
{
Report.Warning(lexer, element, node, Report.DISCARDING_UNEXPECTED);
continue;
}
Report.Warning(lexer, element, node, Report.TAG_NOT_ALLOWED_IN);
/* insert center as parent if heading is empty */
if (element.Content == null)
{
Node.InsertNodeAsParent(element, node);
continue;
}
/* split heading and make center parent of 2nd part */
Node.InsertNodeAfterElement(element, node);
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
element = lexer.CloneNode(element);
element.Start = lexer.Lexsize;
element.End = lexer.Lexsize;
Node.InsertNodeAtEnd(node, element);
continue;
}
if (node.Tag == tt.TagHr)
{
if (node.Type != Node.START_TAG && node.Type != Node.START_END_TAG)
{
Report.Warning(lexer, element, node, Report.DISCARDING_UNEXPECTED);
continue;
}
Report.Warning(lexer, element, node, Report.TAG_NOT_ALLOWED_IN);
/* insert hr before heading if heading is empty */
if (element.Content == null)
{
Node.InsertNodeBeforeElement(element, node);
continue;
}
/* split heading and insert hr before 2nd part */
Node.InsertNodeAfterElement(element, node);
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
element = lexer.CloneNode(element);
element.Start = lexer.Lexsize;
element.End = lexer.Lexsize;
Node.InsertNodeAfterElement(node, element);
continue;
}
}
if (element.Tag == tt.TagDt)
{
if (node.Tag == tt.TagHr)
{
if (node.Type != Node.START_TAG && node.Type != Node.START_END_TAG)
{
Report.Warning(lexer, element, node, Report.DISCARDING_UNEXPECTED);
continue;
}
Report.Warning(lexer, element, node, Report.TAG_NOT_ALLOWED_IN);
Node dd = lexer.InferredTag("dd");
/* insert hr within dd before dt if dt is empty */
if (element.Content == null)
{
Node.InsertNodeBeforeElement(element, dd);
Node.InsertNodeAtEnd(dd, node);
continue;
}
/* split dt and insert hr within dd before 2nd part */
Node.InsertNodeAfterElement(element, dd);
Node.InsertNodeAtEnd(dd, node);
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
element = lexer.CloneNode(element);
element.Start = lexer.Lexsize;
element.End = lexer.Lexsize;
Node.InsertNodeAfterElement(dd, element);
continue;
}
}
/*
if this is the end tag for an ancestor element
then infer end tag for this element
*/
if (node.Type == Node.END_TAG)
{
Node parent;
for (parent = element.Parent; parent != null; parent = parent.Parent)
{
if (node.Tag == parent.Tag)
{
if ((element.Tag.Model & ContentModel.OPT) == 0 && !element.Isimplicit)
{
Report.Warning(lexer, element, node, Report.MISSING_ENDTAG_BEFORE);
}
if (element.Tag == tt.TagA)
{
lexer.PopInline(element);
}
lexer.UngetToken();
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
Node.TrimEmptyElement(lexer, element);
return;
}
}
}
/* block level tags end this element */
if ((node.Tag.Model & ContentModel.INLINE) == 0)
{
if (node.Type != Node.START_TAG)
{
Report.Warning(lexer, element, node, Report.DISCARDING_UNEXPECTED);
continue;
}
if ((element.Tag.Model & ContentModel.OPT) == 0)
{
Report.Warning(lexer, element, node, Report.MISSING_ENDTAG_BEFORE);
}
if ((node.Tag.Model & ContentModel.HEAD) != 0 && (node.Tag.Model & ContentModel.BLOCK) == 0)
{
MoveToHead(lexer, element, node);
continue;
}
/*
prevent anchors from propagating into block tags
except for headings h1 to h6
*/
if (element.Tag == tt.TagA)
{
if (node.Tag != null && (node.Tag.Model & ContentModel.HEADING) == 0)
{
lexer.PopInline(element);
}
else if (element.Content == null)
{
Node.DiscardElement(element);
lexer.UngetToken();
return;
}
}
lexer.UngetToken();
if ((mode & Lexer.PREFORMATTED) == 0)
{
Node.TrimSpaces(lexer, element);
}
Node.TrimEmptyElement(lexer, element);
return;
}
/* parse inline element */
if (node.Type == Node.START_TAG || node.Type == Node.START_END_TAG)
{
if (node.Isimplicit)
{
Report.Warning(lexer, element, node, Report.INSERTING_TAG);
}
/* trim white space before <br> */
if (node.Tag == tt.TagBr)
{
Node.TrimSpaces(lexer, element);
}
Node.InsertNodeAtEnd(element, node);
ParseTag(lexer, node, mode);
continue;
}
/* discard unexpected tags */
Report.Warning(lexer, element, node, Report.DISCARDING_UNEXPECTED);
}
if ((element.Tag.Model & ContentModel.OPT) == 0)
{
Report.Warning(lexer, element, node, Report.MISSING_ENDTAG_FOR);
}
Node.TrimEmptyElement(lexer, element);
}