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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user