Permalink
Browse files

Current version of YUIC Compressor

  • Loading branch information...
1 parent fa3378a commit 98f53772464196e515d23d9ad722d71810e949ba @mrclay mrclay committed with Feb 13, 2012
Showing with 102 additions and 29 deletions.
  1. +102 −29 min/lib/Minify/YUI/CssCompressor.java
View
131 min/lib/Minify/YUI/CssCompressor.java
@@ -29,13 +29,77 @@ public CssCompressor(Reader in) throws IOException {
}
}
+ // Leave data urls alone to increase parse performance.
+ protected String extractDataUrls(String css, ArrayList preservedTokens) {
+
+ int maxIndex = css.length() - 1;
+ int appendIndex = 0;
+
+ StringBuffer sb = new StringBuffer();
+
+ Pattern p = Pattern.compile("url\\(\\s*([\"']?)data\\:");
+ Matcher m = p.matcher(css);
+
+ /*
+ * Since we need to account for non-base64 data urls, we need to handle
+ * ' and ) being part of the data string. Hence switching to indexOf,
+ * to determine whether or not we have matching string terminators and
+ * handling sb appends directly, instead of using matcher.append* methods.
+ */
+
+ while (m.find()) {
+
+ int startIndex = m.start() + 4; // "url(".length()
+ String terminator = m.group(1); // ', " or empty (not quoted)
+
+ if (terminator.length() == 0) {
+ terminator = ")";
+ }
+
+ boolean foundTerminator = false;
+
+ int endIndex = m.end() - 1;
+ while(foundTerminator == false && endIndex+1 <= maxIndex) {
+ endIndex = css.indexOf(terminator, endIndex+1);
+
+ if ((endIndex > 0) && (css.charAt(endIndex-1) != '\\')) {
+ foundTerminator = true;
+ if (!")".equals(terminator)) {
+ endIndex = css.indexOf(")", endIndex);
+ }
+ }
+ }
+
+ // Enough searching, start moving stuff over to the buffer
+ sb.append(css.substring(appendIndex, m.start()));
+
+ if (foundTerminator) {
+ String token = css.substring(startIndex, endIndex);
+ token = token.replaceAll("\\s+", "");
+ preservedTokens.add(token);
+
+ String preserver = "url(___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___)";
+ sb.append(preserver);
+
+ appendIndex = endIndex + 1;
+ } else {
+ // No end terminator found, re-add the whole match. Should we throw/warn here?
+ sb.append(css.substring(m.start(), m.end()));
+ appendIndex = m.end();
+ }
+ }
+
+ sb.append(css.substring(appendIndex));
+
+ return sb.toString();
+ }
+
public void compress(Writer out, int linebreakpos)
throws IOException {
Pattern p;
Matcher m;
String css = srcsb.toString();
- StringBuffer sb = new StringBuffer(css);
int startIndex = 0;
int endIndex = 0;
@@ -47,19 +111,9 @@ public void compress(Writer out, int linebreakpos)
int totallen = css.length();
String placeholder;
- // // leave data urls alone to increase parse performance.
- // sb = new StringBuffer();
- // p = Pattern.compile("url\\(.*data\\:(.*)\\)");
- // m = p.matcher(css);
- // while (m.find()) {
- // token = m.group();
- // token = token.substring(1, token.length() - 1);
- // preservedTokens.add(token);
- // String preserver = "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.size() - 1) + "___";
- // m.appendReplacement(sb, preserver);
- // }
- // m.appendTail(sb);
- // css = sb.toString();
+ css = this.extractDataUrls(css, preservedTokens);
+
+ StringBuffer sb = new StringBuffer(css);
// collect all comment blocks...
while ((startIndex = sb.indexOf("/*", startIndex)) >= 0) {
@@ -236,25 +290,42 @@ public void compress(Writer out, int linebreakpos)
// would become
// filter: chroma(color="#FFF");
// which makes the filter break in IE.
- p = Pattern.compile("([^\"'=\\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])");
+ // We also want to make sure we're only compressing #AABBCC patterns inside { }, not id selectors ( #FAABAC {} )
+ // We also want to avoid compressing invalid values (e.g. #AABBCCD to #ABCD)
+ p = Pattern.compile("(\\=\\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])" + "(:?\\}|[^0-9a-fA-F{][^{]*?\\})");
+
m = p.matcher(css);
sb = new StringBuffer();
- while (m.find()) {
- if (m.group(1).equals("}")) {
- // Likely an ID selector. Don't touch.
- // #AABBCC is a valid ID. IDs are case-sensitive.
- m.appendReplacement(sb, m.group());
- } else if (m.group(3).equalsIgnoreCase(m.group(4)) &&
- m.group(5).equalsIgnoreCase(m.group(6)) &&
- m.group(7).equalsIgnoreCase(m.group(8))) {
- // #AABBCC pattern
- m.appendReplacement(sb, (m.group(1) + m.group(2) + "#" + m.group(3) + m.group(5) + m.group(7)).toLowerCase());
- } else {
- // Any other color.
- m.appendReplacement(sb, m.group().toLowerCase());
+ int index = 0;
+
+ while (m.find(index)) {
+
+ sb.append(css.substring(index, m.start()));
+
+ boolean isFilter = (m.group(1) != null && !"".equals(m.group(1)));
+
+ if (isFilter) {
+ // Restore, as is. Compression will break filters
+ sb.append(m.group(1) + "#" + m.group(2) + m.group(3) + m.group(4) + m.group(5) + m.group(6) + m.group(7));
+ } else {
+ if( m.group(2).equalsIgnoreCase(m.group(3)) &&
+ m.group(4).equalsIgnoreCase(m.group(5)) &&
+ m.group(6).equalsIgnoreCase(m.group(7))) {
+
+ // #AABBCC pattern
+ sb.append("#" + (m.group(3) + m.group(5) + m.group(7)).toLowerCase());
+
+ } else {
+
+ // Non-compressible color, restore, but lower case.
+ sb.append("#" + (m.group(2) + m.group(3) + m.group(4) + m.group(5) + m.group(6) + m.group(7)).toLowerCase());
+ }
}
+
+ index = m.end(7);
}
- m.appendTail(sb);
+
+ sb.append(css.substring(index));
css = sb.toString();
// border: none -> border:0
@@ -273,6 +344,8 @@ public void compress(Writer out, int linebreakpos)
// Remove empty rules.
css = css.replaceAll("[^\\}\\{/;]+\\{\\}", "");
+ // TODO: Should this be after we re-insert tokens. These could alter the break points. However then
+ // we'd need to make sure we don't break in the middle of a string etc.
if (linebreakpos >= 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

0 comments on commit 98f5377

Please sign in to comment.