mirror of
https://github.com/mrclay/minify.git
synced 2025-08-20 04:41:29 +02:00
Current version of YUIC Compressor
This commit is contained in:
@@ -29,13 +29,77 @@ public class CssCompressor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
public void compress(Writer out, int linebreakpos)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
Pattern p;
|
Pattern p;
|
||||||
Matcher m;
|
Matcher m;
|
||||||
String css = srcsb.toString();
|
String css = srcsb.toString();
|
||||||
StringBuffer sb = new StringBuffer(css);
|
|
||||||
|
|
||||||
int startIndex = 0;
|
int startIndex = 0;
|
||||||
int endIndex = 0;
|
int endIndex = 0;
|
||||||
@@ -47,19 +111,9 @@ public class CssCompressor {
|
|||||||
int totallen = css.length();
|
int totallen = css.length();
|
||||||
String placeholder;
|
String placeholder;
|
||||||
|
|
||||||
// // leave data urls alone to increase parse performance.
|
css = this.extractDataUrls(css, preservedTokens);
|
||||||
// sb = new StringBuffer();
|
|
||||||
// p = Pattern.compile("url\\(.*data\\:(.*)\\)");
|
StringBuffer sb = new StringBuffer(css);
|
||||||
// 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();
|
|
||||||
|
|
||||||
// collect all comment blocks...
|
// collect all comment blocks...
|
||||||
while ((startIndex = sb.indexOf("/*", startIndex)) >= 0) {
|
while ((startIndex = sb.indexOf("/*", startIndex)) >= 0) {
|
||||||
@@ -236,25 +290,42 @@ public class CssCompressor {
|
|||||||
// would become
|
// would become
|
||||||
// filter: chroma(color="#FFF");
|
// filter: chroma(color="#FFF");
|
||||||
// which makes the filter break in IE.
|
// 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);
|
m = p.matcher(css);
|
||||||
sb = new StringBuffer();
|
sb = new StringBuffer();
|
||||||
while (m.find()) {
|
int index = 0;
|
||||||
if (m.group(1).equals("}")) {
|
|
||||||
// Likely an ID selector. Don't touch.
|
while (m.find(index)) {
|
||||||
// #AABBCC is a valid ID. IDs are case-sensitive.
|
|
||||||
m.appendReplacement(sb, m.group());
|
sb.append(css.substring(index, m.start()));
|
||||||
} else if (m.group(3).equalsIgnoreCase(m.group(4)) &&
|
|
||||||
m.group(5).equalsIgnoreCase(m.group(6)) &&
|
boolean isFilter = (m.group(1) != null && !"".equals(m.group(1)));
|
||||||
m.group(7).equalsIgnoreCase(m.group(8))) {
|
|
||||||
// #AABBCC pattern
|
if (isFilter) {
|
||||||
m.appendReplacement(sb, (m.group(1) + m.group(2) + "#" + m.group(3) + m.group(5) + m.group(7)).toLowerCase());
|
// Restore, as is. Compression will break filters
|
||||||
} else {
|
sb.append(m.group(1) + "#" + m.group(2) + m.group(3) + m.group(4) + m.group(5) + m.group(6) + m.group(7));
|
||||||
// Any other color.
|
} else {
|
||||||
m.appendReplacement(sb, m.group().toLowerCase());
|
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();
|
css = sb.toString();
|
||||||
|
|
||||||
// border: none -> border:0
|
// border: none -> border:0
|
||||||
@@ -273,6 +344,8 @@ public class CssCompressor {
|
|||||||
// Remove empty rules.
|
// Remove empty rules.
|
||||||
css = css.replaceAll("[^\\}\\{/;]+\\{\\}", "");
|
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) {
|
if (linebreakpos >= 0) {
|
||||||
// Some source control tools don't like it when files containing lines longer
|
// 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
|
// than, say 8000 characters, are checked in. The linebreak option is used in
|
||||||
|
Reference in New Issue
Block a user