Part of Slepp's ProjectsPastebinTURLImagebinFilebin
Feedback -- English French German Japanese
Create Upload Newest Tools Donate
Sign In | Create Account

Dynamic CSS Processor
Saturday, January 24th, 2009 at 7:57:34pm MST 

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using System.Web;
  8. using System.Web.UI;
  9.  
  10. public class StyleSheetHandler : IHttpHandler
  11. {
  12.     protected Dictionary<StylesheetCacheKey, string> _cache; // Memory cache of processed stylesheets.
  13.  
  14.     public StyleSheetHandler()
  15.     {
  16.         _cache = new Dictionary<StylesheetCacheKey, string>();
  17.     }
  18.    
  19.     public bool IsReusable
  20.     {
  21.         get { return true; }
  22.     }
  23.  
  24.     public void ProcessRequest(HttpContext context)
  25.     {
  26.         var rawFileName = context.Request.PhysicalPath;
  27.         var rawFileLastModified = File.GetLastWriteTimeUtc(rawFileName);
  28.         var browserName = context.Request.Browser.Browser;
  29.         var browserVersion = new Version(context.Request.Browser.Version);
  30.         var newCacheKey = new StylesheetCacheKey(rawFileName, rawFileLastModified, browserName, browserVersion);
  31.  
  32.         // Check for entry in cache of processed contents of stylesheets that is at least as recent as last
  33.         // modified time of raw file.
  34.         var cacheEntry = _cache.SingleOrDefault(entry => entry.Key.Equals(newCacheKey) &&
  35.             entry.Key.RawFileLastModified >= newCacheKey.RawFileLastModified);
  36.         string processedContent;
  37.         if (cacheEntry.Value != null)
  38.         {
  39.             // Found entry in cache.
  40.             processedContent = cacheEntry.Value;
  41.         }
  42.         else
  43.         {
  44.             // Process raw content of stylesheet file.
  45.             var rawContent = File.ReadAllText(rawFileName);
  46.             processedContent = ProcessStylesheet(rawContent, browserName, browserVersion);
  47.  
  48.             // Add processed stylesheet to cache.
  49.             _cache[newCacheKey] = processedContent;
  50.         }
  51.  
  52.         // Write process content of stylesheet to response stream.
  53.         context.Response.ContentType = "text/css";
  54.         context.Response.Output.Write(processedContent);
  55.         context.Response.Flush();
  56.     }
  57.  
  58.     protected string ProcessStylesheet(string content, string browserName, Version browserVersion)
  59.     {
  60.         // Matches blocks (regions) of commented text (of form "/*{comment}*/").
  61.         var regexComments = new Regex(@"/\*.+?\*/", RegexOptions.Singleline);
  62.         // Matches browser-conditional tags of form "[[?{browser condition} {true result} | {false result}]]".
  63.         var regexTags = new Regex(@"\[\?(?<browser>.+?) +?(?<trueResult>.*?) ?\| ?(?<falseResult>.*?) *?\]",
  64.             RegexOptions.None);
  65.  
  66.         // Evaluate tags within each comment block.
  67.         var processedContent = regexComments.Replace(content, new MatchEvaluator(blockMatch =>
  68.             {
  69.                 var tagCount = 0;
  70.                 var evaluatedText = regexTags.Replace(blockMatch.Value, new MatchEvaluator(tagMatch =>
  71.                 {
  72.                     tagCount++;
  73.  
  74.                     // Invert condition if preceded by '!'.
  75.                     var browser = tagMatch.Groups["browser"].Value;
  76.                     bool inverted = false;
  77.                     if (browser[0] == '!')
  78.                     {
  79.                         inverted = true;
  80.                         browser = browser.Substring(1);
  81.                     }
  82.                     return (string.Equals(browser, browserName, StringComparison.InvariantCultureIgnoreCase) ^
  83.                         inverted) ? tagMatch.Groups["trueResult"].Value : tagMatch.Groups["falseResult"].Value;
  84.                 }));
  85.                 return (tagCount > 0) ? evaluatedText.Substring(2, evaluatedText.Length - 4) : blockMatch.Value;
  86.             }));
  87.  
  88.         return processedContent;
  89.     }
  90.  
  91.     protected struct StylesheetCacheKey
  92.     {
  93.         public string StylesheetPath;        // Path of raw stylesheet file.
  94.         public DateTime RawFileLastModified; // Time at which raw file was last modified.
  95.         public string BrowserName;           // Name of browser.
  96.         public Version BrowserVersion;       // Version of browser.
  97.  
  98.         public StylesheetCacheKey(string stylesheetPath, DateTime rawFileLastModified, string browserName,
  99.             Version browserVersion)
  100.         {
  101.             this.StylesheetPath = stylesheetPath;
  102.             this.RawFileLastModified = rawFileLastModified;
  103.             this.BrowserName = browserName;
  104.             this.BrowserVersion = browserVersion;
  105.         }
  106.  
  107.         public override bool Equals(object obj)
  108.         {
  109.             var objKey = (StylesheetCacheKey)obj;
  110.             return this.StylesheetPath.Equals(objKey.StylesheetPath) &&
  111.                 this.BrowserName.Equals(objKey.BrowserName) && this.BrowserVersion.Equals(objKey.BrowserVersion);
  112.         }
  113.  
  114.         public override int GetHashCode()
  115.         {
  116.             return this.StylesheetPath.GetHashCode() ^ this.BrowserName.GetHashCode() ^
  117.                 this.BrowserVersion.GetHashCode();
  118.         }
  119.  
  120.         public override string ToString()
  121.         {
  122.             return string.Format("{0}, {1} {2}", this.StylesheetPath, this.BrowserName,
  123.                 this.BrowserVersion.ToString());
  124.         }
  125.     }
  126. }

advertising

Update the Post

Either update this post and resubmit it with changes, or make a new post.

You may also comment on this post.

update paste below
details of the post (optional)

Note: Only the paste content is required, though the following information can be useful to others.

Save name / title?

(space separated, optional)



Please note that information posted here will expire by default in one month. If you do not want it to expire, please set the expiry time above. If it is set to expire, web search engines will not be allowed to index it prior to it expiring. Items that are not marked to expire will be indexable by search engines. Be careful with your passwords. All illegal activities will be reported and any information will be handed over to the authorities, so be good.

worth-right worth-right