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 tagDef = getHtmlTagDefinition(el.name);
|
||||||
var parentEl = this._getParentElement();
|
var parentEl = this._getParentElement();
|
||||||
if (tagDef.requireExtraParent(isPresent(parentEl) ? parentEl.name : null)) {
|
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._addToParent(newParent);
|
||||||
this.elementStack.push(newParent);
|
this.elementStack.push(newParent);
|
||||||
this.elementStack.push(el);
|
this.elementStack.push(el);
|
||||||
|
|
|
@ -66,30 +66,35 @@ export enum HtmlTagContentType {
|
||||||
export class HtmlTagDefinition {
|
export class HtmlTagDefinition {
|
||||||
private closedByChildren: {[key: string]: boolean} = {};
|
private closedByChildren: {[key: string]: boolean} = {};
|
||||||
public closedByParent: boolean = false;
|
public closedByParent: boolean = false;
|
||||||
public requiredParent: string;
|
public requiredParents: {[key: string]: boolean};
|
||||||
|
public parentToAdd: string;
|
||||||
public implicitNamespacePrefix: string;
|
public implicitNamespacePrefix: string;
|
||||||
public contentType: HtmlTagContentType;
|
public contentType: HtmlTagContentType;
|
||||||
|
|
||||||
constructor({closedByChildren, requiredParent, implicitNamespacePrefix, contentType,
|
constructor({closedByChildren, requiredParents, implicitNamespacePrefix, contentType,
|
||||||
closedByParent}: {
|
closedByParent}: {
|
||||||
closedByChildren?: string,
|
closedByChildren?: string[],
|
||||||
closedByParent?: boolean,
|
closedByParent?: boolean,
|
||||||
requiredParent?: string,
|
requiredParents?: string[],
|
||||||
implicitNamespacePrefix?: string,
|
implicitNamespacePrefix?: string,
|
||||||
contentType?: HtmlTagContentType
|
contentType?: HtmlTagContentType
|
||||||
} = {}) {
|
} = {}) {
|
||||||
if (isPresent(closedByChildren) && closedByChildren.length > 0) {
|
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.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.implicitNamespacePrefix = implicitNamespacePrefix;
|
||||||
this.contentType = isPresent(contentType) ? contentType : HtmlTagContentType.PARSABLE_DATA;
|
this.contentType = isPresent(contentType) ? contentType : HtmlTagContentType.PARSABLE_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
requireExtraParent(currentParent: string): boolean {
|
requireExtraParent(currentParent: string): boolean {
|
||||||
return isPresent(this.requiredParent) &&
|
return isPresent(this.requiredParents) &&
|
||||||
(isBlank(currentParent) || this.requiredParent != currentParent.toLowerCase());
|
(isBlank(currentParent) || this.requiredParents[currentParent.toLowerCase()] != true);
|
||||||
}
|
}
|
||||||
|
|
||||||
isClosedByChild(name: string): boolean {
|
isClosedByChild(name: string): boolean {
|
||||||
|
@ -101,37 +106,66 @@ export class HtmlTagDefinition {
|
||||||
// see http://www.w3.org/TR/html51/syntax.html#optional-tags
|
// see http://www.w3.org/TR/html51/syntax.html#optional-tags
|
||||||
// This implementation does not fully conform to the HTML5 spec.
|
// This implementation does not fully conform to the HTML5 spec.
|
||||||
var TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = {
|
var TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = {
|
||||||
'link': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
'link': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||||
'ng-content': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
'ng-content': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||||
'img': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
'img': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||||
'input': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
'input': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||||
'hr': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
'hr': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||||
'br': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
'br': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||||
'wbr': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}),
|
'wbr': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}),
|
||||||
'p': new HtmlTagDefinition({
|
'p': new HtmlTagDefinition({
|
||||||
closedByChildren:
|
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',
|
'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
|
closedByParent: true
|
||||||
}),
|
}),
|
||||||
'thead': new HtmlTagDefinition({closedByChildren: 'tbody,tfoot'}),
|
'thead': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot']}),
|
||||||
'tbody': new HtmlTagDefinition({closedByChildren: 'tbody,tfoot', closedByParent: true}),
|
'tbody': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot'], closedByParent: true}),
|
||||||
'tfoot': new HtmlTagDefinition({closedByChildren: 'tbody', closedByParent: true}),
|
'tfoot': new HtmlTagDefinition({closedByChildren: ['tbody'], closedByParent: true}),
|
||||||
'tr': new HtmlTagDefinition(
|
'tr': new HtmlTagDefinition({
|
||||||
{closedByChildren: 'tr', requiredParent: 'tbody', closedByParent: true}),
|
closedByChildren: ['tr'],
|
||||||
'td': new HtmlTagDefinition({closedByChildren: 'td,th', closedByParent: true}),
|
requiredParents: ['tbody', 'tfoot', 'thead'],
|
||||||
'th': new HtmlTagDefinition({closedByChildren: 'td,th', closedByParent: true}),
|
closedByParent: true
|
||||||
'col': new HtmlTagDefinition({closedByChildren: 'col', requiredParent: 'colgroup'}),
|
}),
|
||||||
|
'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'}),
|
'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),
|
||||||
'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}),
|
'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}),
|
||||||
'li': new HtmlTagDefinition({closedByChildren: 'li', closedByParent: true}),
|
'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}),
|
||||||
'dt': new HtmlTagDefinition({closedByChildren: 'dt,dd'}),
|
'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}),
|
||||||
'dd': new HtmlTagDefinition({closedByChildren: 'dt,dd', closedByParent: true}),
|
'dd': new HtmlTagDefinition({closedByChildren: ['dt', 'dd'], closedByParent: true}),
|
||||||
'rb': new HtmlTagDefinition({closedByChildren: 'rb,rt,rtc,rp', closedByParent: true}),
|
'rb': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
|
||||||
'rt': 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}),
|
'rtc': new HtmlTagDefinition({closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true}),
|
||||||
'rp': new HtmlTagDefinition({closedByChildren: 'rb,rt,rtc,rp', closedByParent: true}),
|
'rp': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
|
||||||
'optgroup': new HtmlTagDefinition({closedByChildren: 'optgroup', closedByParent: true}),
|
'optgroup': new HtmlTagDefinition({closedByChildren: ['optgroup'], closedByParent: true}),
|
||||||
'option': new HtmlTagDefinition({closedByChildren: 'option,optgroup', closedByParent: true}),
|
'option': new HtmlTagDefinition({closedByChildren: ['option', 'optgroup'], closedByParent: true}),
|
||||||
'style': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}),
|
'style': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}),
|
||||||
'script': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}),
|
'script': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}),
|
||||||
'title': new HtmlTagDefinition({contentType: HtmlTagContentType.ESCAPABLE_RAW_TEXT}),
|
'title': new HtmlTagDefinition({contentType: HtmlTagContentType.ESCAPABLE_RAW_TEXT}),
|
||||||
|
|
|
@ -97,11 +97,24 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add the requiredParent', () => {
|
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([
|
.toEqual([
|
||||||
[HtmlElementAst, 'table', 0],
|
[HtmlElementAst, 'table', 0],
|
||||||
|
[HtmlElementAst, 'thead', 1],
|
||||||
|
[HtmlElementAst, 'tr', 2],
|
||||||
|
[HtmlAttrAst, 'head', ''],
|
||||||
[HtmlElementAst, 'tbody', 1],
|
[HtmlElementAst, 'tbody', 1],
|
||||||
[HtmlElementAst, 'tr', 2],
|
[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