@@ -463,6 +463,7 @@ private void BeforeAttribute()
463
463
// http://dev.w3.org/html5/spec/tokenization.html#attribute-name-state
464
464
// Read the 'name' (i.e. read until the '=' or whitespace/newline)
465
465
var name = Enumerable . Empty < HtmlSymbol > ( ) ;
466
+ var whitespaceAfterAttributeName = Enumerable . Empty < HtmlSymbol > ( ) ;
466
467
if ( At ( HtmlSymbolType . Text ) )
467
468
{
468
469
name = ReadWhile ( sym =>
@@ -472,6 +473,10 @@ private void BeforeAttribute()
472
473
sym . Type != HtmlSymbolType . CloseAngle &&
473
474
sym . Type != HtmlSymbolType . OpenAngle &&
474
475
( sym . Type != HtmlSymbolType . ForwardSlash || ! NextIs ( HtmlSymbolType . CloseAngle ) ) ) ;
476
+
477
+ // capture whitespace after attribute name (if any)
478
+ whitespaceAfterAttributeName = ReadWhile (
479
+ sym => sym . Type == HtmlSymbolType . WhiteSpace || sym . Type == HtmlSymbolType . NewLine ) ;
475
480
}
476
481
else
477
482
{
@@ -485,6 +490,10 @@ private void BeforeAttribute()
485
490
{
486
491
// Minimized attribute
487
492
493
+ // We are at the prefix of the next attribute or the end of tag. Put it back so it is parsed later.
494
+ PutCurrentBack ( ) ;
495
+ PutBack ( whitespaceAfterAttributeName ) ;
496
+
488
497
// Output anything prior to the attribute, in most cases this will be the tag name:
489
498
// |<input| checked />. If in-between other attributes this will noop or output malformed attribute
490
499
// content (if the previous attribute was malformed).
@@ -507,11 +516,14 @@ private void BeforeAttribute()
507
516
// Start a new markup block for the attribute
508
517
using ( Context . StartBlock ( BlockType . Markup ) )
509
518
{
510
- AttributePrefix ( whitespace , name ) ;
519
+ AttributePrefix ( whitespace , name , whitespaceAfterAttributeName ) ;
511
520
}
512
521
}
513
522
514
- private void AttributePrefix ( IEnumerable < HtmlSymbol > whitespace , IEnumerable < HtmlSymbol > nameSymbols )
523
+ private void AttributePrefix (
524
+ IEnumerable < HtmlSymbol > whitespace ,
525
+ IEnumerable < HtmlSymbol > nameSymbols ,
526
+ IEnumerable < HtmlSymbol > whitespaceAfterAttributeName )
515
527
{
516
528
// First, determine if this is a 'data-' attribute (since those can't use conditional attributes)
517
529
var name = nameSymbols . GetContent ( Span . Start ) ;
@@ -520,14 +532,27 @@ private void AttributePrefix(IEnumerable<HtmlSymbol> whitespace, IEnumerable<Htm
520
532
// Accept the whitespace and name
521
533
Accept ( whitespace ) ;
522
534
Accept ( nameSymbols ) ;
535
+
536
+ // Since this is not a minimized attribute, the whitespace after attribute name belongs to this attribute.
537
+ Accept ( whitespaceAfterAttributeName ) ;
523
538
Assert ( HtmlSymbolType . Equals ) ; // We should be at "="
524
539
AcceptAndMoveNext ( ) ;
540
+
541
+ var whitespaceAfterEquals = ReadWhile ( sym => sym . Type == HtmlSymbolType . WhiteSpace || sym . Type == HtmlSymbolType . NewLine ) ;
525
542
var quote = HtmlSymbolType . Unknown ;
526
543
if ( At ( HtmlSymbolType . SingleQuote ) || At ( HtmlSymbolType . DoubleQuote ) )
527
544
{
545
+ // Found a quote, the whitespace belongs to this attribute.
546
+ Accept ( whitespaceAfterEquals ) ;
528
547
quote = CurrentSymbol . Type ;
529
548
AcceptAndMoveNext ( ) ;
530
549
}
550
+ else if ( whitespaceAfterEquals . Any ( ) )
551
+ {
552
+ // No quotes found after the whitespace. Put it back so that it can be parsed later.
553
+ PutCurrentBack ( ) ;
554
+ PutBack ( whitespaceAfterEquals ) ;
555
+ }
531
556
532
557
// We now have the prefix: (i.e. ' foo="')
533
558
var prefix = Span . GetContent ( ) ;
@@ -537,10 +562,15 @@ private void AttributePrefix(IEnumerable<HtmlSymbol> whitespace, IEnumerable<Htm
537
562
Span . ChunkGenerator = SpanChunkGenerator . Null ; // The block chunk generator will render the prefix
538
563
Output ( SpanKind . Markup ) ;
539
564
540
- // Read the values
541
- while ( ! EndOfFile && ! IsEndOfAttributeValue ( quote , CurrentSymbol ) )
565
+ // Read the attribute value only if the value is quoted
566
+ // or if there is no whitespace between '=' and the unquoted value.
567
+ if ( quote != HtmlSymbolType . Unknown || ! whitespaceAfterEquals . Any ( ) )
542
568
{
543
- AttributeValue ( quote ) ;
569
+ // Read the attribute value.
570
+ while ( ! EndOfFile && ! IsEndOfAttributeValue ( quote , CurrentSymbol ) )
571
+ {
572
+ AttributeValue ( quote ) ;
573
+ }
544
574
}
545
575
546
576
// Capture the suffix
@@ -567,6 +597,11 @@ private void AttributePrefix(IEnumerable<HtmlSymbol> whitespace, IEnumerable<Htm
567
597
// Output the attribute name, the equals and optional quote. Ex: foo="
568
598
Output ( SpanKind . Markup ) ;
569
599
600
+ if ( quote == HtmlSymbolType . Unknown && whitespaceAfterEquals . Any ( ) )
601
+ {
602
+ return ;
603
+ }
604
+
570
605
// Not a "conditional" attribute, so just read the value
571
606
SkipToAndParseCode ( sym => IsEndOfAttributeValue ( quote , sym ) ) ;
572
607
0 commit comments