fix(HtmlParser): do not add a tbody parent for tr inside thead & tfoot
fixes #5403
This commit is contained in:
parent
bdfed9d850
commit
c58e7e0e91
|
@ -145,7 +145,7 @@ class TreeBuilder {
|
|||
var tagDef = getHtmlTagDefinition(el.name);
|
||||
var parentEl = this._getParentElement();
|
||||
if (tagDef.requireExtraParent(isPresent(parentEl) ? parentEl.name : null)) {
|
||||
var newParent = new HtmlElementAst(tagDef.requiredParent, [], [el], el.sourceSpan);
|
||||
var newParent = new HtmlElementAst(tagDef.parentToAdd, [], [el], el.sourceSpan);
|
||||
this._addToParent(newParent);
|
||||
this.elementStack.push(newParent);
|
||||
this.elementStack.push(el);
|
||||
|
|
|
@ -66,30 +66,35 @@ export enum HtmlTagContentType {
|
|||
export class HtmlTagDefinition {
|
||||
private closedByChildren: {[key: string]: boolean} = {};
|
||||
public closedByParent: boolean = false;
|
||||
public requiredParent: string;
|
||||
public requiredParents: {[key: string]: boolean};
|
||||
public parentToAdd: string;
|
||||
public implicitNamespacePrefix: string;
|
||||
public contentType: HtmlTagContentType;
|
||||
|
||||
constructor({closedByChildren, requiredParent, implicitNamespacePrefix, contentType,
|
||||
constructor({closedByChildren, requiredParents, implicitNamespacePrefix, contentType,
|
||||
closedByParent}: {
|
||||
closedByChildren?: string,
|
||||
closedByChildren?: string[],
|
||||
closedByParent?: boolean,
|
||||
requiredParent?: string,
|
||||
requiredParents?: string[],
|
||||
implicitNamespacePrefix?: string,
|
||||
contentType?: HtmlTagContentType
|
||||
} = {}) {
|
||||
if (isPresent(closedByChildren) && closedByChildren.length > 0) {
|
||||
closedByChildren.split(',').forEach(tagName => this.closedByChildren[tagName.trim()] = true);
|
||||
closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true);
|
||||
}
|
||||
this.closedByParent = normalizeBool(closedByParent);
|
||||
this.requiredParent = requiredParent;
|
||||
if (isPresent(requiredParents) && requiredParents.length > 0) {
|
||||
this.requiredParents = {};
|
||||
this.parentToAdd = requiredParents[0];
|
||||
requiredParents.forEach(tagName => this.requiredParents[tagName] = true);
|
||||
}
|
||||
this.implicitNamespacePrefix = implicitNamespacePrefix;
|
||||
this.contentType = isPresent(contentType) ? contentType : HtmlTagContentType.PARSABLE_DATA;
|
||||
}
|
||||
|
||||
requireExtraParent(currentParent: string): boolean {
|
||||
return isPresent(this.requiredParent) &&
|
||||
(isBlank(currentParent) || this.requiredParent != currentParent.toLowerCase());
|
||||
return isPresent(this.requiredParents) &&
|
||||
(isBlank(currentParent) || this.requiredParents[currentParent.toLowerCase()] != true);
|
||||
}
|
||||
|
||||
isClosedByChild(name: string): boolean {
|
||||
|
@ -101,37 +106,66 @@ export class HtmlTagDefinition {
|
|||
// see http://www.w3.org/TR/html51/syntax.html#optional-tags
|
||||
// This implementation does not fully conform to the HTML5 spec.
|
||||
var TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = {
|
||||
'link': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
||||
'ng-content': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
||||
'img': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
||||
'input': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
||||
'hr': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
||||
'br': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
||||
'wbr': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
||||
'link': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||
'ng-content': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||
'img': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||
'input': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||
'hr': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||
'br': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||
'wbr': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||
'p': new HtmlTagDefinition({
|
||||
closedByChildren:
|
||||
'address,article,aside,blockquote,div,dl,fieldset,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,main,nav,ol,p,pre,section,table,ul',
|
||||
closedByChildren: [
|
||||
'address',
|
||||
'article',
|
||||
'aside',
|
||||
'blockquote',
|
||||
'div',
|
||||
'dl',
|
||||
'fieldset',
|
||||
'footer',
|
||||
'form',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'header',
|
||||
'hgroup',
|
||||
'hr',
|
||||
'main',
|
||||
'nav',
|
||||
'ol',
|
||||
'p',
|
||||
'pre',
|
||||
'section',
|
||||
'table',
|
||||
'ul'
|
||||
],
|
||||
closedByParent: true
|
||||
}),
|
||||
'thead': new HtmlTagDefinition({closedByChildren: 'tbody,tfoot'}),
|
||||
'tbody': new HtmlTagDefinition({closedByChildren: 'tbody,tfoot', closedByParent: true}),
|
||||
'tfoot': new HtmlTagDefinition({closedByChildren: 'tbody', closedByParent: true}),
|
||||
'tr': new HtmlTagDefinition(
|
||||
{closedByChildren: 'tr', requiredParent: 'tbody', closedByParent: true}),
|
||||
'td': new HtmlTagDefinition({closedByChildren: 'td,th', closedByParent: true}),
|
||||
'th': new HtmlTagDefinition({closedByChildren: 'td,th', closedByParent: true}),
|
||||
'col': new HtmlTagDefinition({closedByChildren: 'col', requiredParent: 'colgroup'}),
|
||||
'thead': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot']}),
|
||||
'tbody': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot'], closedByParent: true}),
|
||||
'tfoot': new HtmlTagDefinition({closedByChildren: ['tbody'], closedByParent: true}),
|
||||
'tr': new HtmlTagDefinition({
|
||||
closedByChildren: ['tr'],
|
||||
requiredParents: ['tbody', 'tfoot', 'thead'],
|
||||
closedByParent: true
|
||||
}),
|
||||
'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
|
||||
'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
|
||||
'col': new HtmlTagDefinition({closedByChildren: ['col'], requiredParents: ['colgroup']}),
|
||||
'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),
|
||||
'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}),
|
||||
'li': new HtmlTagDefinition({closedByChildren: 'li', closedByParent: true}),
|
||||
'dt': new HtmlTagDefinition({closedByChildren: 'dt,dd'}),
|
||||
'dd': new HtmlTagDefinition({closedByChildren: 'dt,dd', closedByParent: true}),
|
||||
'rb': new HtmlTagDefinition({closedByChildren: 'rb,rt,rtc,rp', closedByParent: true}),
|
||||
'rt': new HtmlTagDefinition({closedByChildren: 'rb,rt,rtc,rp', closedByParent: true}),
|
||||
'rtc': new HtmlTagDefinition({closedByChildren: 'rb,rtc,rp', closedByParent: true}),
|
||||
'rp': new HtmlTagDefinition({closedByChildren: 'rb,rt,rtc,rp', closedByParent: true}),
|
||||
'optgroup': new HtmlTagDefinition({closedByChildren: 'optgroup', closedByParent: true}),
|
||||
'option': new HtmlTagDefinition({closedByChildren: 'option,optgroup', closedByParent: true}),
|
||||
'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}),
|
||||
'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}),
|
||||
'dd': new HtmlTagDefinition({closedByChildren: ['dt', 'dd'], closedByParent: true}),
|
||||
'rb': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
|
||||
'rt': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
|
||||
'rtc': new HtmlTagDefinition({closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true}),
|
||||
'rp': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
|
||||
'optgroup': new HtmlTagDefinition({closedByChildren: ['optgroup'], closedByParent: true}),
|
||||
'option': new HtmlTagDefinition({closedByChildren: ['option', 'optgroup'], closedByParent: true}),
|
||||
'style': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}),
|
||||
'script': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}),
|
||||
'title': new HtmlTagDefinition({contentType: HtmlTagContentType.ESCAPABLE_RAW_TEXT}),
|
||||
|
|
|
@ -97,11 +97,24 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should add the requiredParent', () => {
|
||||
expect(humanizeDom(parser.parse('<table><tr></tr></table>', 'TestComp')))
|
||||
expect(
|
||||
humanizeDom(parser.parse(
|
||||
'<table><thead><tr head></tr></thead><tr noparent></tr><tbody><tr body></tr></tbody><tfoot><tr foot></tr></tfoot></table>',
|
||||
'TestComp')))
|
||||
.toEqual([
|
||||
[HtmlElementAst, 'table', 0],
|
||||
[HtmlElementAst, 'thead', 1],
|
||||
[HtmlElementAst, 'tr', 2],
|
||||
[HtmlAttrAst, 'head', ''],
|
||||
[HtmlElementAst, 'tbody', 1],
|
||||
[HtmlElementAst, 'tr', 2],
|
||||
[HtmlAttrAst, 'noparent', ''],
|
||||
[HtmlElementAst, 'tbody', 1],
|
||||
[HtmlElementAst, 'tr', 2],
|
||||
[HtmlAttrAst, 'body', ''],
|
||||
[HtmlElementAst, 'tfoot', 1],
|
||||
[HtmlElementAst, 'tr', 2],
|
||||
[HtmlAttrAst, 'foot', '']
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue