| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 | /** * Clean-css - https://github.com/GoalSmashers/clean-css * Released under the terms of MIT license * * Copyright (C) 2011-2014 GoalSmashers.com */var ColorShortener = require('./colors/shortener');var ColorHSLToHex = require('./colors/hsl-to-hex');var ColorRGBToHex = require('./colors/rgb-to-hex');var ColorLongToShortHex = require('./colors/long-to-short-hex');var ImportInliner = require('./imports/inliner');var UrlRebase = require('./images/url-rebase');var EmptyRemoval = require('./selectors/empty-removal');var CommentsProcessor = require('./text/comments');var ExpressionsProcessor = require('./text/expressions');var FreeTextProcessor = require('./text/free');var UrlsProcessor = require('./text/urls');var NameQuotesProcessor = require('./text/name-quotes');var Splitter = require('./text/splitter');var SelectorsOptimizer = require('./selectors/optimizer');var CleanCSS = module.exports = function CleanCSS(options) {  options = options || {};  // back compat  if (!(this instanceof CleanCSS))    return new CleanCSS(options);  options.keepBreaks = options.keepBreaks || false;  //active by default  if (undefined === options.processImport)    options.processImport = true;  this.options = options;  this.stats = {};  this.context = {    errors: [],    warnings: [],    debug: options.debug  };  this.errors = this.context.errors;  this.warnings = this.context.warnings;  this.lineBreak = process.platform == 'win32' ? '\r\n' : '\n';};CleanCSS.prototype.minify = function(data, callback) {  var options = this.options;  if (Buffer.isBuffer(data))    data = data.toString();  if (options.processImport || data.indexOf('@shallow') > 0) {    // inline all imports    var self = this;    var runner = callback ?      process.nextTick :      function(callback) { return callback(); };    return runner(function() {      return new ImportInliner(self.context, options.inliner).process(data, {        localOnly: !callback,        root: options.root || process.cwd(),        relativeTo: options.relativeTo,        whenDone: function(data) {          return minify.call(self, data, callback);        }      });    });  } else {    return minify.call(this, data, callback);  }};var minify = function(data, callback) {  var startedAt;  var stats = this.stats;  var options = this.options;  var context = this.context;  var lineBreak = this.lineBreak;  var commentsProcessor = new CommentsProcessor(    context,    'keepSpecialComments' in options ? options.keepSpecialComments : '*',    options.keepBreaks,    lineBreak  );  var expressionsProcessor = new ExpressionsProcessor();  var freeTextProcessor = new FreeTextProcessor();  var urlsProcessor = new UrlsProcessor(context);  var nameQuotesProcessor = new NameQuotesProcessor();  if (options.debug) {    this.startedAt = process.hrtime();    this.stats.originalSize = data.length;  }  var replace = function() {    if (typeof arguments[0] == 'function')      arguments[0]();    else      data = data.replace.apply(data, arguments);  };  // replace function  if (options.benchmark) {    var originalReplace = replace;    replace = function(pattern, replacement) {      var name = typeof pattern == 'function' ?        /function (\w+)\(/.exec(pattern.toString())[1] :        pattern;      var start = process.hrtime();      originalReplace(pattern, replacement);      var itTook = process.hrtime(start);      console.log('%d ms: ' + name, 1000 * itTook[0] + itTook[1] / 1000000);    };  }  if (options.debug) {    startedAt = process.hrtime();    stats.originalSize = data.length;  }  replace(function escapeComments() {    data = commentsProcessor.escape(data);  });  // replace all escaped line breaks  replace(/\\(\r\n|\n)/gm, '');  // strip parentheses in urls if possible (no spaces inside)  replace(/url\((['"])([^\)]+)['"]\)/g, function(match, quote, url) {    var unsafeDataURI = url.indexOf('data:') === 0 && url.match(/data:\w+\/[^;]+;base64,/) === null;    if (url.match(/[ \t]/g) !== null || unsafeDataURI)      return 'url(' + quote + url + quote + ')';    else      return 'url(' + url + ')';  });  // strip parentheses in animation & font names  replace(function removeQuotes() {    data = nameQuotesProcessor.process(data);  });  // strip parentheses in @keyframes  replace(/@(\-moz\-|\-o\-|\-webkit\-)?keyframes ([^{]+)/g, function(match, prefix, name) {    prefix = prefix || '';    return '@' + prefix + 'keyframes ' + (name.indexOf(' ') > -1 ? name : name.replace(/['"]/g, ''));  });  // IE shorter filters, but only if single (IE 7 issue)  replace(/progid:DXImageTransform\.Microsoft\.(Alpha|Chroma)(\([^\)]+\))([;}'"])/g, function(match, filter, args, suffix) {    return filter.toLowerCase() + args + suffix;  });  replace(function escapeExpressions() {    data = expressionsProcessor.escape(data);  });  // strip parentheses in attribute values  replace(/\[([^\]]+)\]/g, function(match, content) {    var eqIndex = content.indexOf('=');    var singleQuoteIndex = content.indexOf('\'');    var doubleQuoteIndex = content.indexOf('"');    if (eqIndex < 0 && singleQuoteIndex < 0 && doubleQuoteIndex < 0)      return match;    if (singleQuoteIndex === 0 || doubleQuoteIndex === 0)      return match;    var key = content.substring(0, eqIndex);    var value = content.substring(eqIndex + 1, content.length);    if (/^['"](?:[a-zA-Z][a-zA-Z\d\-_]+)['"]$/.test(value))      return '[' + key + '=' + value.substring(1, value.length - 1) + ']';    else      return match;  });  replace(function escapeFreeText() {    data = freeTextProcessor.escape(data);  });  replace(function escapeUrls() {    data = urlsProcessor.escape(data);  });  // remove invalid special declarations  replace(/@charset [^;]+;/ig, function (match) {    return match.indexOf('@charset') > -1 ? match : '';  });  // whitespace inside attribute selectors brackets  replace(/\[([^\]]+)\]/g, function(match) {    return match.replace(/\s/g, '');  });  // line breaks  replace(/[\r]?\n/g, ' ');  // multiple whitespace  replace(/[\t ]+/g, ' ');  // multiple semicolons (with optional whitespace)  replace(/;[ ]?;+/g, ';');  // multiple line breaks to one  replace(/ (?:\r\n|\n)/g, lineBreak);  replace(/(?:\r\n|\n)+/g, lineBreak);  // remove spaces around selectors  replace(/ ([+~>]) /g, '$1');  // remove extra spaces inside content  replace(/([!\(\{\}:;=,\n]) /g, '$1');  replace(/ ([!\)\{\};=,\n])/g, '$1');  replace(/(?:\r\n|\n)\}/g, '}');  replace(/([\{;,])(?:\r\n|\n)/g, '$1');  replace(/ :([^\{\};]+)([;}])/g, ':$1$2');  // restore spaces inside IE filters (IE 7 issue)  replace(/progid:[^(]+\(([^\)]+)/g, function(match) {    return match.replace(/,/g, ', ');  });  // trailing semicolons  replace(/;\}/g, '}');  replace(function hsl2Hex() {    data = new ColorHSLToHex(data).process();  });  replace(function rgb2Hex() {    data = new ColorRGBToHex(data).process();  });  replace(function longToShortHex() {    data = new ColorLongToShortHex(data).process();  });  replace(function shortenColors() {    data = new ColorShortener(data).process();  });  // replace font weight with numerical value  replace(/(font\-weight|font):(normal|bold)([ ;\}!])(\w*)/g, function(match, property, weight, suffix, next) {    if (suffix == ' ' && (next.indexOf('/') > -1 || next == 'normal' || /[1-9]00/.test(next)))      return match;    if (weight == 'normal')      return property + ':400' + suffix + next;    else if (weight == 'bold')      return property + ':700' + suffix + next;    else      return match;  });  // minus zero to zero  // repeated twice on purpose as if not it doesn't process rgba(-0,-0,-0,-0) correctly  var zerosRegexp = /(\s|:|,|\()\-0([^\.])/g;  replace(zerosRegexp, '$10$2');  replace(zerosRegexp, '$10$2');  // zero(s) + value to value  replace(/(\s|:|,)0+([1-9])/g, '$1$2');  // round pixels to 2nd decimal place  var precision = 'roundingPrecision' in options ? options.roundingPrecision : 2;  var decimalMultiplier = Math.pow(10, precision);  replace(new RegExp('(\\d*\\.\\d{' + (precision + 1) + ',})px', 'g'), function(match, number) {    return Math.round(parseFloat(number) * decimalMultiplier) / decimalMultiplier + 'px';  });  // .0 to 0  // repeated twice on purpose as if not it doesn't process {padding: .0 .0 .0 .0} correctly  var leadingDecimalRegexp = /(\D)\.0+(\D)/g;  replace(leadingDecimalRegexp, '$10$2');  replace(leadingDecimalRegexp, '$10$2');  // fraction zeros removal  replace(/\.([1-9]*)0+(\D)/g, function(match, nonZeroPart, suffix) {    return (nonZeroPart.length > 0 ? '.' : '') + nonZeroPart + suffix;  });  // zero + unit to zero  var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%'];  if (['ie7', 'ie8'].indexOf(options.compatibility) == -1)    units.push('rem');  replace(new RegExp('(\\s|:|,)\\-?0(?:' + units.join('|') + ')', 'g'), '$1' + '0');  replace(new RegExp('(\\s|:|,)\\-?(\\d+)\\.(\\D)', 'g'), '$1$2$3');  replace(new RegExp('rect\\(0(?:' + units.join('|') + ')', 'g'), 'rect(0');  // restore % in rgb/rgba and hsl/hsla  replace(/(rgb|rgba|hsl|hsla)\(([^\)]+)\)/g, function(match, colorFunction, colorDef) {    var tokens = colorDef.split(',');    var applies = colorFunction == 'hsl' || colorFunction == 'hsla' || tokens[0].indexOf('%') > -1;    if (!applies)      return match;    if (tokens[1].indexOf('%') == -1)      tokens[1] += '%';    if (tokens[2].indexOf('%') == -1)      tokens[2] += '%';    return colorFunction + '(' + tokens.join(',') + ')';  });  // transparent rgba/hsla to 'transparent' unless in compatibility mode  if (!options.compatibility) {    replace(/:([^;]*)(?:rgba|hsla)\(0,0%?,0%?,0\)/g, function (match, prefix) {      if (new Splitter(',').split(match).pop().indexOf('gradient(') > -1)        return match;      return ':' + prefix + 'transparent';    });  }  // none to 0  replace(/outline:none/g, 'outline:0');  // background:none to background:0 0  replace(/background:(?:none|transparent)([;}])/g, 'background:0 0$1');  // multiple zeros into one  replace(/box-shadow:0 0 0 0([^\.])/g, 'box-shadow:0 0$1');  replace(/:0 0 0 0([^\.])/g, ':0$1');  replace(/([: ,=\-])0\.(\d)/g, '$1.$2');  // restore rect(...) zeros syntax for 4 zeros  replace(/rect\(\s?0(\s|,)0[ ,]0[ ,]0\s?\)/g, 'rect(0$10$10$10)');  // remove universal selector when not needed (*#id, *.class etc)  // pending a better fix  if (options.compatibility != 'ie7') {    replace(/([^,]?)(\*[^ \+\{]*\+html[^\{]*)(\{[^\}]*\})/g, function (match, prefix, selector, body) {      var notHackedSelectors = new Splitter(',').split(selector).filter(function (m) {        return !/^\*[^ \+\{]*\+html/.test(m);      });      return notHackedSelectors.length > 0 ?        prefix + notHackedSelectors.join(',') + body :        prefix;    });    replace(/\*([\.#:\[])/g, '$1');  }  // Restore spaces inside calc back  replace(/calc\([^\}]+\}/g, function(match) {    return match.replace(/\+/g, ' + ');  });  // get rid of IE hacks if not in compatibility mode  if (!options.compatibility)    replace(/([;\{])[\*_][\w\-]+:[^;\}]+/g, '$1');  if (options.noAdvanced) {    if (options.keepBreaks)      replace(/\}/g, '}' + lineBreak);  } else {    replace(function optimizeSelectors() {      data = new SelectorsOptimizer(data, context, {        keepBreaks: options.keepBreaks,        lineBreak: lineBreak,        compatibility: options.compatibility,        aggressiveMerging: !options.noAggressiveMerging      }).process();    });  }  // replace ' / ' in border-*-radius with '/'  replace(/(border-\w+-\w+-radius:\S+)\s+\/\s+/g, '$1/');  // replace same H/V values in border-radius  replace(/(border-\w+-\w+-radius):([^;\}]+)/g, function (match, property, value) {    var parts = value.split('/');    if (parts.length > 1 && parts[0] == parts[1])      return property + ':' + parts[0];    else      return match;  });  replace(function restoreUrls() {    data = urlsProcessor.restore(data);  });  replace(function rebaseUrls() {    data = options.noRebase ? data : new UrlRebase(options, context).process(data);  });  replace(function restoreFreeText() {    data = freeTextProcessor.restore(data);  });  replace(function restoreComments() {    data = commentsProcessor.restore(data);  });  replace(function restoreExpressions() {    data = expressionsProcessor.restore(data);  });  // move first charset to the beginning  replace(function moveCharset() {    // get first charset in stylesheet    var match = data.match(/@charset [^;]+;/);    var firstCharset = match ? match[0] : null;    if (!firstCharset)      return;    // reattach first charset and remove all subsequent    data = firstCharset +      (options.keepBreaks ? lineBreak : '') +      data.replace(new RegExp('@charset [^;]+;(' + lineBreak + ')?', 'g'), '').trim();  });  if (options.noAdvanced) {    replace(function removeEmptySelectors() {      data = new EmptyRemoval(data).process();    });  }  // trim spaces at beginning and end  data = data.trim();  if (options.debug) {    var elapsed = process.hrtime(startedAt);    stats.timeSpent = ~~(elapsed[0] * 1e3 + elapsed[1] / 1e6);    stats.efficiency = 1 - data.length / stats.originalSize;    stats.minifiedSize = data.length;  }  return callback ?    callback.call(this, this.context.errors.length > 0 ? this.context.errors : null, data) :    data;};
 |