/** * cssmin.js * Author: Stoyan Stefanov - http://phpied.com/ * This is a JavaScript port of the CSS minification tool * distributed with YUICompressor, itself a port * of the cssmin utility by Isaac Schlueter - http://foohack.com/ * Permission is hereby granted to use the JavaScript version under the same * conditions as the YUICompressor (original YUICompressor note below). */ /* * YUI Compressor * Author: Julien Lecomte - http://www.julienlecomte.net/ * Copyright (c) 2009 Yahoo! Inc. All rights reserved. * The copyrights embodied in the content of this file are licensed * by Yahoo! Inc. under the BSD (revised) open source license. */ ( function() { var cssmin = function (css, linebreakpos) { var startIndex = 0, endIndex = 0, iemac = false, preserve = false, i = 0, max = 0, preservedTokens = [], token = ''; // preserve strings so their content doesn't get accidentally minified css = css.replace(/("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')/g, function(match) { var quote = match[0]; preservedTokens.push(match.slice(1, -1)); return quote + "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___" + quote; }); // Remove all comment blocks... while ((startIndex = css.indexOf("/*", startIndex)) >= 0) { preserve = css.length > startIndex + 2 && css[startIndex + 2] === '!'; endIndex = css.indexOf("*/", startIndex + 2); if (endIndex < 0) { if (!preserve) { css = css.slice(0, startIndex); } } else if (endIndex >= startIndex + 2) { if (css[endIndex - 1] === '\\') { // Looks like a comment to hide rules from IE Mac. // Leave this comment, and the following one, but shorten them css = css.slice(0, startIndex) + "/*\\*/" + css.slice(endIndex + 2); startIndex += 5; iemac = true; } else if (iemac && !preserve) { css = css.slice(0, startIndex) + "/**/" + css.slice(endIndex + 2); startIndex += 4; iemac = false; } else if (!preserve) { css = css.slice(0, startIndex) + css.slice(endIndex + 2); } else { // preserve token = css.slice(startIndex+3, endIndex); // 3 is "/*!".length preservedTokens.push(token); css = css.slice(0, startIndex+2) + "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___" + css.slice(endIndex); if (iemac) iemac = false; startIndex += 2; } } } // Normalize all whitespace strings to single spaces. Easier to work with that way. css = css.replace(/\s+/g, " "); // 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. css = css.replace(/(^|\})(([^\{:])+:)+([^\{]*\{)/g, function(m) { return m.replace(":", "___YUICSSMIN_PSEUDOCLASSCOLON___"); }); css = css.replace(/\s+([!{};:>+\(\)\],])/g, '$1'); css = css.replace(/___YUICSSMIN_PSEUDOCLASSCOLON___/g, ":"); // retain space for special IE6 cases css = css.replace(/:first-(line|letter)({|,)/g, ":first-$1 $2"); // no space after the end of a preserved comment css = css.replace(/\*\/ /g, '*/'); // If there is a @charset, then only allow one, and push to the top of the file. css = css.replace(/^(.*)(@charset "[^"]*";)/gi, '$2$1'); css = css.replace(/^(\s*@charset [^;]+;\s*)+/gi, '$1'); // Put the space back in some cases, to support stuff like // @media screen and (-webkit-min-device-pixel-ratio:0){ css = css.replace(/\band\(/gi, "and ("); // Remove the spaces after the things that should not have spaces after them. css = css.replace(/([!{}:;>+\(\[,])\s+/g, '$1'); // remove unnecessary semicolons css = css.replace(/;+}/g, "}"); // Replace 0(px,em,%) with 0. css = css.replace(/([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)/gi, "$1$2"); // Replace 0 0 0 0; with 0. css = css.replace(/:0 0 0 0;/g, ":0;"); css = css.replace(/:0 0 0;/g, ":0;"); css = css.replace(/:0 0;/g, ":0;"); // Replace background-position:0; with background-position:0 0; css = css.replace(/background-position:0;/gi, "background-position:0 0;"); // Replace 0.6 to .6, but only when preceded by : or a white-space css = css.replace(/(:|\s)0+\.(\d+)/g, "$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. css = css.replace(/rgb\s*\(\s*([0-9,\s]+)\s*\)/gi, function(){ var rgbcolors = arguments[1].split(','); for (var i = 0; i < rgbcolors.length; i++) { rgbcolors[i] = parseInt(rgbcolors[i], 10).toString(16); if (rgbcolors[i].length === 1) { rgbcolors[i] = '0' + rgbcolors[i]; } } return '#' + rgbcolors.join(''); }); // 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. css = css.replace(/([^"'=\s])(\s*)#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])/gi, function(){ var group = arguments; if ( group[3].toLowerCase() === group[4].toLowerCase() && group[5].toLowerCase() === group[6].toLowerCase() && group[7].toLowerCase() === group[8].toLowerCase() ) { return (group[1] + group[2] + '#' + group[3] + group[5] + group[7]).toLowerCase(); } else { return group[0].toLowerCase(); } }); // Remove empty rules. css = css.replace(/[^\};\{\/]+\{\}/g, ""); 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 // that case to split long lines after a specific column. startIndex = 0; i = 0; while (i < css.length) { if (css[i++] === '}' && i - startIndex > linebreakpos) { css = css.slice(0, i) + '\n' + css.slice(i); startIndex = i; } } } // Replace multiple semi-colons in a row by a single one // See SF bug #1980989 css = css.replace(/;;+/g, ";"); // restore preserved comments and strings for(i = 0, max = preservedTokens.length; i < max; i++) { css = css.replace("___YUICSSMIN_PRESERVED_TOKEN_" + i + "___", preservedTokens[i]); } // Trim the final string (for any leading or trailing white spaces) css = css.replace(/^\s+|\s+$/g, ""); return css; }; //Node.js if ( typeof module === "object" && typeof exports === "object" && module.exports === exports ) { exports.cssmin = cssmin; } //web browser else if ( typeof window === "object" ) { if ( typeof YAHOO !== "object" ) { YAHOO = { compressor: { cssmin:cssmin } }; } else if ( typeof YAHOO.compressor !== "object" ) { YAHOO.compressor = { cssmin:cssmin }; } else { YAHOO.compressor.cssmin = cssmin; } } })();