Skip to content

Instantly share code, notes, and snippets.

@dotnetchris
Created August 20, 2014 18:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dotnetchris/3d1e4fe9b0fa77eefc82 to your computer and use it in GitHub Desktop.
Save dotnetchris/3d1e4fe9b0fa77eefc82 to your computer and use it in GitHub Desktop.
public class StyleRelativePathTransform : IBundleTransform
{
private const string UrlPattern = @"url\s*\(\s*([""']?)([^:)]+)\1\s*\)";
private static readonly Regex UrlRegex = new Regex(UrlPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
public void Process(BundleContext context, BundleResponse response)
{
var mergedCss = new StringBuilder();
// open each of the files
foreach (var cssFileInfo in response.Files)
{
if (cssFileInfo.Exists.Equals(false)) continue;
string cssFileContents = File.ReadAllText(cssFileInfo.FullName);
// apply the RegEx to the file (to change relative paths)
var matches = UrlRegex.Matches(cssFileContents);
// Ignore the file if no match
if (matches.Count > 0)
{
string cssDirectoryPath = cssFileInfo.DirectoryName;
string cssVirtualPath = context.RelativeUrlFrom(cssDirectoryPath);
foreach (Match match in matches)
{
string quoteDelimiter = match.Groups[1].Value; //url('') vs url("")
string relativePathToCss = match.Groups[2].Value;
//prevent querystring from causing error
var pathAndQuery = relativePathToCss.Split(new[] {'?'}, 2, StringSplitOptions.RemoveEmptyEntries);
var pathOnly = pathAndQuery[0];
var queryOnly = pathAndQuery.Length == 2 ? pathAndQuery[1] : string.Empty;
string absolutePath = GetAbsolutePath(cssDirectoryPath, pathOnly);
string serverRelativeUrl = context.RelativeUrlFrom(absolutePath, queryOnly);
string replace = String.Format("url({0}{1}{0})", quoteDelimiter, serverRelativeUrl);
cssFileContents = cssFileContents.Replace(match.Groups[0].Value, replace);
}
}
mergedCss.AppendLine(cssFileContents);
}
response.Content = mergedCss.ToString();
}
private static string GetAbsolutePath(string cssFilePath, string pathOnly)
{
return Path.GetFullPath(Path.Combine(cssFilePath, pathOnly));
}
}
public static class BundleContextExtension
{
/// <summary>
/// Converts the absolutePath to the server relative URL.
/// </summary>
/// <param name="bundleContext">The bundle context.</param>
/// <param name="absolutePath">The absolute path.</param>
/// <param name="queryOnly">[Optional] The query string.</param>
/// <returns></returns>
public static string RelativeUrlFrom(this BundleContext bundleContext, string absolutePath, string queryOnly = null)
{
var request = bundleContext.HttpContext.Request;
var applicationPath = request.PhysicalApplicationPath;
var virtualDir = request.ApplicationPath.EndsIn('/');
string serverRelativePath = absolutePath.Replace(applicationPath, virtualDir).Replace(@"\", "/");
if (string.IsNullOrEmpty(queryOnly)) return serverRelativePath;
string serverRelativeUrl = serverRelativePath + "?" + queryOnly.TrimStart(new[] {'?'});
return serverRelativeUrl;
}
}
public static class StringExtension
{
/// <summary>
/// Removes all trailing occurrences of a set of characters specified in an array from the current
/// <see cref="T:System.String" /> object.
/// </summary>
/// <param name="str">The string.</param>
/// <param name="chars">The chars.</param>
/// <returns></returns>
public static string TrimEndSafe(this string str, params char[] chars)
{
if (string.IsNullOrEmpty(str)) return str;
return str.TrimEnd(chars);
}
/// <summary>
/// Forces the string to end with the specified char. Will not allow duplicated end characters
/// </summary>
/// <param name="str">The string. If null will return @char</param>
/// <param name="char">The character.</param>
/// <returns></returns>
public static string EndsIn(this string str, char @char)
{
return str.TrimEndSafe(@char) + @char;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment