private string ShortenRgbColors(string css)
        {
            StringBuilder stringBuilder = new StringBuilder();
            Regex pattern = new Regex("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)");
            Match match = pattern.Match(css);

            int index = 0;
            while (match.Success)
            {
                int value;
                string[] colors = match.Groups[1].Value.Split(',');
                StringBuilder hexcolor = new StringBuilder("#");

                foreach (string color in colors)
                {
                    if (!Int32.TryParse(color,
                        out value))
                    {
                        value = 0;
                    }

                    if (value < 16)
                    {
                        hexcolor.Append("0");
                    }

                    hexcolor.Append(value.ToHexString());
                }

                index = match.AppendReplacement(stringBuilder,
                    css,
                    hexcolor.ToString(),
                    index);
                match = match.NextMatch();
            }

            stringBuilder.AppendTail(css, index);

            return stringBuilder.ToString();
        }
        private string ShortenHexColors(string css)
        {
            StringBuilder stringBuilder = new StringBuilder();
            Regex pattern = 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 match = pattern.Match(css);

            int index = 0;
            while (match.Success)
            {
                if (match.Groups[3].Value.EqualsIgnoreCase(match.Groups[4].Value) &&
                    match.Groups[5].Value.EqualsIgnoreCase(match.Groups[6].Value) &&
                    match.Groups[7].Value.EqualsIgnoreCase(match.Groups[8].Value))
                {
                    var replacement = String.Concat(match.Groups[1].Value,
                        match.Groups[2].Value,
                        "#", match.Groups[3].Value,
                        match.Groups[5].Value,
                        match.Groups[7].Value);
                    index = match.AppendReplacement(stringBuilder,
                        css,
                        replacement,
                        index);
                }
                else
                {
                    index = match.AppendReplacement(stringBuilder,
                        css,
                        match.Value,
                        index);
                }

                match = match.NextMatch();
            }

            stringBuilder.AppendTail(css,
                index);

            return stringBuilder.ToString();
        }
        private string RemovePrecedingSpaces(string css)
        {
            StringBuilder stringBuilder = new StringBuilder();
            Regex pattern = new Regex("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)");
            Match match = pattern.Match(css);

            int index = 0;
            while (match.Success)
            {
                string s = match.Value;
                s = s.RegexReplace(":", "___PSEUDOCLASSCOLON___");

                index = match.AppendReplacement(stringBuilder,
                    css,
                    s,
                    index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css,
                index);

            string result = stringBuilder.ToString();
            result = result.RegexReplace("\\s+([!{};:>+\\(\\)\\],])", "$1");
            result = result.RegexReplace("___PSEUDOCLASSCOLON___", ":");

            return result;
        }
Example #4
0
        public static string Compress(string css, int columnWidth, bool removeComments)
        {
            if (string.IsNullOrEmpty(css))
            {
                throw new ArgumentNullException("css");
            }

            int totalLen = css.Length;
            int startIndex = 0;
            var comments = new ArrayList();
            var preservedTokens = new ArrayList();
            int max;


            if (removeComments)
            {
                while ((startIndex = css.IndexOf(@"/*", startIndex, StringComparison.OrdinalIgnoreCase)) >= 0)
                {
                    int endIndex = css.IndexOf(@"*/", startIndex + 2, StringComparison.OrdinalIgnoreCase);
                    if (endIndex < 0)
                    {
                        endIndex = totalLen;
                    }

                    // Note: java substring-length param = end index - 2 (which is end index - (startindex + 2))
                    string token = css.Substring(startIndex + 2, endIndex - (startIndex + 2));

                    comments.Add(token);

                    string newResult = css.Replace(startIndex + 2, endIndex,
                                                   "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + (comments.Count - 1) +
                                                   "___");

                    //var newResult = css.Substring(startIndex + 2, endIndex - (startIndex + 2)) + "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" +
                    //                (comments.Count - 1) + "___" + css.Substring(endIndex + 1);

                    startIndex += 2;
                    css = newResult;
                }
            }

            // Preserve strings so their content doesn't get accidently minified
            var stringBuilder = new StringBuilder();
            var pattern = new Regex("(\"([^\\\\\"]|\\\\.|\\\\)*\")|(\'([^\\\\\']|\\\\.|\\\\)*\')");
            Match match = pattern.Match(css);
            int index = 0;
            while (match.Success)
            {
                string text = match.Groups[0].Value;
                if (!string.IsNullOrEmpty(text))
                {
                    string token = match.Value;
                    char quote = token[0];

                    // Java code: token.substring(1, token.length() -1) .. but that's ...
                    //            token.substring(start index, end index) .. while .NET it's length for the 2nd arg.
                    token = token.Substring(1, token.Length - 2);

                    // Maybe the string contains a comment-like substring?
                    // one, maybe more? put'em back then.
                    if (token.IndexOf("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_") >= 0)
                    {
                        max = comments.Count;
                        for (int i = 0; i < max; i += 1)
                        {
                            token = token.Replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___",
                                                  comments[i].ToString());
                        }
                    }

                    // Minify alpha opacity in filter strings.
                    token = token.RegexReplace("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=",
                                               "alpha(opacity=");

                    preservedTokens.Add(token);
                    string preserver = quote + "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___" +
                                       quote;

                    index = match.AppendReplacement(stringBuilder, css, preserver, index);
                    match = match.NextMatch();
                }
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // Strings are safe, now wrestle the comments.
            max = comments.Count;
            for (int i = 0; i < max; i += 1)
            {
                string token = comments[i].ToString();
                string placeholder = "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___";

                // ! in the first position of the comment means preserve
                // so push to the preserved tokens while stripping the !
                if (token.StartsWith("!"))
                {
                    preservedTokens.Add(token);
                    css = css.Replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___");
                    continue;
                }

                // \ in the last position looks like hack for Mac/IE5
                // shorten that to /*\*/ and the next one to /**/
                if (token.EndsWith("\\"))
                {
                    preservedTokens.Add("\\");
                    css = css.Replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___");
                    i = i + 1; // attn: advancing the loop.
                    preservedTokens.Add(string.Empty);
                    css = css.Replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___",
                                      "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___");
                    continue;
                }

                // keep empty comments after child selectors (IE7 hack)
                // e.g. html >/**/ body
                if (token.Length == 0)
                {
                    startIndex = css.IndexOf(placeholder);
                    if (startIndex > 2)
                    {
                        if (css[startIndex - 3] == '>')
                        {
                            preservedTokens.Add(string.Empty);
                            css = css.Replace(placeholder,
                                              "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___");
                        }
                    }
                }

                // In all other cases kill the comment.
                css = css.Replace("/*" + placeholder + "*/", string.Empty);
            }

            // Normalize all whitespace strings to single spaces. Easier to work with that way.
            css = css.RegexReplace("\\s+", " ");

            // Remove the spaces before the things that should not have spaces before them.
            // But, be careful not to turn "p :link {...}" into "p:link{...}"
            // Swap out any pseudo-class colons with the token, and then swap back.
            stringBuilder = new StringBuilder();
            pattern = new Regex("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)");
            match = pattern.Match(css);
            index = 0;
            while (match.Success)
            {
                string text = match.Value;
                text = text.Replace(":", "___YUICSSMIN_PSEUDOCLASSCOLON___");
                text = text.Replace("\\\\", "\\\\\\\\");
                text = text.Replace("\\$", "\\\\\\$");
                index = match.AppendReplacement(stringBuilder, css, text, index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // Remove spaces before the things that should not have spaces before them.
            css = css.RegexReplace("\\s+([!{};:>+\\(\\)\\],])", "$1");

            // Bring back the colon.
            css = css.RegexReplace("___YUICSSMIN_PSEUDOCLASSCOLON___", ":");

            // Retain space for special IE6 cases.
            css = css.RegexReplace(":first\\-(line|letter)(\\{|,)", ":first-$1 $2");

            // no space after the end of a preserved comment.
            css = css.RegexReplace("\\*/ ", "*/");

            // If there is a @charset, then only allow one, and push to the top of the file.
            css = css.RegexReplace("^(.*)(@charset \"[^\"]*\";)", "$2$1");
            css = css.RegexReplace("^(\\s*@charset [^;]+;\\s*)+", "$1");

            // Put the space back in some cases, to support stuff like
            // @media screen and (-webkit-min-device-pixel-ratio:0){
            css = css.RegexReplace("\\band\\(", "and (");

            // Remove the spaces after the things that should not have spaces after them.
            css = css.RegexReplace("([!{}:;>+\\(\\[,])\\s+", "$1");

            // remove unnecessary semicolons.
            css = css.RegexReplace(";+}", "}");

            // Replace 0(px,em,%) with 0.
            css = css.RegexReplace("([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", "$1$2");

            // Replace 0 0 0 0; with 0.
            css = css.RegexReplace(":0 0 0 0(;|})", ":0$1");
            css = css.RegexReplace(":0 0 0(;|})", ":0$1");
            css = css.RegexReplace(":0 0(;|})", ":0$1");

            // Replace background-position:0; with background-position:0 0;
            // same for transform-origin
            stringBuilder = new StringBuilder();
            pattern =
                new Regex(
                    "(?i)(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin):0(;|})");
            match = pattern.Match(css);
            index = 0;
            while (match.Success)
            {
                index = match.AppendReplacement(stringBuilder, css,
                                                match.Groups[1].Value.ToLowerInvariant() + ":0 0" + match.Groups[2],
                                                index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // Replace 0.6 to .6, but only when preceded by : or a white-space.
            css = css.RegexReplace("(:|\\s)0+\\.(\\d+)", "$1.$2");

            // Shorten colors from rgb(51,102,153) to #336699
            // This makes it more likely that it'll get further compressed in the next step.
            stringBuilder = new StringBuilder();
            pattern = new Regex("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)");
            match = pattern.Match(css);
            index = 0;
            int value;
            while (match.Success)
            {
                string[] rgbcolors = match.Groups[1].Value.Split(',');
                var hexcolor = new StringBuilder("#");
                foreach (string rgbColour in rgbcolors)
                {
                    if (!Int32.TryParse(rgbColour, out value))
                    {
                        value = 0;
                    }

                    if (value < 16)
                    {
                        hexcolor.Append("0");
                    }
                    hexcolor.Append(value.ToHexString().ToLowerInvariant());
                }

                index = match.AppendReplacement(stringBuilder, css, hexcolor.ToString(), index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();


            // Shorten colors from #AABBCC to #ABC. Note that we want to make sure
            // the color is not preceded by either ", " or =. Indeed, the property
            //     filter: chroma(color="#FFFFFF");
            // would become
            //     filter: chroma(color="#FFF");
            // which makes the filter break in IE.
            stringBuilder = new StringBuilder();
            pattern =
                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 = pattern.Match(css);
            index = 0;
            while (match.Success)
            {
                // Test for AABBCC pattern.
                if (match.Groups[3].Value.EqualsIgnoreCase(match.Groups[4].Value) &&
                    match.Groups[5].Value.EqualsIgnoreCase(match.Groups[6].Value) &&
                    match.Groups[7].Value.EqualsIgnoreCase(match.Groups[8].Value))
                {
                    string replacement = String.Concat(match.Groups[1].Value, match.Groups[2].Value, "#",
                                                       match.Groups[3].Value, match.Groups[5].Value,
                                                       match.Groups[7].Value);
                    index = match.AppendReplacement(stringBuilder, css, replacement, index);
                }
                else
                {
                    index = match.AppendReplacement(stringBuilder, css, match.Value, index);
                }

                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // border: none -> border:0
            stringBuilder = new StringBuilder();
            pattern =
                new Regex("(?i)(border|border-top|border-right|border-bottom|border-right|outline|background):none(;|})");
            match = pattern.Match(css);
            index = 0;
            while (match.Success)
            {
                string replacement = match.Groups[1].Value.ToLowerInvariant() + ":0" + match.Groups[2].Value;
                index = match.AppendReplacement(stringBuilder, css, replacement, index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // Shorter opacity IE filter.
            css = css.RegexReplace("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=", "alpha(opacity=");

            // Remove empty rules.
            css = css.RegexReplace("[^\\}\\{/;]+\\{\\}", string.Empty);

            if (columnWidth >= 0)
            {
                // Some source control tools don't like it when files containing lines longer
                // than, say 8000 characters, are checked in. The linebreak option is used in
                // that case to split long lines after a specific column.
                int i = 0;
                int linestartpos = 0;
                stringBuilder = new StringBuilder(css);
                while (i < stringBuilder.Length)
                {
                    char c = stringBuilder[i++];
                    if (c == '}' && i - linestartpos > columnWidth)
                    {
                        stringBuilder.Insert(i, '\n');
                        linestartpos = i;
                    }
                }

                css = stringBuilder.ToString();
            }

            // Replace multiple semi-colons in a row by a single one.
            // See SF bug #1980989.
            css = css.RegexReplace(";;+", ";");

            // Restore preserved comments and strings.
            max = preservedTokens.Count;
            for (int i = 0; i < max; i++)
            {
                css = css.Replace("___YUICSSMIN_PRESERVED_TOKEN_" + i + "___", preservedTokens[i].ToString());
            }

            // Trim the final string (for any leading or trailing white spaces).
            css = css.Trim();

            // Write the output...
            return css;
        }
Example #5
0
        public static string Compress(string css, int columnWidth, bool removeComments)
        {
            if (string.IsNullOrEmpty(css))
            {
                throw new ArgumentNullException("css");
            }

            int totalLen        = css.Length;
            int startIndex      = 0;
            var comments        = new ArrayList();
            var preservedTokens = new ArrayList();
            int max;


            if (removeComments)
            {
                while ((startIndex = css.IndexOf(@"/*", startIndex, StringComparison.OrdinalIgnoreCase)) >= 0)
                {
                    int endIndex = css.IndexOf(@"*/", startIndex + 2, StringComparison.OrdinalIgnoreCase);
                    if (endIndex < 0)
                    {
                        endIndex = totalLen;
                    }

                    // Note: java substring-length param = end index - 2 (which is end index - (startindex + 2))
                    string token = css.Substring(startIndex + 2, endIndex - (startIndex + 2));

                    comments.Add(token);

                    string newResult = css.Replace(startIndex + 2, endIndex,
                                                   "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + (comments.Count - 1) +
                                                   "___");

                    //var newResult = css.Substring(startIndex + 2, endIndex - (startIndex + 2)) + "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" +
                    //                (comments.Count - 1) + "___" + css.Substring(endIndex + 1);

                    startIndex += 2;
                    css         = newResult;
                }
            }

            // Preserve strings so their content doesn't get accidently minified
            var   stringBuilder = new StringBuilder();
            var   pattern       = new Regex("(\"([^\\\\\"]|\\\\.|\\\\)*\")|(\'([^\\\\\']|\\\\.|\\\\)*\')");
            Match match         = pattern.Match(css);
            int   index         = 0;

            while (match.Success)
            {
                string text = match.Groups[0].Value;
                if (!string.IsNullOrEmpty(text))
                {
                    string token = match.Value;
                    char   quote = token[0];

                    // Java code: token.substring(1, token.length() -1) .. but that's ...
                    //            token.substring(start index, end index) .. while .NET it's length for the 2nd arg.
                    token = token.Substring(1, token.Length - 2);

                    // Maybe the string contains a comment-like substring?
                    // one, maybe more? put'em back then.
                    if (token.IndexOf("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_") >= 0)
                    {
                        max = comments.Count;
                        for (int i = 0; i < max; i += 1)
                        {
                            token = token.Replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___",
                                                  comments[i].ToString());
                        }
                    }

                    // Minify alpha opacity in filter strings.
                    token = token.RegexReplace("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=",
                                               "alpha(opacity=");

                    preservedTokens.Add(token);
                    string preserver = quote + "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___" +
                                       quote;

                    index = match.AppendReplacement(stringBuilder, css, preserver, index);
                    match = match.NextMatch();
                }
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // Strings are safe, now wrestle the comments.
            max = comments.Count;
            for (int i = 0; i < max; i += 1)
            {
                string token       = comments[i].ToString();
                string placeholder = "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___";

                // ! in the first position of the comment means preserve
                // so push to the preserved tokens while stripping the !
                if (token.StartsWith("!"))
                {
                    preservedTokens.Add(token);
                    css = css.Replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___");
                    continue;
                }

                // \ in the last position looks like hack for Mac/IE5
                // shorten that to /*\*/ and the next one to /**/
                if (token.EndsWith("\\"))
                {
                    preservedTokens.Add("\\");
                    css = css.Replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___");
                    i   = i + 1; // attn: advancing the loop.
                    preservedTokens.Add(string.Empty);
                    css = css.Replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___",
                                      "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___");
                    continue;
                }

                // keep empty comments after child selectors (IE7 hack)
                // e.g. html >/**/ body
                if (token.Length == 0)
                {
                    startIndex = css.IndexOf(placeholder);
                    if (startIndex > 2)
                    {
                        if (css[startIndex - 3] == '>')
                        {
                            preservedTokens.Add(string.Empty);
                            css = css.Replace(placeholder,
                                              "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.Count - 1) + "___");
                        }
                    }
                }

                // In all other cases kill the comment.
                css = css.Replace("/*" + placeholder + "*/", string.Empty);
            }

            // Normalize all whitespace strings to single spaces. Easier to work with that way.
            css = css.RegexReplace("\\s+", " ");

            // Remove the spaces before the things that should not have spaces before them.
            // But, be careful not to turn "p :link {...}" into "p:link{...}"
            // Swap out any pseudo-class colons with the token, and then swap back.
            stringBuilder = new StringBuilder();
            pattern       = new Regex("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)");
            match         = pattern.Match(css);
            index         = 0;
            while (match.Success)
            {
                string text = match.Value;
                text  = text.Replace(":", "___YUICSSMIN_PSEUDOCLASSCOLON___");
                text  = text.Replace("\\\\", "\\\\\\\\");
                text  = text.Replace("\\$", "\\\\\\$");
                index = match.AppendReplacement(stringBuilder, css, text, index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // Remove spaces before the things that should not have spaces before them.
            css = css.RegexReplace("\\s+([!{};:>+\\(\\)\\],])", "$1");

            // Bring back the colon.
            css = css.RegexReplace("___YUICSSMIN_PSEUDOCLASSCOLON___", ":");

            // Retain space for special IE6 cases.
            css = css.RegexReplace(":first\\-(line|letter)(\\{|,)", ":first-$1 $2");

            // no space after the end of a preserved comment.
            css = css.RegexReplace("\\*/ ", "*/");

            // If there is a @charset, then only allow one, and push to the top of the file.
            css = css.RegexReplace("^(.*)(@charset \"[^\"]*\";)", "$2$1");
            css = css.RegexReplace("^(\\s*@charset [^;]+;\\s*)+", "$1");

            // Put the space back in some cases, to support stuff like
            // @media screen and (-webkit-min-device-pixel-ratio:0){
            css = css.RegexReplace("\\band\\(", "and (");

            // Remove the spaces after the things that should not have spaces after them.
            css = css.RegexReplace("([!{}:;>+\\(\\[,])\\s+", "$1");

            // remove unnecessary semicolons.
            css = css.RegexReplace(";+}", "}");

            // Replace 0(px,em,%) with 0.
            css = css.RegexReplace("([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", "$1$2");

            // Replace 0 0 0 0; with 0.
            css = css.RegexReplace(":0 0 0 0(;|})", ":0$1");
            css = css.RegexReplace(":0 0 0(;|})", ":0$1");
            css = css.RegexReplace(":0 0(;|})", ":0$1");

            // Replace background-position:0; with background-position:0 0;
            // same for transform-origin
            stringBuilder = new StringBuilder();
            pattern       =
                new Regex(
                    "(?i)(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin):0(;|})");
            match = pattern.Match(css);
            index = 0;
            while (match.Success)
            {
                index = match.AppendReplacement(stringBuilder, css,
                                                match.Groups[1].Value.ToLowerInvariant() + ":0 0" + match.Groups[2],
                                                index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // Replace 0.6 to .6, but only when preceded by : or a white-space.
            css = css.RegexReplace("(:|\\s)0+\\.(\\d+)", "$1.$2");

            // Shorten colors from rgb(51,102,153) to #336699
            // This makes it more likely that it'll get further compressed in the next step.
            stringBuilder = new StringBuilder();
            pattern       = new Regex("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)");
            match         = pattern.Match(css);
            index         = 0;
            int value;

            while (match.Success)
            {
                string[] rgbcolors = match.Groups[1].Value.Split(',');
                var      hexcolor  = new StringBuilder("#");
                foreach (string rgbColour in rgbcolors)
                {
                    if (!Int32.TryParse(rgbColour, out value))
                    {
                        value = 0;
                    }

                    if (value < 16)
                    {
                        hexcolor.Append("0");
                    }
                    hexcolor.Append(value.ToHexString().ToLowerInvariant());
                }

                index = match.AppendReplacement(stringBuilder, css, hexcolor.ToString(), index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();


            // Shorten colors from #AABBCC to #ABC. Note that we want to make sure
            // the color is not preceded by either ", " or =. Indeed, the property
            //     filter: chroma(color="#FFFFFF");
            // would become
            //     filter: chroma(color="#FFF");
            // which makes the filter break in IE.
            stringBuilder = new StringBuilder();
            pattern       =
                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 = pattern.Match(css);
            index = 0;
            while (match.Success)
            {
                // Test for AABBCC pattern.
                if (match.Groups[3].Value.EqualsIgnoreCase(match.Groups[4].Value) &&
                    match.Groups[5].Value.EqualsIgnoreCase(match.Groups[6].Value) &&
                    match.Groups[7].Value.EqualsIgnoreCase(match.Groups[8].Value))
                {
                    string replacement = String.Concat(match.Groups[1].Value, match.Groups[2].Value, "#",
                                                       match.Groups[3].Value, match.Groups[5].Value,
                                                       match.Groups[7].Value);
                    index = match.AppendReplacement(stringBuilder, css, replacement, index);
                }
                else
                {
                    index = match.AppendReplacement(stringBuilder, css, match.Value, index);
                }

                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // border: none -> border:0
            stringBuilder = new StringBuilder();
            pattern       =
                new Regex("(?i)(border|border-top|border-right|border-bottom|border-right|outline|background):none(;|})");
            match = pattern.Match(css);
            index = 0;
            while (match.Success)
            {
                string replacement = match.Groups[1].Value.ToLowerInvariant() + ":0" + match.Groups[2].Value;
                index = match.AppendReplacement(stringBuilder, css, replacement, index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css, index);
            css = stringBuilder.ToString();

            // Shorter opacity IE filter.
            css = css.RegexReplace("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=", "alpha(opacity=");

            // Remove empty rules.
            css = css.RegexReplace("[^\\}\\{/;]+\\{\\}", string.Empty);

            if (columnWidth >= 0)
            {
                // Some source control tools don't like it when files containing lines longer
                // than, say 8000 characters, are checked in. The linebreak option is used in
                // that case to split long lines after a specific column.
                int i            = 0;
                int linestartpos = 0;
                stringBuilder = new StringBuilder(css);
                while (i < stringBuilder.Length)
                {
                    char c = stringBuilder[i++];
                    if (c == '}' && i - linestartpos > columnWidth)
                    {
                        stringBuilder.Insert(i, '\n');
                        linestartpos = i;
                    }
                }

                css = stringBuilder.ToString();
            }

            // Replace multiple semi-colons in a row by a single one.
            // See SF bug #1980989.
            css = css.RegexReplace(";;+", ";");

            // Restore preserved comments and strings.
            max = preservedTokens.Count;
            for (int i = 0; i < max; i++)
            {
                css = css.Replace("___YUICSSMIN_PRESERVED_TOKEN_" + i + "___", preservedTokens[i].ToString());
            }

            // Trim the final string (for any leading or trailing white spaces).
            css = css.Trim();

            // Write the output...
            return(css);
        }
Example #6
0
        private static string RemovePrecedingSpaces(this string css)
        {
            StringBuilder stringBuilder = new StringBuilder();
            Regex pattern = new Regex("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)");
            Match match = pattern.Match(css);

            int index = 0;
            while (match.Success)
            {
                string s = match.Value;
                s = s.RegexReplace(":", "___PSEUDOCLASSCOLON___");

                index = match.AppendReplacement(stringBuilder,
                    css,
                    s,
                    index);
                match = match.NextMatch();
            }
            stringBuilder.AppendTail(css,
                index);

            string result = stringBuilder.ToString();
            result = result.RegexReplace("\\s+([!{};:>+\\(\\)\\],])", "$1");
            // Put the space back in some cases, to support stuff like
            // @media screen and (-webkit-min-device-pixel-ratio:0){
            // https://github.com/isaacs/yuicompressor/commit/0e290b8e05392f19bccc2f7d524725a87739e6d6
            result = result.RegexReplace("(@media[^{]*[^\\s])\\(", "$1 (");
            result = result.RegexReplace("___PSEUDOCLASSCOLON___", ":");

            return result;
        }