text: /**
   * Clean-css - https://github.com/jakubpawlowicz/clean-css
   * Released under the terms of MIT license
   *
   * Copyright (C) 2014 JakubPawlowicz.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;
  
    if(options.simple){
        return simpleMinify.call(this,data,callback);
    }
  
    var commentsProcessor = new CommentsProcessor(
      'keepSpecialComments' in options ? options.keepSpecialComments : '*',
      options.keepBreaks,
      lineBreak
    );
    var expressionsProcessor = new ExpressionsProcessor();
    var freeTextProcessor = new FreeTextProcessor();
    var urlsProcessor = new UrlsProcessor();
    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{' + (precision + 1) + ',})px', 'g'), function(match, decimalPlaces) {
      return precision === 0 ?
        'px' :
        '.' + Math.round(parseFloat('.' + decimalPlaces) * 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)
    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;
  };
  function simpleMinify (data, callback) {
    var startedAt;
    var stats = this.stats;
    var options = this.options;
    var lineBreak = this.lineBreak;
  
    var commentsProcessor = new CommentsProcessor(
      'keepSpecialComments' in options ? options.keepSpecialComments : '*',
      options.keepBreaks,
      lineBreak
    );
  
    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, '');
     // 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');
  
    // trailing semicolons
    replace(/;\}/g, '}');
  
    // 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 Comments
    replace(function restoreComments() {
      data = commentsProcessor.restore(data);
    });
  
    // 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;
  }
  