|
|
@@ -62,91 +62,9 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
output: ""
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * convertRelativeUrls converts relative urls and replaces them with tokens
|
|
|
- * before we start compressing. It must be called *after* extractDataUrls
|
|
|
- *
|
|
|
- * @param {string} css - CSS content
|
|
|
- * @param {options} options - UglifyCSS Options
|
|
|
- * @param {string[]} preservedTokens - Global array of tokens to preserve
|
|
|
- *
|
|
|
- * @return {string} Processed css
|
|
|
- */
|
|
|
-
|
|
|
- function convertRelativeUrls(css, options, preservedTokens) {
|
|
|
-
|
|
|
- const pattern = /(url\s*\()\s*(["']?)/g;
|
|
|
- const maxIndex = css.length - 1;
|
|
|
- const sb = [];
|
|
|
-
|
|
|
- let appendIndex = 0, match;
|
|
|
-
|
|
|
- // 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 ((match = pattern.exec(css)) !== null) {
|
|
|
-
|
|
|
- const startIndex = match.index + match[1].length; // 'url('.length()
|
|
|
- let terminator = match[2]; // ', " or empty (not quoted)
|
|
|
-
|
|
|
- if (terminator.length === 0) {
|
|
|
- terminator = ")";
|
|
|
- }
|
|
|
-
|
|
|
- let foundTerminator = false, endIndex = pattern.lastIndex - 1;
|
|
|
-
|
|
|
- while (foundTerminator === false && endIndex + 1 <= maxIndex) {
|
|
|
- endIndex = css.indexOf(terminator, endIndex + 1);
|
|
|
-
|
|
|
- // endIndex == 0 doesn't really apply here
|
|
|
- if ((endIndex > 0) && (css.charAt(endIndex - 1) !== "\\")) {
|
|
|
- foundTerminator = true;
|
|
|
- if (")" != terminator) {
|
|
|
- endIndex = css.indexOf(")", endIndex);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Enough searching, start moving stuff over to the buffer
|
|
|
- sb.push(css.substring(appendIndex, match.index));
|
|
|
-
|
|
|
- if (foundTerminator) {
|
|
|
-
|
|
|
- let token = css.substring(startIndex, endIndex).replace(/(^\s*|\s*$)/g, "");
|
|
|
- if (token.slice(0, 19) !== ___PRESERVED_TOKEN_) {
|
|
|
-
|
|
|
- if (terminator === "'" || terminator === "\"") {
|
|
|
- token = token.slice(1, -1);
|
|
|
- } else if (terminator === ")") {
|
|
|
- terminator = "";
|
|
|
- }
|
|
|
-
|
|
|
- const url = terminator + token + terminator;
|
|
|
-
|
|
|
- preservedTokens.push(url);
|
|
|
-
|
|
|
- const preserver = "url(" + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___)";
|
|
|
- sb.push(preserver);
|
|
|
-
|
|
|
- } else {
|
|
|
- sb.push(`url(${token})`);
|
|
|
- }
|
|
|
-
|
|
|
- appendIndex = endIndex + 1;
|
|
|
-
|
|
|
- } else {
|
|
|
- // No end terminator found, re-add the whole match. Should we throw/warn here?
|
|
|
- sb.push(css.substring(match.index, pattern.lastIndex));
|
|
|
- appendIndex = pattern.lastIndex;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- sb.push(css.substring(appendIndex));
|
|
|
-
|
|
|
- return sb.join("");
|
|
|
- }
|
|
|
+ const REGEXP_DATA_URI = /url\(\s*(["']?)data:/g;
|
|
|
+ const REGEXP_WHITE_SPACES = /\s+/g;
|
|
|
+ const REGEXP_NEW_LINE = /\n/g;
|
|
|
|
|
|
/**
|
|
|
* extractDataUrls replaces all data urls with tokens before we start
|
|
|
@@ -162,7 +80,7 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
function extractDataUrls(css, preservedTokens) {
|
|
|
|
|
|
// Leave data urls alone to increase parse performance.
|
|
|
- const pattern = /url\(\s*(["']?)data:/g;
|
|
|
+ const pattern = REGEXP_DATA_URI;
|
|
|
const maxIndex = css.length - 1;
|
|
|
const sb = [];
|
|
|
|
|
|
@@ -204,11 +122,11 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
let token = css.substring(startIndex, endIndex);
|
|
|
const parts = token.split(",");
|
|
|
if (parts.length > 1 && parts[0].slice(-7) == ";base64") {
|
|
|
- token = token.replace(/\s+/g, "");
|
|
|
+ token = token.replace(REGEXP_WHITE_SPACES, "");
|
|
|
} else {
|
|
|
- token = token.replace(/\n/g, " ");
|
|
|
- token = token.replace(/\s+/g, " ");
|
|
|
- token = token.replace(/(^\s+|\s+$)/g, "");
|
|
|
+ token = token.replace(REGEXP_NEW_LINE, " ");
|
|
|
+ token = token.replace(REGEXP_WHITE_SPACES, " ");
|
|
|
+ token = token.replace(REGEXP_PRESERVE_HSLA1, "");
|
|
|
}
|
|
|
|
|
|
preservedTokens.push(token);
|
|
|
@@ -229,6 +147,8 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
return sb.join("");
|
|
|
}
|
|
|
|
|
|
+ const REGEXP_HEX_COLORS = /(=\s*?["']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/gi;
|
|
|
+
|
|
|
/**
|
|
|
* compressHexColors compresses hex color values of the form #AABBCC to #ABC.
|
|
|
*
|
|
|
@@ -249,7 +169,7 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
|
|
|
// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
|
|
|
|
|
|
- const pattern = /(=\s*?["']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/gi;
|
|
|
+ const pattern = REGEXP_HEX_COLORS;
|
|
|
const sb = [];
|
|
|
|
|
|
let index = 0, match;
|
|
|
@@ -284,6 +204,9 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
return sb.join("");
|
|
|
}
|
|
|
|
|
|
+ const REGEXP_KEYFRAMES = /@[a-z0-9-_]*keyframes\s+[a-z0-9-_]+\s*{/gi;
|
|
|
+ const REGEXP_WHITE_SPACE = /(^\s|\s$)/g;
|
|
|
+
|
|
|
/** keyframes preserves 0 followed by unit in keyframes steps
|
|
|
*
|
|
|
* @param {string} content - CSS content
|
|
|
@@ -294,12 +217,12 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
|
|
|
function keyframes(content, preservedTokens) {
|
|
|
|
|
|
- const pattern = /@[a-z0-9-_]*keyframes\s+[a-z0-9-_]+\s*{/gi;
|
|
|
+ const pattern = REGEXP_KEYFRAMES;
|
|
|
|
|
|
let index = 0, buffer;
|
|
|
|
|
|
const preserve = (part, i) => {
|
|
|
- part = part.replace(/(^\s|\s$)/g, "");
|
|
|
+ part = part.replace(REGEXP_WHITE_SPACE, "");
|
|
|
if (part.charAt(0) === "0") {
|
|
|
preservedTokens.push(part);
|
|
|
buffer[i] = ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
|
|
|
@@ -329,7 +252,7 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
if (ch === "{") {
|
|
|
|
|
|
if (level === 0) {
|
|
|
- buffers.push(buffer.replace(/(^\s|\s$)/g, ""));
|
|
|
+ buffers.push(buffer.replace(REGEXP_WHITE_SPACE, ""));
|
|
|
|
|
|
} else if (level === 1) {
|
|
|
|
|
|
@@ -337,7 +260,7 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
|
|
|
buffer.forEach(preserve);
|
|
|
|
|
|
- buffers.push(buffer.join(",").replace(/(^\s|\s$)/g, ""));
|
|
|
+ buffers.push(buffer.join(",").replace(REGEXP_WHITE_SPACE, ""));
|
|
|
}
|
|
|
|
|
|
buffer = "";
|
|
|
@@ -346,7 +269,7 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
} else if (ch === "}") {
|
|
|
|
|
|
if (level === 2) {
|
|
|
- buffers.push("{" + buffer.replace(/(^\s|\s$)/g, "") + "}");
|
|
|
+ buffers.push("{" + buffer.replace(REGEXP_WHITE_SPACE, "") + "}");
|
|
|
buffer = "";
|
|
|
|
|
|
} else if (level === 1) {
|
|
|
@@ -426,6 +349,69 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
* @return {string} Uglified result
|
|
|
*/
|
|
|
|
|
|
+ // const REGEXP_EMPTY_RULES = /[^};{/]+\{\}/g;
|
|
|
+ const REGEXP_PRESERVE_STRING = /("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')/g;
|
|
|
+ const REGEXP_MINIFY_ALPHA = /progid:DXImageTransform.Microsoft.Alpha\(Opacity=/gi;
|
|
|
+ const REGEXP_PRESERVE_TOKEN1 = /\r\n/g;
|
|
|
+ const REGEXP_PRESERVE_TOKEN2 = /[\r\n]/g;
|
|
|
+ const REGEXP_VARIABLES = /@variables\s*\{\s*([^}]+)\s*\}/g;
|
|
|
+ const REGEXP_VARIABLE = /\s*([a-z0-9-]+)\s*:\s*([^;}]+)\s*/gi;
|
|
|
+ const REGEXP_VARIABLE_VALUE = /var\s*\(\s*([^)]+)\s*\)/g;
|
|
|
+ const REGEXP_PRESERVE_CALC = /calc\(([^;}]*)\)/g;
|
|
|
+ const REGEXP_TRIM = /(^\s*|\s*$)/g;
|
|
|
+ const REGEXP_PRESERVE_CALC2 = /\( /g;
|
|
|
+ const REGEXP_PRESERVE_CALC3 = / \)/g;
|
|
|
+ const REGEXP_PRESERVE_MATRIX = /\s*filter:\s*progid:DXImageTransform.Microsoft.Matrix\(([^)]+)\);/g;
|
|
|
+ const REGEXP_REMOVE_SPACES = /(^|\})(([^{:])+:)+([^{]*{)/g;
|
|
|
+ const REGEXP_REMOVE_SPACES2 = /\s+([!{};:>+()\],])/g;
|
|
|
+ const REGEXP_RESTORE_SPACE_IMPORTANT = /!important/g;
|
|
|
+ const REGEXP_PSEUDOCLASSCOLON = /___PSEUDOCLASSCOLON___/g;
|
|
|
+ const REGEXP_COLUMN = /:/g;
|
|
|
+ const REGEXP_PRESERVE_ZERO_UNIT = /\s*(animation|animation-delay|animation-duration|transition|transition-delay|transition-duration):\s*([^;}]+)/gi;
|
|
|
+ const REGEXP_PRESERVE_ZERO_UNIT1 = /(^|\D)0?\.?0(m?s)/gi;
|
|
|
+ const REGEXP_PRESERVE_FLEX = /\s*(flex|flex-basis):\s*([^;}]+)/gi;
|
|
|
+ const REGEXP_SPACES = /\s+/;
|
|
|
+ const REGEXP_PRESERVE_HSLA = /(hsla?)\(([^)]+)\)/g;
|
|
|
+ const REGEXP_PRESERVE_HSLA1 = /(^\s+|\s+$)/g;
|
|
|
+ const REGEXP_RETAIN_SPACE_IE6 = /:first-(line|letter)(\{|,)/gi;
|
|
|
+ const REGEXP_CHARSET = /^(.*)(@charset)( "[^"]*";)/gi;
|
|
|
+ const REGEXP_REMOVE_SECOND_CHARSET = /^((\s*)(@charset)( [^;]+;\s*))+/gi;
|
|
|
+ const REGEXP_LOWERCASE_DIRECTIVES = /@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/gi;
|
|
|
+ const REGEXP_LOWERCASE_PSEUDO_ELEMENTS = /:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/gi;
|
|
|
+ const REGEXP_CHARSET2 = /^(.*)(@charset "[^"]*";)/g;
|
|
|
+ const REGEXP_CHARSET3 = /^(\s*@charset [^;]+;\s*)+/g;
|
|
|
+ const REGEXP_LOWERCASE_FUNCTIONS = /:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?any)\(/gi;
|
|
|
+ const REGEXP_LOWERCASE_FUNCTIONS2 = /([:,( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/gi;
|
|
|
+ const REGEXP_NEWLINE1 = /\s*\/\*/g;
|
|
|
+ const REGEXP_NEWLINE2 = /\*\/\s*/g;
|
|
|
+ const REGEXP_RESTORE_SPACE1 = /\band\(/gi;
|
|
|
+ const REGEXP_RESTORE_SPACE2 = /([^:])not\(/gi;
|
|
|
+ const REGEXP_REMOVE_SPACES3 = /([!{}:;>+([,])\s+/g;
|
|
|
+ const REGEXP_REMOVE_SEMI_COLUMNS = /;+\}/g;
|
|
|
+ const REGEXP_REPLACE_ZERO = /(^|[^.0-9\\])(?:0?\.)?0(?:ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%)(?![a-z0-9])/gi;
|
|
|
+ const REGEXP_REPLACE_ZERO_DOT = /([0-9])\.0(ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%| |;)/gi;
|
|
|
+ const REGEXP_REPLACE_4_ZEROS = /:0 0 0 0(;|\})/g;
|
|
|
+ const REGEXP_REPLACE_3_ZEROS = /:0 0 0(;|\})/g;
|
|
|
+ const REGEXP_REPLACE_2_ZEROS = /:0 0(;|\})/g;
|
|
|
+ const REGEXP_REPLACE_1_ZERO = /(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin|box-shadow):0(;|\})/gi;
|
|
|
+ const REGEXP_REPLACE_ZERO_DOT_DECIMAL = /(:|\s)0+\.(\d+)/g;
|
|
|
+ const REGEXP_REPLACE_RGB = /rgb\s*\(\s*([0-9,\s]+)\s*\)/gi;
|
|
|
+ const REGEXP_REPLACE_BORDER_ZERO = /(border|border-top|border-right|border-bottom|border-left|outline|background):none(;|\})/gi;
|
|
|
+ const REGEXP_REPLACE_IE_OPACITY = /progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi;
|
|
|
+ const REGEXP_REPLACE_QUERY_FRACTION = /\(([-A-Za-z]+):([0-9]+)\/([0-9]+)\)/g;
|
|
|
+ const REGEXP_QUERY_FRACTION = /___QUERY_FRACTION___/g;
|
|
|
+ const REGEXP_REPLACE_SEMI_COLUMNS = /;;+/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR = /(:|\s)(#f00)(;|})/g;
|
|
|
+ const REGEXP_PRESERVED_NEWLINE = /___PRESERVED_NEWLINE___/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR_SHORT1 = /(:|\s)(#000080)(;|})/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR_SHORT2 = /(:|\s)(#808080)(;|})/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR_SHORT3 = /(:|\s)(#808000)(;|})/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR_SHORT4 = /(:|\s)(#800080)(;|})/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR_SHORT5 = /(:|\s)(#c0c0c0)(;|})/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR_SHORT6 = /(:|\s)(#008080)(;|})/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR_SHORT7 = /(:|\s)(#ffa500)(;|})/g;
|
|
|
+ const REGEXP_REPLACE_HASH_COLOR_SHORT8 = /(:|\s)(#800000)(;|})/g;
|
|
|
+
|
|
|
function processString(content = "", options = defaultOptions) {
|
|
|
|
|
|
const comments = [];
|
|
|
@@ -434,11 +420,10 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
let pattern;
|
|
|
|
|
|
content = extractDataUrls(content, preservedTokens);
|
|
|
- content = convertRelativeUrls(content, options, preservedTokens);
|
|
|
content = collectComments(content, comments);
|
|
|
|
|
|
// preserve strings so their content doesn't get accidentally minified
|
|
|
- pattern = /("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')/g;
|
|
|
+ pattern = REGEXP_PRESERVE_STRING;
|
|
|
content = content.replace(pattern, token => {
|
|
|
const quote = token.substring(0, 1);
|
|
|
token = token.slice(1, -1);
|
|
|
@@ -449,7 +434,7 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
}
|
|
|
}
|
|
|
// minify alpha opacity in filter strings
|
|
|
- token = token.replace(/progid:DXImageTransform.Microsoft.Alpha\(Opacity=/gi, "alpha(opacity=");
|
|
|
+ token = token.replace(REGEXP_MINIFY_ALPHA, "alpha(opacity=");
|
|
|
preservedTokens.push(token);
|
|
|
return quote + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___" + quote;
|
|
|
});
|
|
|
@@ -464,9 +449,9 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
// so push to the preserved tokens keeping the !
|
|
|
if (token.charAt(0) === "!") {
|
|
|
if (options.cuteComments) {
|
|
|
- preservedTokens.push(token.substring(1).replace(/\r\n/g, "\n"));
|
|
|
+ preservedTokens.push(token.substring(1).replace(REGEXP_PRESERVE_TOKEN1, "\n"));
|
|
|
} else if (options.uglyComments) {
|
|
|
- preservedTokens.push(token.substring(1).replace(/[\r\n]/g, ""));
|
|
|
+ preservedTokens.push(token.substring(1).replace(REGEXP_PRESERVE_TOKEN2, ""));
|
|
|
} else {
|
|
|
preservedTokens.push(token);
|
|
|
}
|
|
|
@@ -507,9 +492,9 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
// parse simple @variables blocks and remove them
|
|
|
if (options.expandVars) {
|
|
|
const vars = {};
|
|
|
- pattern = /@variables\s*\{\s*([^}]+)\s*\}/g;
|
|
|
+ pattern = REGEXP_VARIABLES;
|
|
|
content = content.replace(pattern, (_, f1) => {
|
|
|
- pattern = /\s*([a-z0-9-]+)\s*:\s*([^;}]+)\s*/gi;
|
|
|
+ pattern = REGEXP_VARIABLE;
|
|
|
f1.replace(pattern, (_, f1, f2) => {
|
|
|
if (f1 && f2) {
|
|
|
vars[f1] = f2;
|
|
|
@@ -520,30 +505,30 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
});
|
|
|
|
|
|
// replace var(x) with the value of x
|
|
|
- pattern = /var\s*\(\s*([^)]+)\s*\)/g;
|
|
|
+ pattern = REGEXP_VARIABLE_VALUE;
|
|
|
content = content.replace(pattern, (_, f1) => {
|
|
|
return vars[f1] || "none";
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// normalize all whitespace strings to single spaces. Easier to work with that way.
|
|
|
- content = content.replace(/\s+/g, " ");
|
|
|
+ content = content.replace(REGEXP_WHITE_SPACES, " ");
|
|
|
|
|
|
// preserve formulas in calc() before removing spaces
|
|
|
- pattern = /calc\(([^;}]*)\)/g;
|
|
|
+ pattern = REGEXP_PRESERVE_CALC;
|
|
|
content = content.replace(pattern, (_, f1) => {
|
|
|
preservedTokens.push(
|
|
|
"calc(" +
|
|
|
- f1.replace(/(^\s*|\s*$)/g, "")
|
|
|
- .replace(/\( /g, "(")
|
|
|
- .replace(/ \)/g, ")") +
|
|
|
+ f1.replace(REGEXP_TRIM, "")
|
|
|
+ .replace(REGEXP_PRESERVE_CALC2, "(")
|
|
|
+ .replace(REGEXP_PRESERVE_CALC3, ")") +
|
|
|
")"
|
|
|
);
|
|
|
return ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
|
|
|
});
|
|
|
|
|
|
// preserve matrix
|
|
|
- pattern = /\s*filter:\s*progid:DXImageTransform.Microsoft.Matrix\(([^)]+)\);/g;
|
|
|
+ pattern = REGEXP_PRESERVE_MATRIX;
|
|
|
content = content.replace(pattern, (_, f1) => {
|
|
|
preservedTokens.push(f1);
|
|
|
return "filter:progid:DXImageTransform.Microsoft.Matrix(" + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___);";
|
|
|
@@ -552,23 +537,23 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
// 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.
|
|
|
- pattern = /(^|\})(([^{:])+:)+([^{]*{)/g;
|
|
|
- content = content.replace(pattern, token => token.replace(/:/g, "___PSEUDOCLASSCOLON___"));
|
|
|
+ pattern = REGEXP_REMOVE_SPACES;
|
|
|
+ content = content.replace(pattern, token => token.replace(REGEXP_COLUMN, "___PSEUDOCLASSCOLON___"));
|
|
|
|
|
|
// remove spaces before the things that should not have spaces before them.
|
|
|
- content = content.replace(/\s+([!{};:>+()\],])/g, "$1");
|
|
|
+ content = content.replace(REGEXP_REMOVE_SPACES2, "$1");
|
|
|
|
|
|
// restore spaces for !important
|
|
|
- content = content.replace(/!important/g, " !important");
|
|
|
+ content = content.replace(REGEXP_RESTORE_SPACE_IMPORTANT, " !important");
|
|
|
|
|
|
// bring back the colon
|
|
|
- content = content.replace(/___PSEUDOCLASSCOLON___/g, ":");
|
|
|
+ content = content.replace(REGEXP_PSEUDOCLASSCOLON, ":");
|
|
|
|
|
|
// preserve 0 followed by a time unit for properties using time units
|
|
|
- pattern = /\s*(animation|animation-delay|animation-duration|transition|transition-delay|transition-duration):\s*([^;}]+)/gi;
|
|
|
+ pattern = REGEXP_PRESERVE_ZERO_UNIT;
|
|
|
content = content.replace(pattern, (_, f1, f2) => {
|
|
|
|
|
|
- f2 = f2.replace(/(^|\D)0?\.?0(m?s)/gi, (_, g1, g2) => {
|
|
|
+ f2 = f2.replace(REGEXP_PRESERVE_ZERO_UNIT1, (_, g1, g2) => {
|
|
|
preservedTokens.push("0" + g2);
|
|
|
return g1 + ___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___";
|
|
|
});
|
|
|
@@ -577,9 +562,9 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
});
|
|
|
|
|
|
// preserve unit for flex-basis within flex and flex-basis (ie10 bug)
|
|
|
- pattern = /\s*(flex|flex-basis):\s*([^;}]+)/gi;
|
|
|
+ pattern = REGEXP_PRESERVE_FLEX;
|
|
|
content = content.replace(pattern, (_, f1, f2) => {
|
|
|
- let f2b = f2.split(/\s+/);
|
|
|
+ let f2b = f2.split(REGEXP_SPACES);
|
|
|
preservedTokens.push(f2b.pop());
|
|
|
f2b.push(___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
|
|
|
f2b = f2b.join(" ");
|
|
|
@@ -587,10 +572,10 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
});
|
|
|
|
|
|
// preserve 0% in hsl and hsla color definitions
|
|
|
- content = content.replace(/(hsla?)\(([^)]+)\)/g, (_, f1, f2) => {
|
|
|
+ content = content.replace(REGEXP_PRESERVE_HSLA, (_, f1, f2) => {
|
|
|
const f0 = [];
|
|
|
f2.split(",").forEach(part => {
|
|
|
- part = part.replace(/(^\s+|\s+$)/g, "");
|
|
|
+ part = part.replace(REGEXP_PRESERVE_HSLA1, "");
|
|
|
if (part === "0%") {
|
|
|
preservedTokens.push("0%");
|
|
|
f0.push(___PRESERVED_TOKEN_ + (preservedTokens.length - 1) + "___");
|
|
|
@@ -605,79 +590,79 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
content = keyframes(content, preservedTokens);
|
|
|
|
|
|
// retain space for special IE6 cases
|
|
|
- content = content.replace(/:first-(line|letter)(\{|,)/gi, (_, f1, f2) => ":first-" + f1.toLowerCase() + " " + f2);
|
|
|
+ content = content.replace(REGEXP_RETAIN_SPACE_IE6, (_, f1, f2) => ":first-" + f1.toLowerCase() + " " + f2);
|
|
|
|
|
|
// newlines before and after the end of a preserved comment
|
|
|
if (options.cuteComments) {
|
|
|
- content = content.replace(/\s*\/\*/g, "___PRESERVED_NEWLINE___/*");
|
|
|
- content = content.replace(/\*\/\s*/g, "*/___PRESERVED_NEWLINE___");
|
|
|
+ content = content.replace(REGEXP_NEWLINE1, "___PRESERVED_NEWLINE___/*");
|
|
|
+ content = content.replace(REGEXP_NEWLINE2, "*/___PRESERVED_NEWLINE___");
|
|
|
// no space after the end of a preserved comment
|
|
|
} else {
|
|
|
- content = content.replace(/\*\/\s*/g, "*/");
|
|
|
+ content = content.replace(REGEXP_NEWLINE2, "*/");
|
|
|
}
|
|
|
|
|
|
// If there are multiple @charset directives, push them to the top of the file.
|
|
|
- pattern = /^(.*)(@charset)( "[^"]*";)/gi;
|
|
|
+ pattern = REGEXP_CHARSET;
|
|
|
content = content.replace(pattern, (_, f1, f2, f3) => f2.toLowerCase() + f3 + f1);
|
|
|
|
|
|
// When all @charset are at the top, remove the second and after (as they are completely ignored).
|
|
|
- pattern = /^((\s*)(@charset)( [^;]+;\s*))+/gi;
|
|
|
+ pattern = REGEXP_REMOVE_SECOND_CHARSET;
|
|
|
content = content.replace(pattern, (_, __, f2, f3, f4) => f2 + f3.toLowerCase() + f4);
|
|
|
|
|
|
// lowercase some popular @directives (@charset is done right above)
|
|
|
- pattern = /@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/gi;
|
|
|
+ pattern = REGEXP_LOWERCASE_DIRECTIVES;
|
|
|
content = content.replace(pattern, (_, f1) => "@" + f1.toLowerCase());
|
|
|
|
|
|
// lowercase some more common pseudo-elements
|
|
|
- pattern = /:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/gi;
|
|
|
+ pattern = REGEXP_LOWERCASE_PSEUDO_ELEMENTS;
|
|
|
content = content.replace(pattern, (_, f1) => ":" + f1.toLowerCase());
|
|
|
|
|
|
// if there is a @charset, then only allow one, and push to the top of the file.
|
|
|
- content = content.replace(/^(.*)(@charset "[^"]*";)/g, "$2$1");
|
|
|
- content = content.replace(/^(\s*@charset [^;]+;\s*)+/g, "$1");
|
|
|
+ content = content.replace(REGEXP_CHARSET2, "$2$1");
|
|
|
+ content = content.replace(REGEXP_CHARSET3, "$1");
|
|
|
|
|
|
// lowercase some more common functions
|
|
|
- pattern = /:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?any)\(/gi;
|
|
|
+ pattern = REGEXP_LOWERCASE_FUNCTIONS;
|
|
|
content = content.replace(pattern, (_, f1) => ":" + f1.toLowerCase() + "(");
|
|
|
|
|
|
// lower case some common function that can be values
|
|
|
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us right after this
|
|
|
- pattern = /([:,( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/gi;
|
|
|
+ pattern = REGEXP_LOWERCASE_FUNCTIONS2;
|
|
|
content = content.replace(pattern, (_, f1, f2) => f1 + f2.toLowerCase());
|
|
|
|
|
|
// put the space back in some cases, to support stuff like
|
|
|
// @media screen and (-webkit-min-device-pixel-ratio:0){
|
|
|
- content = content.replace(/\band\(/gi, "and (");
|
|
|
- content = content.replace(/([^:])not\(/gi, "$1not (");
|
|
|
+ content = content.replace(REGEXP_RESTORE_SPACE1, "and (");
|
|
|
+ content = content.replace(REGEXP_RESTORE_SPACE2, "$1not (");
|
|
|
|
|
|
// remove the spaces after the things that should not have spaces after them.
|
|
|
- content = content.replace(/([!{}:;>+([,])\s+/g, "$1");
|
|
|
+ content = content.replace(REGEXP_REMOVE_SPACES3, "$1");
|
|
|
|
|
|
// remove unnecessary semicolons
|
|
|
- content = content.replace(/;+\}/g, "}");
|
|
|
+ content = content.replace(REGEXP_REMOVE_SEMI_COLUMNS, "}");
|
|
|
|
|
|
// replace 0(px,em,%) with 0.
|
|
|
- content = content.replace(/(^|[^.0-9\\])(?:0?\.)?0(?:ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%)(?![a-z0-9])/gi, "$10");
|
|
|
+ content = content.replace(REGEXP_REPLACE_ZERO, "$10");
|
|
|
|
|
|
// Replace x.0(px,em,%) with x(px,em,%).
|
|
|
- content = content.replace(/([0-9])\.0(ex|ch|r?em|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|g?rad|turn|m?s|k?Hz|dpi|dpcm|dppx|%| |;)/gi, "$1$2");
|
|
|
+ content = content.replace(REGEXP_REPLACE_ZERO_DOT, "$1$2");
|
|
|
|
|
|
// replace 0 0 0 0; with 0.
|
|
|
- content = content.replace(/:0 0 0 0(;|\})/g, ":0$1");
|
|
|
- content = content.replace(/:0 0 0(;|\})/g, ":0$1");
|
|
|
- content = content.replace(/:0 0(;|\})/g, ":0$1");
|
|
|
+ content = content.replace(REGEXP_REPLACE_4_ZEROS, ":0$1");
|
|
|
+ content = content.replace(REGEXP_REPLACE_3_ZEROS, ":0$1");
|
|
|
+ content = content.replace(REGEXP_REPLACE_2_ZEROS, ":0$1");
|
|
|
|
|
|
// replace background-position:0; with background-position:0 0;
|
|
|
// same for transform-origin and box-shadow
|
|
|
- pattern = /(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin|box-shadow):0(;|\})/gi;
|
|
|
+ pattern = REGEXP_REPLACE_1_ZERO;
|
|
|
content = content.replace(pattern, (_, f1, f2) => f1.toLowerCase() + ":0 0" + f2);
|
|
|
|
|
|
// replace 0.6 to .6, but only when preceded by : or a white-space
|
|
|
- content = content.replace(/(:|\s)0+\.(\d+)/g, "$1.$2");
|
|
|
+ content = content.replace(REGEXP_REPLACE_ZERO_DOT_DECIMAL, "$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.
|
|
|
- pattern = /rgb\s*\(\s*([0-9,\s]+)\s*\)/gi;
|
|
|
+ pattern = REGEXP_REPLACE_RGB;
|
|
|
content = content.replace(pattern, (_, f1) => {
|
|
|
let rgbcolors = f1.split(","), hexcolor = "#";
|
|
|
for (let i = 0; i < rgbcolors.length; i += 1) {
|
|
|
@@ -697,34 +682,34 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
content = compressHexColors(content);
|
|
|
|
|
|
// Replace #f00 -> red
|
|
|
- content = content.replace(/(:|\s)(#f00)(;|})/g, "$1red$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR, "$1red$3");
|
|
|
|
|
|
// Replace other short color keywords
|
|
|
- content = content.replace(/(:|\s)(#000080)(;|})/g, "$1navy$3");
|
|
|
- content = content.replace(/(:|\s)(#808080)(;|})/g, "$1gray$3");
|
|
|
- content = content.replace(/(:|\s)(#808000)(;|})/g, "$1olive$3");
|
|
|
- content = content.replace(/(:|\s)(#800080)(;|})/g, "$1purple$3");
|
|
|
- content = content.replace(/(:|\s)(#c0c0c0)(;|})/g, "$1silver$3");
|
|
|
- content = content.replace(/(:|\s)(#008080)(;|})/g, "$1teal$3");
|
|
|
- content = content.replace(/(:|\s)(#ffa500)(;|})/g, "$1orange$3");
|
|
|
- content = content.replace(/(:|\s)(#800000)(;|})/g, "$1maroon$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT1, "$1navy$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT2, "$1gray$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT3, "$1olive$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT4, "$1purple$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT5, "$1silver$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT6, "$1teal$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT7, "$1orange$3");
|
|
|
+ content = content.replace(REGEXP_REPLACE_HASH_COLOR_SHORT8, "$1maroon$3");
|
|
|
|
|
|
// border: none -> border:0
|
|
|
- pattern = /(border|border-top|border-right|border-bottom|border-left|outline|background):none(;|\})/gi;
|
|
|
+ pattern = REGEXP_REPLACE_BORDER_ZERO;
|
|
|
content = content.replace(pattern, (_, f1, f2) => f1.toLowerCase() + ":0" + f2);
|
|
|
|
|
|
// shorter opacity IE filter
|
|
|
- content = content.replace(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi, "alpha(opacity=");
|
|
|
+ content = content.replace(REGEXP_REPLACE_IE_OPACITY, "alpha(opacity=");
|
|
|
|
|
|
// Find a fraction that is used for Opera's -o-device-pixel-ratio query
|
|
|
// Add token to add the '\' back in later
|
|
|
- content = content.replace(/\(([-A-Za-z]+):([0-9]+)\/([0-9]+)\)/g, "($1:$2___QUERY_FRACTION___$3)");
|
|
|
+ content = content.replace(REGEXP_REPLACE_QUERY_FRACTION, "($1:$2___QUERY_FRACTION___$3)");
|
|
|
|
|
|
// remove empty rules.
|
|
|
- content = content.replace(/[^};{/]+\{\}/g, "");
|
|
|
+ // content = content.replace(REGEXP_EMPTY_RULES, "");
|
|
|
|
|
|
// Add '\' back to fix Opera -o-device-pixel-ratio query
|
|
|
- content = content.replace(/___QUERY_FRACTION___/g, "/");
|
|
|
+ content = content.replace(REGEXP_QUERY_FRACTION, "/");
|
|
|
|
|
|
// 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
|
|
|
@@ -749,10 +734,10 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
|
|
|
// replace multiple semi-colons in a row by a single one
|
|
|
// see SF bug #1980989
|
|
|
- content = content.replace(/;;+/g, ";");
|
|
|
+ content = content.replace(REGEXP_REPLACE_SEMI_COLUMNS, ";");
|
|
|
|
|
|
// trim the final string (for any leading or trailing white spaces)
|
|
|
- content = content.replace(/(^\s*|\s*$)/g, "");
|
|
|
+ content = content.replace(REGEXP_TRIM, "");
|
|
|
|
|
|
// restore preserved tokens
|
|
|
for (let i = preservedTokens.length - 1; i >= 0; i--) {
|
|
|
@@ -760,7 +745,7 @@ this.uglifycss = this.uglifycss || (() => {
|
|
|
}
|
|
|
|
|
|
// restore preserved newlines
|
|
|
- content = content.replace(/___PRESERVED_NEWLINE___/g, "\n");
|
|
|
+ content = content.replace(REGEXP_PRESERVED_NEWLINE, "\n");
|
|
|
|
|
|
// return
|
|
|
return content;
|