Skip to content

Commit

Permalink
HTML and XHTML minifiers now support processing of CDATA sections out…
Browse files Browse the repository at this point in the history
…side the `script` and `style` tags
  • Loading branch information
Taritsyn committed May 6, 2017
1 parent 6bf1f0a commit eb1a5d6
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 129 deletions.
5 changes: 4 additions & 1 deletion README.md
@@ -1,4 +1,7 @@
<img src="logo.png" width="440" height="86" alt="WebMarkupMin logo" />
Web Markup Minifier
===================

<img src="https://raw.githubusercontent.com/Taritsyn/WebMarkupMin/master/images/WebMarkupMin_Logo.png" width="440" height="86" alt="WebMarkupMin logo" />

The **Web Markup Minifier** (abbreviated WebMarkupMin) - a .NET library that contains a set of markup minifiers. The objective of this project is to improve the performance of web applications by reducing the size of HTML, XHTML and XML code.

Expand Down
File renamed without changes
3 changes: 2 additions & 1 deletion nuget/WebMarkupMin.Core/WebMarkupMin.Core.nuspec
Expand Up @@ -18,7 +18,8 @@ Minification of markup produces by removing extra whitespaces, comments and redu

Also supports minification of views of popular JavaScript template engines: KnockoutJS, Kendo UI MVVM and AngularJS 1.X.</description>
<summary>The Web Markup Minifier (abbreviated WebMarkupMin) is a .NET library that contains a set of markup minifiers. The objective of this project is to improve the performance of web applications by reducing the size of HTML, XHTML and XML code.</summary>
<releaseNotes>Added support of .NET Core 1.0.4.</releaseNotes>
<releaseNotes>1. Added support of .NET Core 1.0.4;
2. HTML and XHTML minifiers now support processing of CDATA sections outside the `script` and `style` tags.</releaseNotes>
<copyright>Copyright (c) 2013-2017 Andrey Taritsyn - http://www.taritsyn.ru</copyright>
<language>en-US</language>
<tags>WebMarkupMin Markup HTML XHTML XML Minification Minifier Minify Performance Optimization Compression</tags>
Expand Down
4 changes: 3 additions & 1 deletion nuget/WebMarkupMin.Core/readme.txt
Expand Up @@ -46,7 +46,9 @@
=============
RELEASE NOTES
=============
Added support of .NET Core 1.0.4.
1. Added support of .NET Core 1.0.4;
2. HTML and XHTML minifiers now support processing of CDATA sections outside the
`script` and `style` tags.

=============
DOCUMENTATION
Expand Down
26 changes: 20 additions & 6 deletions src/WebMarkupMin.Core/GenericHtmlMinifier.cs
Expand Up @@ -84,17 +84,17 @@ internal sealed class GenericHtmlMinifier : IMarkupMinifier
RegexOptions.RightToLeft);

private static readonly Regex _beginCdataSectionRegex = new Regex(
@"^\s*<!\[CDATA\[(?:[ \t\v]*\r?\n)?", RegexOptions.IgnoreCase);
@"^\s*<!\[CDATA\[(?:[ \t\v]*\r?\n)?");
private static readonly Regex _endCdataSectionRegex = new Regex(@"(?:\r?\n[ \t\v]*)?\]\]>\s*$",
RegexOptions.RightToLeft);

private static readonly Regex _styleBeginCdataSectionRegex = new Regex(
@"^\s*/\*\s*<!\[CDATA\[\s*\*/(?:[ \t\v]*\r?\n)?", RegexOptions.IgnoreCase);
@"^\s*/\*\s*<!\[CDATA\[\s*\*/(?:[ \t\v]*\r?\n)?");
private static readonly Regex _styleEndCdataSectionRegex = new Regex(@"(?:\r?\n[ \t\v]*)?/\*\s*\]\]>\s*\*/\s*$",
RegexOptions.RightToLeft);

private static readonly Regex _styleBeginMaxCompatibleCdataSectionRegex = new Regex(
@"^\s*<!--\s*/\*\s*--><!\[CDATA\[\s*/\*\s*><!--\s*\*/(?:[ \t\v]*\r?\n)?", RegexOptions.IgnoreCase);
@"^\s*<!--\s*/\*\s*--><!\[CDATA\[\s*/\*\s*><!--\s*\*/(?:[ \t\v]*\r?\n)?");
private static readonly Regex _styleEndMaxCompatibleCdataSectionRegex = new Regex(
@"(?:\r?\n[ \t\v]*)?/\*\s*\]\]>\s*\*/\s*-->\s*$", RegexOptions.RightToLeft);

Expand All @@ -104,14 +104,13 @@ internal sealed class GenericHtmlMinifier : IMarkupMinifier
RegexOptions.RightToLeft);

private static readonly Regex _scriptBeginCdataSectionRegex = new Regex(
@"^\s*(?://[ \t\v]*<!\[CDATA\[[ \t\v\S]*\r?\n|/\*\s*<!\[CDATA\[\s*\*/(?:[ \t\v]*\r?\n)?)",
RegexOptions.IgnoreCase);
@"^\s*(?://[ \t\v]*<!\[CDATA\[[ \t\v\S]*\r?\n|/\*\s*<!\[CDATA\[\s*\*/(?:[ \t\v]*\r?\n)?)");
private static readonly Regex _scriptEndCdataSectionRegex = new Regex(
@"(?:\r?\n//[ \t\v\S]*\]\]>|(?:\r?\n[ \t\v]*)?/\*\s*\]\]>\s*\*/)\s*$", RegexOptions.RightToLeft);

private static readonly Regex _scriptBeginMaxCompatibleCdataSectionRegex = new Regex(
@"^\s*(?:<!--[ \t\v]*//[ \t\v]*--><!\[CDATA\[[ \t\v]*//[ \t\v]*><!--[ \t\v]*\r?\n" +
@"|<!--\s*/\*\s*--><!\[CDATA\[\s*/\*\s*><!--\s*\*/(?:[ \t\v]*\r?\n)?)", RegexOptions.IgnoreCase);
@"|<!--\s*/\*\s*--><!\[CDATA\[\s*/\*\s*><!--\s*\*/(?:[ \t\v]*\r?\n)?)");
private static readonly Regex _scriptEndMaxCompatibleCdataSectionRegex = new Regex(
@"(?:\r?\n[ \t\v]*//[ \t\v]*--><!\]\]>" +
@"|(?:\r?\n[ \t\v]*)?/\*\s*\]\]>\s*\*/\s*-->)\s*$", RegexOptions.RightToLeft);
Expand Down Expand Up @@ -324,6 +323,7 @@ internal sealed class GenericHtmlMinifier : IMarkupMinifier
Comment = CommentHandler,
IfConditionalComment = IfConditionalCommentHandler,
EndIfConditionalComment = EndIfConditionalCommentHandler,
CdataSection = CdataSectionHandler,
StartTag = StartTagHandler,
EndTag = EndTagHandler,
Text = TextHandler,
Expand Down Expand Up @@ -688,6 +688,20 @@ private void CommentHandler(MarkupParsingContext context, string commentText)
}
}

/// <summary>
/// CDATA sections handler
/// </summary>
/// <param name="context">Markup parsing context</param>
/// <param name="cdataText">CDATA text</param>
private void CdataSectionHandler(MarkupParsingContext context, string cdataText)
{
_currentNodeType = HtmlNodeType.CdataSection;

_buffer.Add("<![CDATA[");
_buffer.Add(cdataText);
_buffer.Add("]]>");
}

/// <summary>
/// If conditional comments handler
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/WebMarkupMin.Core/Parsers/HtmlNodeType.cs
Expand Up @@ -11,6 +11,7 @@ internal enum HtmlNodeType : byte
Comment,
IfConditionalComment,
EndIfConditionalComment,
CdataSection,
StartTag,
EndTag,
Text,
Expand Down
47 changes: 30 additions & 17 deletions src/WebMarkupMin.Core/Parsers/HtmlParser.cs
Expand Up @@ -202,14 +202,19 @@ public void Parse(string content)
break;

case '!':
int fourthCharPosition = thirdCharPosition + 1;
char fourthCharValue;
bool fourthCharExist = content.TryGetChar(fourthCharPosition, out fourthCharValue);

if (!fourthCharExist)
{
break;
}

switch (thirdCharValue)
{
case '-':
int fourthCharPosition = thirdCharPosition + 1;
char fourthCharValue;
bool fourthCharExist = content.TryGetChar(fourthCharPosition, out fourthCharValue);

if (fourthCharExist && fourthCharValue == '-')
if (fourthCharValue == '-')
{
// Comments
int fifthCharPosition = fourthCharPosition + 1;
Expand Down Expand Up @@ -247,21 +252,29 @@ public void Parse(string content)
break;

case '[':
// Remaining conditional comments

// Hidden End If conditional comment (e.g. <![endif]-->)
isProcessed = ProcessHiddenEndIfComment();

if (!isProcessed)
if (fourthCharValue == 'C')
{
// Revealed If conditional comment (e.g. <![if ... ]>)
isProcessed = ProcessRevealedIfComment();
// CDATA sections
isProcessed = ProcessCdataSection();
}

if (!isProcessed)
else
{
// Revealed End If conditional comment (e.g. <![endif]>)
isProcessed = ProcessRevealedEndIfComment();
// Remaining conditional comments

// Hidden End If conditional comment (e.g. <![endif]-->)
isProcessed = ProcessHiddenEndIfComment();

if (!isProcessed)
{
// Revealed If conditional comment (e.g. <![if ... ]>)
isProcessed = ProcessRevealedIfComment();
}

if (!isProcessed)
{
// Revealed End If conditional comment (e.g. <![endif]>)
isProcessed = ProcessRevealedEndIfComment();
}
}
break;

Expand Down
34 changes: 34 additions & 0 deletions src/WebMarkupMin.Core/Parsers/MarkupParserBase.cs
Expand Up @@ -2,6 +2,7 @@
using System.Text.RegularExpressions;

using WebMarkupMin.Core.Resources;
using WebMarkupMin.Core.Utilities;

namespace WebMarkupMin.Core.Parsers
{
Expand Down Expand Up @@ -170,6 +171,39 @@ protected bool ProcessEndIgnoringCommentTag()
_innerContext.NodeCoordinates, _innerContext.GetSourceFragment());
}

/// <summary>
/// Process a CDATA sections
/// </summary>
/// <returns>Result of processing (true - is processed; false - is not processed)</returns>
protected bool ProcessCdataSection()
{
bool isProcessed = false;
string content = _innerContext.SourceCode;

if (content.CustomStartsWith("<![CDATA[", _innerContext.Position, StringComparison.Ordinal))
{
int cdataStartPosition = _innerContext.Position;
int cdataEndPosition = content.IndexOf("]]>", cdataStartPosition, StringComparison.Ordinal);

if (cdataEndPosition > cdataStartPosition)
{
string cdataText = content.Substring(cdataStartPosition + 9,
cdataEndPosition - cdataStartPosition - 9);

var cdataSectionHandler = CommonHandlers.CdataSection;
if (cdataSectionHandler != null)
{
cdataSectionHandler(_context, cdataText);
}

_innerContext.IncreasePosition(cdataEndPosition + 3 - cdataStartPosition);
isProcessed = true;
}
}

return isProcessed;
}

#endregion
}
}
16 changes: 16 additions & 0 deletions src/WebMarkupMin.Core/Parsers/MarkupParsingHandlersBase.cs
Expand Up @@ -23,6 +23,15 @@ public CommentDelegate Comment
set;
}

/// <summary>
/// CDATA sections handler
/// </summary>
public CdataSectionDelegate CdataSection
{
get;
set;
}

/// <summary>
/// Text handler
/// </summary>
Expand Down Expand Up @@ -56,6 +65,13 @@ public IgnoredFragmentDelegate IgnoredFragment
/// <param name="comment">Comment text</param>
public delegate void CommentDelegate(MarkupParsingContext context, string comment);

/// <summary>
/// CDATA sections delegate
/// </summary>
/// <param name="context">Markup parsing context</param>
/// <param name="cdataText">CDATA text</param>
public delegate void CdataSectionDelegate(MarkupParsingContext context, string cdataText);

/// <summary>
/// Text delegate
/// </summary>
Expand Down
32 changes: 0 additions & 32 deletions src/WebMarkupMin.Core/Parsers/XmlParser.cs
Expand Up @@ -246,38 +246,6 @@ private bool ProcessProcessingInstruction()
return isProcessed;
}

/// <summary>
/// Process a CDATA sections
/// </summary>
/// <returns>Result of processing (true - is processed; false - is not processed)</returns>
private bool ProcessCdataSection()
{
bool isProcessed = false;
string content = _innerContext.SourceCode;

if (content.CustomStartsWith("<![CDATA[", _innerContext.Position, StringComparison.OrdinalIgnoreCase))
{
int cdataStartPosition = _innerContext.Position;
int cdataEndPosition = content.IndexOf("]]>", cdataStartPosition, StringComparison.Ordinal);

if (cdataEndPosition > cdataStartPosition)
{
string cdataText = content.Substring(cdataStartPosition + 9,
cdataEndPosition - cdataStartPosition - 9);

if (_handlers.CdataSection != null)
{
_handlers.CdataSection(_context, cdataText);
}

_innerContext.IncreasePosition(cdataEndPosition + 3 - cdataStartPosition);
isProcessed = true;
}
}

return isProcessed;
}

/// <summary>
/// Process a start tag
/// </summary>
Expand Down
16 changes: 0 additions & 16 deletions src/WebMarkupMin.Core/Parsers/XmlParsingHandlers.cs
Expand Up @@ -25,15 +25,6 @@ public ProcessingInstructionDelegate ProcessingInstruction
set;
}

/// <summary>
/// CDATA sections handler
/// </summary>
public CdataSectionDelegate CdataSection
{
get;
set;
}

/// <summary>
/// Start tags handler
/// </summary>
Expand Down Expand Up @@ -78,13 +69,6 @@ public EmptyTagDelegate EmptyTag
public delegate void ProcessingInstructionDelegate(MarkupParsingContext context, string instructionName,
IList<XmlAttribute> attributes);

/// <summary>
/// CDATA sections delegate
/// </summary>
/// <param name="context">Markup parsing context</param>
/// <param name="cdataText">CDATA text</param>
public delegate void CdataSectionDelegate(MarkupParsingContext context, string cdataText);

/// <summary>
/// Start tags delegate
/// </summary>
Expand Down

0 comments on commit eb1a5d6

Please sign in to comment.