(function(
  // Reliable reference to the global object (i.e. window in browsers).
  global,

  // Dummy constructor that we use as the .constructor property for
  // functions that return Generator objects.
  GeneratorFunction,

  // Undefined value, more compressible than void 0.
  undefined
) {
  var hasOwn = Object.prototype.hasOwnProperty;

  if (global.wrapGenerator) {
    return;
  }

  function wrapGenerator(innerFn, self, tryList) {
    return new Generator(innerFn, self || null, tryList || []);
  }

  global.wrapGenerator = wrapGenerator;
  if (typeof exports !== "undefined") {
    exports.wrapGenerator = wrapGenerator;
  }

  var GenStateSuspendedStart = "suspendedStart";
  var GenStateSuspendedYield = "suspendedYield";
  var GenStateExecuting = "executing";
  var GenStateCompleted = "completed";

  // Returning this object from the innerFn has the same effect as
  // breaking out of the dispatch switch statement.
  var ContinueSentinel = {};

  wrapGenerator.mark = function(genFun) {
    genFun.constructor = GeneratorFunction;
    return genFun;
  };

  // Ensure isGeneratorFunction works when Function#name not supported.
  if (GeneratorFunction.name !== "GeneratorFunction") {
    GeneratorFunction.name = "GeneratorFunction";
  }

  wrapGenerator.isGeneratorFunction = function(genFun) {
    var ctor = genFun && genFun.constructor;
    return ctor ? GeneratorFunction.name === ctor.name : false;
  };

  function Generator(innerFn, self, tryList) {
    var generator = this;
    var context = new Context(tryList);
    var state = GenStateSuspendedStart;

    function invoke(method, arg) {
      if (state === GenStateExecuting) {
        throw new Error("Generator is already running");
      }

      if (state === GenStateCompleted) {
        throw new Error("Generator has already finished");
      }

      while (true) {
        var delegate = context.delegate;
        if (delegate) {
          try {
            var info = delegate.generator[method](arg);

            // Delegate generator ran and handled its own exceptions so
            // regardless of what the method was, we continue as if it is
            // "next" with an undefined arg.
            method = "next";
            arg = undefined;

          } catch (uncaught) {
            context.delegate = null;

            // Like returning generator.throw(uncaught), but without the
            // overhead of an extra function call.
            method = "throw";
            arg = uncaught;

            continue;
          }

          if (info.done) {
            context[delegate.resultName] = info.value;
            context.next = delegate.nextLoc;
          } else {
            state = GenStateSuspendedYield;
            return info;
          }

          context.delegate = null;
        }

        if (method === "next") {
          if (state === GenStateSuspendedStart &&
              typeof arg !== "undefined") {
            // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
            throw new TypeError(
              "attempt to send " + JSON.stringify(arg) + " to newborn generator"
            );
          }

          if (state === GenStateSuspendedYield) {
            context.sent = arg;
          } else {
            delete context.sent;
          }

        } else if (method === "throw") {
          if (state === GenStateSuspendedStart) {
            state = GenStateCompleted;
            throw arg;
          }

          if (context.dispatchException(arg)) {
            // If the dispatched exception was caught by a catch block,
            // then let that catch block handle the exception normally.
            method = "next";
            arg = undefined;
          }
        }

        state = GenStateExecuting;

        try {
          var value = innerFn.call(self, context);

          // If an exception is thrown from innerFn, we leave state ===
          // GenStateExecuting and loop back for another invocation.
          state = context.done
            ? GenStateCompleted
            : GenStateSuspendedYield;

          var info = {
            value: value,
            done: context.done
          };

          if (value === ContinueSentinel) {
            if (context.delegate && method === "next") {
              // Deliberately forget the last sent value so that we don't
              // accidentally pass it on to the delegate.
              arg = undefined;
            }
          } else {
            return info;
          }

        } catch (thrown) {
          state = GenStateCompleted;

          if (method === "next") {
            context.dispatchException(thrown);
          } else {
            arg = thrown;
          }
        }
      }
    }

    generator.next = invoke.bind(generator, "next");
    generator.throw = invoke.bind(generator, "throw");
  }

  Generator.prototype.toString = function() {
    return "[object Generator]";
  };

  function pushTryEntry(triple) {
    var entry = { tryLoc: triple[0] };

    if (1 in triple) {
      entry.catchLoc = triple[1];
    }

    if (2 in triple) {
      entry.finallyLoc = triple[2];
    }

    this.tryEntries.push(entry);
  }

  function resetTryEntry(entry, i) {
    var record = entry.completion || {};
    record.type = i === 0 ? "normal" : "return";
    delete record.arg;
    entry.completion = record;
  }

  function Context(tryList) {
    // The root entry object (effectively a try statement without a catch
    // or a finally block) gives us a place to store values thrown from
    // locations where there is no enclosing try statement.
    this.tryEntries = [{ tryLoc: "root" }];
    tryList.forEach(pushTryEntry, this);
    this.reset();
  }

  Context.prototype = {
    constructor: Context,

    reset: function() {
      this.prev = 0;
      this.next = 0;
      this.sent = undefined;
      this.done = false;
      this.delegate = null;

      this.tryEntries.forEach(resetTryEntry);

      // Pre-initialize at least 20 temporary variables to enable hidden
      // class optimizations for simple generators.
      for (var tempIndex = 0, tempName;
           hasOwn.call(this, tempName = "t" + tempIndex) || tempIndex < 20;
           ++tempIndex) {
        this[tempName] = null;
      }
    },

    stop: function() {
      this.done = true;

      var rootEntry = this.tryEntries[0];
      var rootRecord = rootEntry.completion;
      if (rootRecord.type === "throw") {
        throw rootRecord.arg;
      }

      return this.rval;
    },

    keys: function(object) {
      var keys = [];
      for (var key in object) {
        keys.push(key);
      }
      keys.reverse();

      // Rather than returning an object with a next method, we keep
      // things simple and return the next function itself.
      return function next() {
        while (keys.length) {
          var key = keys.pop();
          if (key in object) {
            next.value = key;
            next.done = false;
            return next;
          }
        }

        // To avoid creating an additional object, we just hang the .value
        // and .done properties off the next function object itself. This
        // also ensures that the minifier will not anonymize the function.
        next.done = true;
        return next;
      };
    },

    dispatchException: function(exception) {
      if (this.done) {
        throw exception;
      }

      var context = this;
      function handle(loc, caught) {
        record.type = "throw";
        record.arg = exception;
        context.next = loc;
        return !!caught;
      }

      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
        var entry = this.tryEntries[i];
        var record = entry.completion;

        if (entry.tryLoc === "root") {
          // Exception thrown outside of any try block that could handle
          // it, so set the completion value of the entire function to
          // throw the exception.
          return handle("end");
        }

        if (entry.tryLoc <= this.prev) {
          var hasCatch = hasOwn.call(entry, "catchLoc");
          var hasFinally = hasOwn.call(entry, "finallyLoc");

          if (hasCatch && hasFinally) {
            if (this.prev < entry.catchLoc) {
              return handle(entry.catchLoc, true);
            } else if (this.prev < entry.finallyLoc) {
              return handle(entry.finallyLoc);
            }

          } else if (hasCatch) {
            if (this.prev < entry.catchLoc) {
              return handle(entry.catchLoc, true);
            }

          } else if (hasFinally) {
            if (this.prev < entry.finallyLoc) {
              return handle(entry.finallyLoc);
            }

          } else {
            throw new Error("try statement without catch or finally");
          }
        }
      }
    },

    _findFinallyEntry: function(finallyLoc) {
      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
        var entry = this.tryEntries[i];
        if (entry.tryLoc <= this.prev &&
            hasOwn.call(entry, "finallyLoc") && (
              entry.finallyLoc === finallyLoc ||
              this.prev < entry.finallyLoc)) {
          return entry;
        }
      }
    },

    abrupt: function(type, arg) {
      var entry = this._findFinallyEntry();
      var record = entry ? entry.completion : {};

      record.type = type;
      record.arg = arg;

      if (entry) {
        this.next = entry.finallyLoc;
      } else {
        this.complete(record);
      }

      return ContinueSentinel;
    },

    complete: function(record) {
      if (record.type === "throw") {
        throw record.arg;
      }

      if (record.type === "break" ||
          record.type === "continue") {
        this.next = record.arg;
      } else if (record.type === "return") {
        this.rval = record.arg;
        this.next = "end";
      }

      return ContinueSentinel;
    },

    finish: function(finallyLoc) {
      var entry = this._findFinallyEntry(finallyLoc);
      return this.complete(entry.completion);
    },

    "catch": function(tryLoc) {
      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
        var entry = this.tryEntries[i];
        if (entry.tryLoc === tryLoc) {
          var record = entry.completion;
          if (record.type === "throw") {
            var thrown = record.arg;
            resetTryEntry(entry, i);
          }
          return thrown;
        }
      }

      // The context.catch method must only be called with a location
      // argument that corresponds to a known catch block.
      throw new Error("illegal catch attempt");
    },

    delegateYield: function(generator, resultName, nextLoc) {
      this.delegate = {
        generator: generator,
        resultName: resultName,
        nextLoc: nextLoc
      };

      return ContinueSentinel;
    }
  };
}).apply(this, Function("return [this, function GeneratorFunction(){}]")());

var fs = require('graceful-fs');

/**
 * Check if a file exists. Throws if it does not.
 * Mostly just for a nicer error message.
 *
 * @param {String} filename
 * @return {Object}
 * @api public
 */

exports.exists = wrapGenerator.mark(function(filename) {
  return wrapGenerator(function($ctx0) {
    while (1) switch ($ctx0.prev = $ctx0.next) {
    case 0:
      $ctx0.prev = 0;
      $ctx0.next = 3;
      return fs.stat.bind(null, filename);
    case 3:
      return $ctx0.abrupt("return", $ctx0.sent);
    case 6:
      $ctx0.prev = 6;
      $ctx0.t0 = $ctx0.catch(0);

      if (!($ctx0.t0.code === "ENOENT")) {
        $ctx0.next = 10;
        break;
      }

      throw new Error("file \"" + filename + "\" does not exist.");
    case 10:
      throw $ctx0.t0;
    case 11:
    case "end":
      return $ctx0.stop();
    }
  }, this, [[0, 6]]);
});

/**
 * Unlink a file. Ignores errors incase it doesn't exist.
 *
 * @param {String} filename
 * @api public
 */

exports.unlink = wrapGenerator.mark(function(filename) {
  return wrapGenerator(function($ctx1) {
    while (1) switch ($ctx1.prev = $ctx1.next) {
    case 0:
      $ctx1.prev = 0;
      $ctx1.next = 3;
      return fs.unlink.bind(null, filename);
    case 3:
      $ctx1.next = 10;
      break;
    case 5:
      $ctx1.prev = 5;
      $ctx1.t1 = $ctx1.catch(0);

      if (!($ctx1.t1.code === "ENOENT")) {
        $ctx1.next = 9;
        break;
      }

      return $ctx1.abrupt("return");
    case 9:
      throw $ctx1.t1;
    case 10:
    case "end":
      return $ctx1.stop();
    }
  }, this, [[0, 5]]);
});

/**
 * This is how the url rewriter and file copy/symlink will rewrite the file names.
 * This will create names like github's with `/`s.
 * i.e. fortawesome/fontawesome/v4.0.3/fonts/font.woff
 * and, for local components, lib/my-local-component/image.png
 *
 * @param {Object} branch
 * @return {String}
 * @api public
 */

exports.rewriteUrl = function (branch) {
  if (branch.type === 'local') return (branch.relativePath || branch.name).replace(/\\/g, '/');
  if (branch.type === 'dependency') return branch.name + '/' + branch.ref;
}

/**
 * Strip leading `./` from filenames.
 *
 * @param {String} filename
 * @return {String}
 * @api public
 */

exports.stripLeading = function (x) {
  if (x.slice(0, 2) === './') return x.slice(2);
  return x;
}

/**
 * Check if an object is a Generator Function.
 *
 * @param {Object} obj
 * @return {Boolean}
 * @api private
 */

exports.isGeneratorFunction = function (obj) {
  return obj
    && obj.constructor
    && 'GeneratorFunction' === obj.constructor.name;
}

/*
 * css-url-rewriter
 * https://github.com/callumlocke/css-url-rewriter
 *
 * Copyright (c) 2014 Callum Locke
 * Licensed under the MIT license.
 */

// Regex to find CSS properties that contain URLs
// Fiddle: http://refiddle.com/refiddles/css-url-matcher
// Railroad: http://goo.gl/LXpk52
var cssPropertyMatcher = /@import[^;]*|[;\s]?\*?[a-zA-Z\-]+\s*\:\#?[^;}]*url\(\s*['"]?[^'"\)\s]+['"]?\s*\)[^;}]*/g;

// Regex to find the URLs within a CSS property value
// Fiddle: http://refiddle.com/refiddles/match-multiple-urls-within-a-css-property-value
// Railroad: http://goo.gl/vQzMcg
var urlMatcher = /url\(\s*['"]?([^)'"]+)['"]?\s*\)/g;

var defaults = {
  excludeProperties: ['behavior', '*behavior']
};

exports.rewriteCSSURLs = function rewriteCSSURLs(css, settings, rewriterFn) {
  // Normalise arguments and settings
  if (typeof settings === 'function') {
    rewriterFn = settings;
    settings = defaults;
  }

  // Return the modified CSS
  var result = css.toString().replace(cssPropertyMatcher, function(property) {
    // This function deals with an individual CSS property.

    // If this property is excluded, return it unchanged
    if (settings.excludeProperties.length) {
      var propertyName = property.split(':')[0].replace(/^\s+|\s+$/g, '');

      for (var i = settings.excludeProperties.length - 1; i >= 0; i--) {
        if (propertyName.indexOf(settings.excludeProperties[i]) === 0) {
          return property;
        }
      }
    }

    // Return the property with the URL rewritten
    return property.replace(urlMatcher, function(urlFunc, justURL) {
      return urlFunc.replace(justURL, rewriterFn(justURL));
    });
  });

  return result;
};
