How to Compress Css and JavaScript Runtime

2

In this time CMSN Software Tutorials has decided to provide a very useful tutorial. "How to Compress Css and JavaScript Runtime" shows you how to compress, combined, compact and cache Style Sheets and JavaScript files.

Following sample show you how to use Compact, Combine, Compress and Cache for Style sheets and JavaScripts files.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="How to compress.aspx.cs"
    Inherits="CMSN.Software.Tutorials.HowToCompressCssAndJs.HowToCompress" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>How to Compress Css and JavaScript Runtime</title>
    <link href="/HttpCompress.ashx?files=Css/jquery-ui.css&v=1" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="/HttpCompress.ashx?files=Scripts/jquery-1.4.1.js,Scripts/SampleScript.js&v=1"></script>
</head>
<body>
    <form id="form1" runat="server">
    <div class="samplePlaceHolder ui-dialog">
    </div>
    </form>
</body>
</html>

We can use the Style sheets and JavaScript files as usual. only different here is we need to call those files through HttpCompress.ashx. If there are multiple files we need to call those files separated by coma. parameter "v" is for the version of the file. If we need to load un cached version we can just change the file version.

Following code shows how to implement the Compact, Combine, Compress and Cache for Style sheets and JavaScript files.
//-----------------------------------------------------------------------
// <copyright file="HttpCompress.ashx.cs" company="CMSN Software">
//    Copyright © 2010  CMSN Software
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see http://www.gnu.org/licenses.
// </copyright>
//-----------------------------------------------------------------------
 
namespace CMSN.Software.Tutorials.HowToCompressCssAndJs
{
    using System;
    using System.Globalization;
    using System.IO;
    using System.IO.Compression;
    using System.Text;
    using System.Web;
    using System.Web.Caching;
 
    /// <summary>
    /// compact combine cache and compress css and JavaScript files at runtime.
    /// </summary>
    public class HttpCompress : IHttpHandler
    {
        /// <summary>
        /// Default cache duration
        /// </summary>
        private static readonly TimeSpan CacheDuration = TimeSpan.FromDays(30);
 
        /// <summary>
        /// file names from query string
        /// </summary>
        private string fileNames;
 
        /// <summary>
        /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance.
        /// </summary>
        /// <returns>true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false.</returns>
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
        public void ProcessRequest(HttpContext context)
        {
            string cacheKeyName = context.Request.Url.PathAndQuery;
            this.fileNames = context.Request.QueryString["files"] ?? string.Empty;
            string[] relativeFiles = this.fileNames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            string[] absoluteFiles = new string[relativeFiles.Length];
            StringBuilder combinedScript = new StringBuilder();
            bool isCss;
 
            if (relativeFiles.Length > 0 && relativeFiles[0].EndsWith(".css"StringComparison.OrdinalIgnoreCase))
            {
                isCss = true;
                context.Response.ContentType = "text/css";
            }
            else
            {
                isCss = false;
                context.Response.ContentType = "application/x-javascript";
            }
 
            for (int i = 0; i < relativeFiles.Length; i++)
            {
                string absoluteFile = context.Server.MapPath("~/" + relativeFiles[i]);
                combinedScript.Append(MinifyContent(absoluteFile, isCss));
                absoluteFiles[i] = absoluteFile;
            }
 
            if (context.Cache[CacheKey(cacheKeyName, CompressionType(context))] != null)
            {
                byte[] responseBytes = context.Cache[CacheKey(cacheKeyName, CompressionType(context))] as byte[];
                this.WriteContent(responseBytes, context, CompressionType(context), cacheKeyName, absoluteFiles);
            }
            else
            {
                CacheDependency dependency = new CacheDependency(absoluteFiles);
                string compressionType = CompressionType(context);
                if (compressionType == "deflate")
                {
                    using (MemoryStream stream = new MemoryStream())
                    {
                        using (StreamWriter writer = new StreamWriter(new DeflateStream(stream, CompressionMode.Compress), Encoding.UTF8))
                        {
                            writer.Write(combinedScript);
                        }
 
                        byte[] buffer = stream.ToArray();
 
                        context.Cache.Insert(
                            CacheKey(cacheKeyName, compressionType),
                            buffer,
                            dependency,
                            System.Web.Caching.Cache.NoAbsoluteExpiration,
                            CacheDuration);
 
                        this.WriteContent(buffer, context, compressionType, cacheKeyName, absoluteFiles);
                    }
                }
                else if (compressionType == "gzip")
                {
                    using (MemoryStream stream = new MemoryStream())
                    {
                        using (StreamWriter writer = new StreamWriter(new GZipStream(stream, CompressionMode.Compress), Encoding.UTF8))
                        {
                            writer.Write(combinedScript);
                        }
 
                        byte[] buffer = stream.ToArray();
                        context.Cache.Insert(
                            CacheKey(cacheKeyName, compressionType),
                            buffer,
                            dependency,
                            System.Web.Caching.Cache.NoAbsoluteExpiration,
                            CacheDuration);
                        this.WriteContent(buffer, context, compressionType, cacheKeyName, absoluteFiles);
                    }
                }
                else
                {
                    context.Cache.Insert(
                        CacheKey(cacheKeyName, compressionType),
                        combinedScript,
                        dependency,
                        System.Web.Caching.Cache.NoAbsoluteExpiration,
                        CacheDuration);
 
                    context.Response.AddHeader("Content-Length", combinedScript.Length.ToString(CultureInfo.InvariantCulture));
                    context.Response.Write(combinedScript);
                }
            }
        }
 
        /// <summary>
        /// Gets the compression type that the browser supports
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>
        /// none, deflate, or gzip
        /// </returns>
        protected static string CompressionType(HttpContext context)
        {
            string compressionType = "none";
            string encodingTypes = string.Empty;
            encodingTypes = context.Request.Headers["Accept-Encoding"];
 
            if (!string.IsNullOrEmpty(encodingTypes))
            {
                encodingTypes = encodingTypes.ToLower(CultureInfo.InvariantCulture);
                if (context.Request.Browser.Browser == "IE")
                {
                    if (context.Request.Browser.MajorVersion < 6)
                    {
                        compressionType = "none";
                    }
                    else if (context.Request.Browser.MajorVersion == 6 &&
                        !string.IsNullOrEmpty(context.Request.ServerVariables["HTTP_USER_AGENT"]) &&
                        context.Request.ServerVariables["HTTP_USER_AGENT"].Contains("EV1"))
                    {
                        compressionType = "none";
                    }
                }
 
                if (encodingTypes.Contains("deflate") || encodingTypes.Contains("*"))
                {
                    compressionType = "deflate";
                }
                else if (encodingTypes.Contains("gzip") || encodingTypes.Contains("x-gzip"))
                {
                    compressionType = "gzip";
                }
            }
 
            return compressionType;
        }
 
        /// <summary>
        /// Cache key for the HttpCompress module.
        /// </summary>
        /// <param name="key">default cache key.</param>
        /// <param name="compressionType">Type of the compression.</param>
        /// <returns>Cache key for the HttpCompress module</returns>
        protected static string CacheKey(string key, string compressionType)
        {
            return "HttpCompress." + key + "." + compressionType;
        }
 
        /// <summary>
        /// Sets the response cache.
        /// </summary>
        /// <param name="response">response of the current context</param>
        /// <param name="files">absolute file paths.</param>
        protected void SetResponseCache(HttpResponse response, string[] files)
        {
            response.AddFileDependencies(files);
            HttpCachePolicy browserCache = response.Cache;
            DateTime modifiedTime = DateTime.Now;
            browserCache.SetCacheability(HttpCacheability.Public);
            browserCache.VaryByParams["files"] = true;
            browserCache.VaryByParams["v"] = true;
            browserCache.SetOmitVaryStar(true);
            browserCache.SetExpires(modifiedTime.AddDays(7));
            browserCache.SetValidUntilExpires(true);
            browserCache.SetLastModified(modifiedTime);
            browserCache.SetETagFromFileDependencies();
            browserCache.SetLastModifiedFromFileDependencies();
        }
 
        /// <summary>
        /// Writes the bytes.
        /// </summary>
        /// <param name="fileContent">The bytes.</param>
        /// <param name="context">The context.</param>
        /// <param name="compressionType">Type of the compression.</param>
        /// <param name="cacheKey">The cache key.</param>
        /// <param name="files">The files.</param>
        protected void WriteContent(byte[] fileContent, HttpContext context, string compressionType, string cacheKey, string[] files)
        {
            HttpResponse response = context.Response;
 
            this.SetResponseCache(response, files);
 
            if (compressionType != "none")
            {
                response.AddHeader("Content-encoding", compressionType);
                response.OutputStream.Write(fileContent, 0, fileContent.Length);
            }
            else
            {
                string uncompressedScript = context.Cache[CacheKey(cacheKey, compressionType)].ToString();
                response.AddHeader("Content-Length", uncompressedScript.Length.ToString(CultureInfo.InvariantCulture));
                response.Write(uncompressedScript);
            }
        }
 
        /// <summary>
        /// minify individual file content.
        /// </summary>
        /// <param name="file">css or JavaScript file name.</param>
        /// <param name="isCss">if set to <c>true</c> [is CSS].</param>
        /// <returns>
        /// minified file content
        /// </returns>
        private static string MinifyContent(string file, bool isCss)
        {
            string compactContent = string.Empty;
            if (File.Exists(file))
            {
                using (StreamReader reader = new StreamReader(file))
                {
                    compactContent = StripWhitespace(reader.ReadToEnd(), isCss);
                }
            }
 
            return compactContent;
        }
 
        /// <summary>
        /// Strips the whitespace.
        /// </summary>
        /// <param name="rowContent">Content of the row.</param>
        /// <param name="isCss">if set to <c>true</c> [is CSS].</param>
        /// <returns>minified file content</returns>
        private static string StripWhitespace(string rowContent, bool isCss)
        {
            if (isCss)
            {
                rowContent = StyleSheetMinifier.CssMinify(rowContent);
            }
            else
            {
                JavaScriptMinifier javaScriptMinify = new JavaScriptMinifier();
                rowContent = javaScriptMinify.MinifyJavaScript(rowContent);
            }
 
            return rowContent;
        }
    }
}

In above code Http Handler will read and compact files one by one and combined it to one file. Then it will cache in the server side. This cache is depend on the file content. When file modification detected cache will invalidate. Next part is based on the browser support it will compress using deflate or GZip. Since GZip is simply deflate plus a checksum and header/footer we gave more priority to deflate. Finally it will cache in the client browser. This cache will depend on the file names and the file version mention in the URL.

Following code containing methods to compact Style sheets and JavaScript files.
using System;
using System.IO;
using System.Text;
 
/* jsmin.c
   2007-05-22
 
Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
 
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
The Software shall be used for Good, not Evil.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
 
namespace CMSN.Software.Tutorials.HowToCompressCssAndJs
{
    public class JavaScriptMinifier
    {
        private const int EndOfFile = -1;
 
        StreamReader streamReader;
        StreamWriter streamWriter;
        int firstCharacter;
        int nextCharacter;
        int theLookahead = EndOfFile;
 
        public string MinifyJavaScript(string src)
        {
            MemoryStream srcStream = new MemoryStream(Encoding.Unicode.GetBytes(src));
            MemoryStream tgStream = new MemoryStream(8092);
 
            using (streamReader = new StreamReader(srcStream, Encoding.Unicode))
            {
                using (streamWriter = new StreamWriter(tgStream, Encoding.Unicode))
                {
                    JavaScriptMinify();
                }
            }
 
            return Encoding.Unicode.GetString(tgStream.ToArray());
        }
 
        /// <summary>
        /// Copy the input to the output, deleting the characters which are
        /// insignificant to JavaScript. Comments will be removed. Tabs will be
        /// replaced with spaces. Carriage returns will be replaced with linefeeds.
        /// Most spaces and linefeeds will be removed.
        /// </summary>
        private void JavaScriptMinify()
        {
            firstCharacter = '\n';
            JavaScriptMinifyAction(3);
            while (firstCharacter != EndOfFile)
            {
                switch (firstCharacter)
                {
                    case ' ':
                        {
                            if (isAlphanum(nextCharacter))
                            {
                                JavaScriptMinifyAction(1);
                            }
                            else
                            {
                                JavaScriptMinifyAction(2);
                            }
 
                            break;
                        }
                    case '\n':
                        {
                            switch (nextCharacter)
                            {
                                case '{':
                                case '[':
                                case '(':
                                case '+':
                                case '-':
                                    {
                                        JavaScriptMinifyAction(1);
                                        break;
                                    }
                                case ' ':
                                    {
                                        JavaScriptMinifyAction(3);
                                        break;
                                    }
                                default:
                                    {
                                        if (isAlphanum(nextCharacter))
                                        {
                                            JavaScriptMinifyAction(1);
                                        }
                                        else
                                        {
                                            JavaScriptMinifyAction(2);
                                        }
                                        break;
                                    }
                            }
                            break;
                        }
                    default:
                        {
                            switch (nextCharacter)
                            {
                                case ' ':
                                    {
                                        if (isAlphanum(firstCharacter))
                                        {
                                            JavaScriptMinifyAction(1);
                                            break;
                                        }
                                        JavaScriptMinifyAction(3);
                                        break;
                                    }
                                case '\n':
                                    {
                                        switch (firstCharacter)
                                        {
                                            case '}':
                                            case ']':
                                            case ')':
                                            case '+':
                                            case '-':
                                            case '"':
                                            case '\'':
                                                {
                                                    JavaScriptMinifyAction(1);
                                                    break;
                                                }
                                            default:
                                                {
                                                    if (isAlphanum(firstCharacter))
                                                    {
                                                        JavaScriptMinifyAction(1);
                                                    }
                                                    else
                                                    {
                                                        JavaScriptMinifyAction(3);
                                                    }
                                                    break;
                                                }
                                        }
                                        break;
                                    }
                                default:
                                    {
                                        JavaScriptMinifyAction(1);
                                        break;
                                    }
                            }
                            break;
                        }
                }
            }
        }
 
        /// <summary>
        /// JavaScript minify action.
        /// </summary>
        /// <param name="actionNumber">action number</param>
        private void JavaScriptMinifyAction(int actionNumber)
        {
            if (actionNumber <= 1)
            {
                put(firstCharacter);
            }
            if (actionNumber <= 2)
            {
                firstCharacter = nextCharacter;
                if (firstCharacter == '\'' || firstCharacter == '"')
                {
                    for (; ; )
                    {
                        put(firstCharacter);
                        firstCharacter = get();
                        if (firstCharacter == nextCharacter)
                        {
                            break;
                        }
                        if (firstCharacter <= '\n')
                        {
                            throw new FormatException(string.Format("Error: JSMIN unterminated string literal: {0}\n", firstCharacter));
                        }
                        if (firstCharacter == '\\')
                        {
                            put(firstCharacter);
                            firstCharacter = get();
                        }
                    }
                }
            }
            if (actionNumber <= 3)
            {
                nextCharacter = NextCharacter();
                if (nextCharacter == '/' && (firstCharacter == '(' || firstCharacter == ',' || firstCharacter == '=' ||
                                    firstCharacter == '[' || firstCharacter == '!' || firstCharacter == ':' ||
                                    firstCharacter == '&' || firstCharacter == '|' || firstCharacter == '?' ||
                                    firstCharacter == '{' || firstCharacter == '}' || firstCharacter == ';' ||
                                    firstCharacter == '\n'))
                {
                    put(firstCharacter);
                    put(nextCharacter);
                    for (; ; )
                    {
                        firstCharacter = get();
                        if (firstCharacter == '/')
                        {
                            break;
                        }
                        else if (firstCharacter == '\\')
                        {
                            put(firstCharacter);
                            firstCharacter = get();
                        }
                        else if (firstCharacter <= '\n')
                        {
                            throw new Exception(string.Format("Error: JSMIN unterminated Regular Expression literal : {0}.\n", firstCharacter));
                        }
                        put(firstCharacter);
                    }
                    nextCharacter = NextCharacter();
                }
            }
        }
 
        /* next -- get the next character, excluding comments. peek() is used to see
                if a '/' is followed by a '/' or '*'.
        */
 
        private int NextCharacter()
        {
            int c = get();
            if (c == '/')
            {
                switch (peek())
                {
                    case '/':
                        {
                            for (; ; )
                            {
                                c = get();
                                if (c <= '\n')
                                {
                                    return c;
                                }
                            }
                        }
                    case '*':
                        {
                            get();
                            for (; ; )
                            {
                                switch (get())
                                {
                                    case '*':
                                        {
                                            if (peek() == '/')
                                            {
                                                get();
                                                return ' ';
                                            }
                                            break;
                                        }
                                    case EndOfFile:
                                        {
                                            throw new Exception("Error: JSMIN Unterminated comment.\n");
                                        }
                                }
                            }
                        }
                    default:
                        {
                            return c;
                        }
                }
            }
            return c;
        }
 
        /* peek -- get the next character without getting it.
        */
 
        private int peek()
        {
            theLookahead = get();
            return theLookahead;
        }
 
        /* get -- return the next character from stdin. Watch out for look ahead. If
                the character is a control character, translate it to a space or
                linefeed.
        */
 
        private int get()
        {
            int c = theLookahead;
            theLookahead = EndOfFile;
            if (c == EndOfFile)
            {
                c = streamReader.Read();
            }
            if (c >= ' ' || c == '\n' || c == EndOfFile)
            {
                return c;
            }
            if (c == '\r')
            {
                return '\n';
            }
            return ' ';
        }
 
        private void put(int c)
        {
            streamWriter.Write((char)c);
        }
 
        /* isAlphanum -- return true if the character is a letter, digit, underscore,
                dollar sign, or non-ASCII character.
        */
 
        private bool isAlphanum(int c)
        {
            return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
                (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
                c > 126);
        }
    }
}

Above code shows how to compact JavaScript files. This is not developed by CMSN Software. This code is part of the jsmin library.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
 
namespace CMSN.Software.Tutorials.HowToCompressCssAndJs
{
    public static class StyleSheetMinifier
    {
        public static int AppendReplacement(this Match match, StringBuilder sb, string input, string replacement, int index)
        {
            var preceding = input.Substring(index, match.Index - index);
 
            sb.Append(preceding);
            sb.Append(replacement);
 
            return match.Index + match.Length;
        }
 
        public static void AppendTail(this Match match, StringBuilder sb, string input, int index)
        {
            sb.Append(input.Substring(index));
        }
 
        public static uint ToUInt32(this ValueType instance)
        {
            return Convert.ToUInt32(instance);
        }
 
        public static string RegexReplace(this string input, string pattern, string replacement)
        {
            return Regex.Replace(input, pattern, replacement);
        }
 
        public static string RegexReplace(this string input, string pattern, string replacement, RegexOptions options)
        {
            return Regex.Replace(input, pattern, replacement, options);
        }
 
        public static string Fill(this string format, params object[] args)
        {
            return String.Format(format, args);
        }
 
        public static string RemoveRange(this string input, int startIndex, int endIndex)
        {
            return input.Remove(startIndex, endIndex - startIndex);
        }
 
        public static bool EqualsIgnoreCase(this string left, string right)
        {
            return String.Compare(left, right, true) == 0;
        }
 
        public static string ToHexString(this int value)
        {
            var sb = new StringBuilder();
            var input = value.ToString();
 
            foreach (char digit in input)
            {
                sb.Append("{0:x2}".Fill(digit.ToUInt32()));
            }
 
            return sb.ToString();
        }
 
        #region YUI Compressor's CssMin originally written by Isaac Schlueter
 
        /// <summary>
        /// Minifies CSS.
        /// </summary>
        /// <param name="css">The CSS content to minify.</param>
        /// <returns>Minified CSS content.</returns>
        public static string CssMinify(this string css)
        {
            return CssMinify(css, 0);
        }
 
        /// <summary>
        /// Minifies CSS with a column width maximum.
        /// </summary>
        /// <param name="css">The CSS content to minify.</param>
        /// <param name="columnWidth">The maximum column width.</param>
        /// <returns>Minified CSS content.</returns>
        public static string CssMinify(this string css, int columnWidth)
        {
            css = css.RemoveCommentBlocks();
            css = css.RegexReplace("\\s+"" ");
            css = css.RegexReplace("\"\\\\\"}\\\\\"\"""___PSEUDOCLASSBMH___");
            css = css.RemovePrecedingSpaces();
            css = css.RegexReplace("([!{}:;>+\\(\\[,])\\s+""$1");
            css = css.RegexReplace("([^;\\}])}""$1;}");
            css = css.RegexReplace("([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)""$1$2");
            css = css.RegexReplace(":0 0 0 0;"":0;");
            css = css.RegexReplace(":0 0 0;"":0;");
            css = css.RegexReplace(":0 0;"":0;");
            css = css.RegexReplace("background-position:0;""background-position:0 0;");
            css = css.RegexReplace("(:|\\s)0+\\.(\\d+)""$1.$2");
            css = css.ShortenRgbColors();
            css = css.ShortenHexColors();
            css = css.RegexReplace("[^\\}]+\\{;\\}""");
 
            if (columnWidth > 0)
            {
                css = css.BreakLines(columnWidth);
            }
 
            css = css.RegexReplace("___PSEUDOCLASSBMH___""\"\\\\\"}\\\\\"\"");
            css = css.Trim();
 
            return css;
        }
 
        private static string RemoveCommentBlocks(this string input)
        {
            var startIndex = 0;
            var endIndex = 0;
            var iemac = false;
 
            startIndex = input.IndexOf(@"/*", startIndex);
            while (startIndex >= 0)
            {
                endIndex = input.IndexOf(@"*/", startIndex + 2);
                if (endIndex >= startIndex + 2)
                {
                    if (input[endIndex - 1] == '\\')
                    {
                        startIndex = endIndex + 2;
                        iemac = true;
                    }
                    else if (iemac)
                    {
                        startIndex = endIndex + 2;
                        iemac = false;
                    }
                    else
                    {
                        input = input.RemoveRange(startIndex, endIndex + 2);
                    }
                }
                startIndex = input.IndexOf(@"/*", startIndex);
            }
 
            return input;
        }
 
        private static string ShortenRgbColors(this string css)
        {
            var sb = new StringBuilder();
            Regex p = new Regex("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)");
            Match m = p.Match(css);
 
            int index = 0;
            while (m.Success)
            {
                string[] colors = m.Groups[1].Value.Split(',');
                StringBuilder hexcolor = new StringBuilder("#");
 
                foreach (string color in colors)
                {
                    int val = Int32.Parse(color);
                    if (val < 16)
                    {
                        hexcolor.Append("0");
                    }
                    hexcolor.Append(val.ToHexString());
                }
 
                index = m.AppendReplacement(sb, css, hexcolor.ToString(), index);
                m = m.NextMatch();
            }
 
            m.AppendTail(sb, css, index);
            return sb.ToString();
        }
 
        private static string ShortenHexColors(this string css)
        {
            var sb = new StringBuilder();
            Regex p = new Regex("([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])");
            Match m = p.Match(css);
 
            int index = 0;
            while (m.Success)
            {
                if (m.Groups[3].Value.EqualsIgnoreCase(m.Groups[4].Value) &&
                    m.Groups[5].Value.EqualsIgnoreCase(m.Groups[6].Value) &&
                    m.Groups[7].Value.EqualsIgnoreCase(m.Groups[8].Value))
                {
                    var replacement = String.Concat(m.Groups[1].Value, m.Groups[2].Value, "#", m.Groups[3].Value, m.Groups[5].Value, m.Groups[7].Value);
                    index = m.AppendReplacement(sb, css, replacement, index);
                }
                else
                {
                    index = m.AppendReplacement(sb, css, m.Value, index);
                }
 
                m = m.NextMatch();
            }
 
            m.AppendTail(sb, css, index);
            return sb.ToString();
        }
 
        private static string RemovePrecedingSpaces(this string css)
        {
            var sb = new StringBuilder();
            Regex p = new Regex("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)");
            Match m = p.Match(css);
 
            int index = 0;
            while (m.Success)
            {
                var s = m.Value;
                s = s.RegexReplace(":""___PSEUDOCLASSCOLON___");
 
                index = m.AppendReplacement(sb, css, s, index);
                m = m.NextMatch();
            }
            m.AppendTail(sb, css, index);
 
            var result = sb.ToString();
            result = result.RegexReplace("\\s+([!{};:>+\\(\\)\\],])""$1");
            result = result.RegexReplace("___PSEUDOCLASSCOLON___"":");
 
            return result;
        }
 
        private static string BreakLines(this string css, int columnWidth)
        {
            int i = 0;
            int start = 0;
 
            var sb = new StringBuilder(css);
            while (i < sb.Length)
            {
                var c = sb[i++];
                if (c == '}' && i - start > columnWidth)
                {
                    sb.Insert(i, '\n');
                    start = i;
                }
            }
            return sb.ToString();
        }
        #endregion
    }
}

Above code shows how to compact Style sheet files. This is not developed by CMSN Software. This code is part of the YUI Compressor library.

3.c
Download tutorial

2 comments:

Post a Comment