diff --git a/HtmlMinifier.Tests/ArgumentsTests.cs b/HtmlMinifier.Tests/ArgumentsTests.cs
index b89d69e..067ebac 100644
--- a/HtmlMinifier.Tests/ArgumentsTests.cs
+++ b/HtmlMinifier.Tests/ArgumentsTests.cs
@@ -15,7 +15,7 @@ public void FindValuesInArgs_WithIgnores_ShouldReturnCorrectly()
argsList.Add("ignorejscomments");
// Act
- Features disabledFeatures = Program.FindValuesInArgs(argsList.ToArray());
+ Features disabledFeatures = new Features(argsList.ToArray());
// Assert
Assert.That(disabledFeatures.IgnoreHtmlComments, Is.True);
@@ -30,7 +30,7 @@ public void FindValuesInArgs_WithOneIgnore_ShouldReturnCorrectly()
argsList.Add("ignorehtmlcomments");
// Act
- Features disabledFeatures = Program.FindValuesInArgs(argsList.ToArray());
+ Features disabledFeatures = new Features(argsList.ToArray());
// Assert
Assert.That(disabledFeatures.IgnoreHtmlComments, Is.True);
diff --git a/HtmlMinifier.Tests/FileExtensionTests.cs b/HtmlMinifier.Tests/FileExtensionTests.cs
new file mode 100644
index 0000000..3652373
--- /dev/null
+++ b/HtmlMinifier.Tests/FileExtensionTests.cs
@@ -0,0 +1,19 @@
+using NUnit.Framework;
+using System.Collections.Generic;
+
+namespace HtmlMinifier.Tests
+{
+ [TestFixture]
+ public class FileExtensionTests
+ {
+ [Test]
+ public void GithubIssue25__ShouldReturnCorrectly()
+ {
+ Assert.That("test.html".IsHtmlFile(), Is.True);
+ Assert.That("codes.js.aspx".IsHtmlFile(), Is.True);
+
+ Assert.That("codes.aspx.js".IsHtmlFile(), Is.False);
+ Assert.That("aspx.codes.js".IsHtmlFile(), Is.False);
+ }
+ }
+}
diff --git a/HtmlMinifier.Tests/HtmlMinifier.Tests.csproj b/HtmlMinifier.Tests/HtmlMinifier.Tests.csproj
index 1db358b..c857a64 100644
--- a/HtmlMinifier.Tests/HtmlMinifier.Tests.csproj
+++ b/HtmlMinifier.Tests/HtmlMinifier.Tests.csproj
@@ -44,6 +44,7 @@
+
@@ -75,6 +76,9 @@
+
+
+
", "");
+ return reader.MinifyHtmlCode(features);
}
-
- // single-line doctype must be preserved
- var firstEndBracketPosition = htmlContents.IndexOf(">", StringComparison.Ordinal);
- if (firstEndBracketPosition >= 0)
- {
- htmlContents = htmlContents.Remove(firstEndBracketPosition, 1);
- htmlContents = htmlContents.Insert(firstEndBracketPosition, ">");
- }
-
- return htmlContents.Trim();
- }
-
- ///
- /// Removes any JavaScript Comments in a script block
- ///
- ///
- /// A string with all JS comments removed
- public static string RemoveJavaScriptComments(string javaScriptComments)
- {
- // Remove JavaScript comments
- Regex extractScripts = new Regex(@"");
-
- // Loop through the script blocks
- foreach (Match match in extractScripts.Matches(javaScriptComments))
- {
- var scriptBlock = match.Value;
-
- javaScriptComments = javaScriptComments.Replace(scriptBlock, Regex.Replace(scriptBlock, @"[^:|""|']//(.*?)\r?\n", ""));
-
- }
-
- return javaScriptComments;
- }
-
- ///
- /// Ensure that the max character count is less than 65K.
- /// If so, break onto the next line.
- ///
- /// The minified HTML
- /// An optional parameter for the max character count
- /// A html string
- public static string EnsureMaxLength(string htmlContents, string[] args)
- {
- int maxLength = 60000;
-
- // This is a check to see if the args contain an optional parameter for the max line length
- if (args != null && args.Length > 1)
- {
- // Try and parse the value sent through
- if (!int.TryParse(args[1], out maxLength))
- {
- maxLength = 60000;
- }
-
- int htmlLength = htmlContents.Length;
- int currentMaxLength = maxLength;
- int position;
-
- while (htmlLength > currentMaxLength)
- {
- position = htmlContents.LastIndexOf("><", currentMaxLength);
- htmlContents = htmlContents.Substring(0, position + 1) + "\r\n" + htmlContents.Substring(position + 1);
- currentMaxLength += maxLength;
- }
- }
-
- return htmlContents;
- }
-
- ///
- /// Check the arguments passed in to determine if we should enable or disable any features.
- ///
- /// The arguments passed in.
- /// A list of features to be enabled or disabled.
- public static Features FindValuesInArgs(string[] args)
- {
- _features = new Features();
-
- if (args.Contains("ignorehtmlcomments"))
- {
- _features.IgnoreHtmlComments = true;
- }
-
- if (args.Contains("ignorejscomments"))
- {
- _features.IgnoreJsComments = true;
- }
-
- return _features;
}
}
}
diff --git a/ViewMinifier/StreamReaderExtension.cs b/ViewMinifier/StreamReaderExtension.cs
new file mode 100644
index 0000000..53d9a91
--- /dev/null
+++ b/ViewMinifier/StreamReaderExtension.cs
@@ -0,0 +1,210 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace HtmlMinifier
+{
+ public static class StreamReaderExtension
+ {
+ public static string MinifyHtmlCode(this StreamReader reader, Features features)
+ {
+ return MinifyHtmlCode(reader.ReadToEnd(), features);
+ }
+
+ public static string MinifyHtmlCode(string htmlCode, Features features)
+ {
+ string contents;
+ // Minify the contents
+ contents = MinifyHtml(htmlCode, features);
+
+ // Ensure that the max length is less than 65K characters
+ contents = EnsureMaxLength(contents, features);
+
+ // Re-add the @model declaration
+ contents = ReArrangeDeclarations(contents);
+ return contents;
+ }
+
+ ///
+ /// Find any occurences of the particular Razor keywords
+ /// and add a new line or move to the top of the view.
+ ///
+ /// The contents of the file
+ ///
+ /// The .
+ ///
+ public static string ReArrangeDeclarations(string fileContents)
+ {
+ // A list of all the declarations
+ Dictionary declarations = new Dictionary();
+ declarations.Add("@model ", true);
+ declarations.Add("@using ", false);
+ declarations.Add("@inherits ", false);
+
+ // Loop through the declarations
+ foreach (var declaration in declarations)
+ {
+ fileContents = ReArrangeDeclaration(fileContents, declaration.Key, declaration.Value);
+ }
+
+ return fileContents;
+ }
+
+ ///
+ /// Re-arranges the razor syntax on its own line.
+ /// It seems to break the razor engine if this isnt on
+ /// it's own line in certain cases.
+ ///
+ /// The file contents.
+ /// The declaration keywords that will cause a new line split.
+ ///
+ /// The .
+ ///
+ private static string ReArrangeDeclaration(string fileContents, string declaration, bool bringToTop)
+ {
+ // Find possible multiple occurences in the file contents
+ MatchCollection matches = Regex.Matches(fileContents, declaration);
+
+ // Loop through the matches
+ int alreadyMatched = 0;
+ foreach (Match match in matches)
+ {
+ int position = declaration.Length;
+ int declarationPosition = match.Index;
+
+ // If we have more than one match, we need to keep the counter moving everytime we add a new line
+ if (matches.Count > 1 && alreadyMatched > 0)
+ {
+ // Cos we added one or more new line break \n\r
+ declarationPosition += (2 * alreadyMatched);
+ }
+
+ while (declarationPosition >= 0)
+ {
+ // Move one forward
+ position += 1;
+ string substring = fileContents.Substring(declarationPosition, position);
+
+ // Check if it contains a whitespace at the end
+ if (substring.EndsWith(" ") || substring.EndsWith(">"))
+ {
+ if (bringToTop)
+ {
+ // First replace the occurence
+ fileContents = fileContents.Replace(substring, "");
+
+ // Next move it to the top on its own line
+ fileContents = substring + Environment.NewLine + fileContents;
+ break;
+ }
+ else
+ {
+ // Add a line break afterwards
+ fileContents = fileContents.Replace(substring, substring + Environment.NewLine);
+ alreadyMatched++;
+ break;
+ }
+ }
+ }
+ }
+
+ return fileContents;
+ }
+
+ ///
+ /// Minifies the given HTML string.
+ ///
+ /// The html to minify.
+ /// The features
+ ///
+ /// The .
+ ///
+ public static string MinifyHtml(string htmlContents, Features features)
+ {
+ // First, remove all JavaScript comments
+ if (!features.IgnoreJsComments)
+ {
+ htmlContents = RemoveJavaScriptComments(htmlContents);
+ }
+
+ // Minify the string
+ htmlContents = Regex.Replace(htmlContents, @"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/", "");
+
+ // Replace line comments
+ htmlContents = Regex.Replace(htmlContents, @"// (.*?)\r?\n", "", RegexOptions.Singleline);
+
+ // Replace spaces between quotes
+ htmlContents = Regex.Replace(htmlContents, @"\s+", " ");
+
+ // Replace line breaks
+ htmlContents = Regex.Replace(htmlContents, @"\s*\n\s*", "\n");
+
+ // Replace spaces between brackets
+ htmlContents = Regex.Replace(htmlContents, @"\s*\>\s*\<\s*", "><");
+
+ // Replace comments
+ if (!features.IgnoreHtmlComments)
+ {
+ htmlContents = Regex.Replace(htmlContents, @"", "");
+ }
+
+ // single-line doctype must be preserved
+ var firstEndBracketPosition = htmlContents.IndexOf(">", StringComparison.Ordinal);
+ if (firstEndBracketPosition >= 0)
+ {
+ htmlContents = htmlContents.Remove(firstEndBracketPosition, 1);
+ htmlContents = htmlContents.Insert(firstEndBracketPosition, ">");
+ }
+
+ return htmlContents.Trim();
+ }
+
+ ///
+ /// Removes any JavaScript Comments in a script block
+ ///
+ ///
+ /// A string with all JS comments removed
+ public static string RemoveJavaScriptComments(string javaScriptComments)
+ {
+ // Remove JavaScript comments
+ Regex extractScripts = new Regex(@"");
+
+ // Loop through the script blocks
+ foreach (Match match in extractScripts.Matches(javaScriptComments))
+ {
+ var scriptBlock = match.Value;
+
+ javaScriptComments = javaScriptComments.Replace(scriptBlock, Regex.Replace(scriptBlock, @"[^:|""|']//(.*?)\r?\n", ""));
+
+ }
+
+ return javaScriptComments;
+ }
+
+ ///
+ /// Ensure that the max character count is less than 65K.
+ /// If so, break onto the next line.
+ ///
+ /// The minified HTML
+ /// The features
+ /// A html string
+ public static string EnsureMaxLength(string htmlContents, Features features)
+ {
+ if (features.MaxLength > 0)
+ {
+ int htmlLength = htmlContents.Length;
+ int currentMaxLength = features.MaxLength;
+ int position;
+
+ while (htmlLength > currentMaxLength)
+ {
+ position = htmlContents.LastIndexOf("><", currentMaxLength);
+ htmlContents = htmlContents.Substring(0, position + 1) + "\r\n" + htmlContents.Substring(position + 1);
+ currentMaxLength += features.MaxLength;
+ }
+ }
+ return htmlContents;
+ }
+ }
+}
diff --git a/ViewMinifier/StringExtension.cs b/ViewMinifier/StringExtension.cs
new file mode 100644
index 0000000..a65b3e9
--- /dev/null
+++ b/ViewMinifier/StringExtension.cs
@@ -0,0 +1,17 @@
+namespace HtmlMinifier
+{
+ public static class StringExtension
+ {
+ public static bool IsHtmlFile(this string value)
+ {
+ var file = value.ToLower();
+ return file.EndsWith(".cshtml") ||
+ file.EndsWith(".vbhtml") ||
+ file.EndsWith(".aspx") ||
+ file.EndsWith(".html") ||
+ file.EndsWith(".htm") ||
+ file.EndsWith(".ascx") ||
+ file.EndsWith(".master");
+ }
+ }
+}