text: /*!
   * async
   * https://github.com/caolan/async
   *
   * Copyright 2010-2014 Caolan McMahon
   * Released under the MIT license
   */
  (function () {
  
      var async = {};
      function noop() {}
      function identity(v) {
          return v;
      }
      function toBool(v) {
          return !!v;
      }
      function notId(v) {
          return !v;
      }
  
      // global on the server, window in the browser
      var previous_async;
  
      // Establish the root object, `window` (`self`) in the browser, `global`
      // on the server, or `this` in some virtual machines. We use `self`
      // instead of `window` for `WebWorker` support.
      var root = typeof self === 'object' && self.self === self && self ||
          typeof global === 'object' && global.global === global && global ||
          this;
  
      if (root != null) {
          previous_async = root.async;
      }
  
      async.noConflict = function () {
          root.async = previous_async;
          return async;
      };
  
      function only_once(fn) {
          return function() {
              if (fn === null) throw new Error("Callback was already called.");
              fn.apply(this, arguments);
              fn = null;
          };
      }
  
      function _once(fn) {
          return function() {
              if (fn === null) return;
              fn.apply(this, arguments);
              fn = null;
          };
      }
  
      //// cross-browser compatiblity functions ////
  
      var _toString = Object.prototype.toString;
  
      var _isArray = Array.isArray || function (obj) {
          return _toString.call(obj) === '[object Array]';
      };
  
      // Ported from underscore.js isObject
      var _isObject = function(obj) {
          var type = typeof obj;
          return type === 'function' || type === 'object' && !!obj;
      };
  
      function _isArrayLike(arr) {
          return _isArray(arr) || (
              // has a positive integer length property
              typeof arr.length === "number" &&
              arr.length >= 0 &&
              arr.length % 1 === 0
          );
      }
  
      function _arrayEach(arr, iterator) {
          var index = -1,
              length = arr.length;
  
          while (++index < length) {
              iterator(arr[index], index, arr);
          }
      }
  
      function _map(arr, iterator) {
          var index = -1,
              length = arr.length,
              result = Array(length);
  
          while (++index < length) {
              result[index] = iterator(arr[index], index, arr);
          }
          return result;
      }
  
      function _range(count) {
          return _map(Array(count), function (v, i) { return i; });
      }
  
      function _reduce(arr, iterator, memo) {
          _arrayEach(arr, function (x, i, a) {
              memo = iterator(memo, x, i, a);
          });
          return memo;
      }
  
      function _forEachOf(object, iterator) {
          _arrayEach(_keys(object), function (key) {
              iterator(object[key], key);
          });
      }
  
      function _indexOf(arr, item) {
          for (var i = 0; i < arr.length; i++) {
              if (arr[i] === item) return i;
          }
          return -1;
      }
  
      var _keys = Object.keys || function (obj) {
          var keys = [];
          for (var k in obj) {
              if (obj.hasOwnProperty(k)) {
                  keys.push(k);
              }
          }
          return keys;
      };
  
      function _keyIterator(coll) {
          var i = -1;
          var len;
          var keys;
          if (_isArrayLike(coll)) {
              len = coll.length;
              return function next() {
                  i++;
                  return i < len ? i : null;
              };
          } else {
              keys = _keys(coll);
              len = keys.length;
              return function next() {
                  i++;
                  return i < len ? keys[i] : null;
              };
          }
      }
  
      // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
      // This accumulates the arguments passed into an array, after a given index.
      // From underscore.js (https://github.com/jashkenas/underscore/pull/2140).
      function _restParam(func, startIndex) {
          startIndex = startIndex == null ? func.length - 1 : +startIndex;
          return function() {
              var length = Math.max(arguments.length - startIndex, 0);
              var rest = Array(length);
              for (var index = 0; index < length; index++) {
                  rest[index] = arguments[index + startIndex];
              }
              switch (startIndex) {
                  case 0: return func.call(this, rest);
                  case 1: return func.call(this, arguments[0], rest);
              }
              // Currently unused but handle cases outside of the switch statement:
              // var args = Array(startIndex + 1);
              // for (index = 0; index < startIndex; index++) {
              //     args[index] = arguments[index];
              // }
              // args[startIndex] = rest;
              // return func.apply(this, args);
          };
      }
  
      function _withoutIndex(iterator) {
          return function (value, index, callback) {
              return iterator(value, callback);
          };
      }
  
      //// exported async module functions ////
  
      //// nextTick implementation with browser-compatible fallback ////
  
      // capture the global reference to guard against fakeTimer mocks
      var _setImmediate = typeof setImmediate === 'function' && setImmediate;
  
      var _delay = _setImmediate ? function(fn) {
          // not a direct alias for IE10 compatibility
          _setImmediate(fn);
      } : function(fn) {
          setTimeout(fn, 0);
      };
  
      if (typeof process === 'object' && typeof process.nextTick === 'function') {
          async.nextTick = process.nextTick;
      } else {
          async.nextTick = _delay;
      }
      async.setImmediate = _setImmediate ? _delay : async.nextTick;
  
  
      async.forEach =
          async.each = function (arr, iterator, callback) {
              return async.eachOf(arr, _withoutIndex(iterator), callback);
          };
  
      async.forEachSeries =
          async.eachSeries = function (arr, iterator, callback) {
              return async.eachOfSeries(arr, _withoutIndex(iterator), callback);
          };
  
  
      async.forEachLimit =
          async.eachLimit = function (arr, limit, iterator, callback) {
              return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback);
          };
  
      async.forEachOf =
          async.eachOf = function (object, iterator, callback) {
              callback = _once(callback || noop);
              object = object || [];
  
              var iter = _keyIterator(object);
              var key, completed = 0;
  
              while ((key = iter()) != null) {
                  completed += 1;
                  iterator(object[key], key, only_once(done));
              }
  
              if (completed === 0) callback(null);
  
              function done(err) {
                  completed--;
                  if (err) {
                      callback(err);
                  }
                  // Check key is null in case iterator isn't exhausted
                  // and done resolved synchronously.
                  else if (key === null && completed <= 0) {
                      callback(null);
                  }
              }
          };
  
      async.forEachOfSeries =
          async.eachOfSeries = function (obj, iterator, callback) {
              callback = _once(callback || noop);
              obj = obj || [];
              var nextKey = _keyIterator(obj);
              var key = nextKey();
              function iterate() {
                  var sync = true;
                  if (key === null) {
                      return callback(null);
                  }
                  iterator(obj[key], key, only_once(function (err) {
                      if (err) {
                          callback(err);
                      }
                      else {
                          key = nextKey();
                          if (key === null) {
                              return callback(null);
                          } else {
                              if (sync) {
                                  async.setImmediate(iterate);
                              } else {
                                  iterate();
                              }
                          }
                      }
                  }));
                  sync = false;
              }
              iterate();
          };
  
  
  
      async.forEachOfLimit =
          async.eachOfLimit = function (obj, limit, iterator, callback) {
              _eachOfLimit(limit)(obj, iterator, callback);
          };
  
      function _eachOfLimit(limit) {
  
          return function (obj, iterator, callback) {
              callback = _once(callback || noop);
              obj = obj || [];
              var nextKey = _keyIterator(obj);
              if (limit <= 0) {
                  return callback(null);
              }
              var done = false;
              var running = 0;
              var errored = false;
  
              (function replenish () {
                  if (done && running <= 0) {
                      return callback(null);
                  }
  
                  while (running < limit && !errored) {
                      var key = nextKey();
                      if (key === null) {
                          done = true;
                          if (running <= 0) {
                              callback(null);
                          }
                          return;
                      }
                      running += 1;
                      iterator(obj[key], key, only_once(function (err) {
                          running -= 1;
                          if (err) {
                              callback(err);
                              errored = true;
                          }
                          else {
                              replenish();
                          }
                      }));
                  }
              })();
          };
      }
  
  
      function doParallel(fn) {
          return function (obj, iterator, callback) {
              return fn(async.eachOf, obj, iterator, callback);
          };
      }
      function doParallelLimit(fn) {
          return function (obj, limit, iterator, callback) {
              return fn(_eachOfLimit(limit), obj, iterator, callback);
          };
      }
      function doSeries(fn) {
          return function (obj, iterator, callback) {
              return fn(async.eachOfSeries, obj, iterator, callback);
          };
      }
  
      function _asyncMap(eachfn, arr, iterator, callback) {
          callback = _once(callback || noop);
          arr = arr || [];
          var results = _isArrayLike(arr) ? [] : {};
          eachfn(arr, function (value, index, callback) {
              iterator(value, function (err, v) {
                  results[index] = v;
                  callback(err);
              });
          }, function (err) {
              callback(err, results);
          });
      }
  
      async.map = doParallel(_asyncMap);
      async.mapSeries = doSeries(_asyncMap);
      async.mapLimit = doParallelLimit(_asyncMap);
  
      // reduce only has a series version, as doing reduce in parallel won't
      // work in many situations.
      async.inject =
          async.foldl =
              async.reduce = function (arr, memo, iterator, callback) {
                  async.eachOfSeries(arr, function (x, i, callback) {
                      iterator(memo, x, function (err, v) {
                          memo = v;
                          callback(err);
                      });
                  }, function (err) {
                      callback(err, memo);
                  });
              };
  
      async.foldr =
          async.reduceRight = function (arr, memo, iterator, callback) {
              var reversed = _map(arr, identity).reverse();
              async.reduce(reversed, memo, iterator, callback);
          };
  
      async.transform = function (arr, memo, iterator, callback) {
          if (arguments.length === 3) {
              callback = iterator;
              iterator = memo;
              memo = _isArray(arr) ? [] : {};
          }
  
          async.eachOf(arr, function(v, k, cb) {
              iterator(memo, v, k, cb);
          }, function(err) {
              callback(err, memo);
          });
      };
  
      function _filter(eachfn, arr, iterator, callback) {
          var results = [];
          eachfn(arr, function (x, index, callback) {
              iterator(x, function (v) {
                  if (v) {
                      results.push({index: index, value: x});
                  }
                  callback();
              });
          }, function () {
              callback(_map(results.sort(function (a, b) {
                  return a.index - b.index;
              }), function (x) {
                  return x.value;
              }));
          });
      }
  
      async.select =
          async.filter = doParallel(_filter);
  
      async.selectLimit =
          async.filterLimit = doParallelLimit(_filter);
  
      async.selectSeries =
          async.filterSeries = doSeries(_filter);
  
      function _reject(eachfn, arr, iterator, callback) {
          _filter(eachfn, arr, function(value, cb) {
              iterator(value, function(v) {
                  cb(!v);
              });
          }, callback);
      }
      async.reject = doParallel(_reject);
      async.rejectLimit = doParallelLimit(_reject);
      async.rejectSeries = doSeries(_reject);
  
      function _createTester(eachfn, check, getResult) {
          return function(arr, limit, iterator, cb) {
              function done() {
                  if (cb) cb(getResult(false, void 0));
              }
              function iteratee(x, _, callback) {
                  if (!cb) return callback();
                  iterator(x, function (v) {
                      if (cb && check(v)) {
                          cb(getResult(true, x));
                          cb = iterator = false;
                      }
                      callback();
                  });
              }
              if (arguments.length > 3) {
                  eachfn(arr, limit, iteratee, done);
              } else {
                  cb = iterator;
                  iterator = limit;
                  eachfn(arr, iteratee, done);
              }
          };
      }
  
      async.any =
          async.some = _createTester(async.eachOf, toBool, identity);
  
      async.someLimit = _createTester(async.eachOfLimit, toBool, identity);
  
      async.all =
          async.every = _createTester(async.eachOf, notId, notId);
  
      async.everyLimit = _createTester(async.eachOfLimit, notId, notId);
  
      function _findGetResult(v, x) {
          return x;
      }
      async.detect = _createTester(async.eachOf, identity, _findGetResult);
      async.detectSeries = _createTester(async.eachOfSeries, identity, _findGetResult);
      async.detectLimit = _createTester(async.eachOfLimit, identity, _findGetResult);
  
      async.sortBy = function (arr, iterator, callback) {
          async.map(arr, function (x, callback) {
              iterator(x, function (err, criteria) {
                  if (err) {
                      callback(err);
                  }
                  else {
                      callback(null, {value: x, criteria: criteria});
                  }
              });
          }, function (err, results) {
              if (err) {
                  return callback(err);
              }
              else {
                  callback(null, _map(results.sort(comparator), function (x) {
                      return x.value;
                  }));
              }
  
          });
  
          function comparator(left, right) {
              var a = left.criteria, b = right.criteria;
              return a < b ? -1 : a > b ? 1 : 0;
          }
      };
  
      async.auto = function (tasks, concurrency, callback) {
          if (typeof arguments[1] === 'function') {
              // concurrency is optional, shift the args.
              callback = concurrency;
              concurrency = null;
          }
          callback = _once(callback || noop);
          var keys = _keys(tasks);
          var remainingTasks = keys.length;
          if (!remainingTasks) {
              return callback(null);
          }
          if (!concurrency) {
              concurrency = remainingTasks;
          }
  
          var results = {};
          var runningTasks = 0;
  
          var hasError = false;
  
          var listeners = [];
          function addListener(fn) {
              listeners.unshift(fn);
          }
          function removeListener(fn) {
              var idx = _indexOf(listeners, fn);
              if (idx >= 0) listeners.splice(idx, 1);
          }
          function taskComplete() {
              remainingTasks--;
              _arrayEach(listeners.slice(0), function (fn) {
                  fn();
              });
          }
  
          addListener(function () {
              if (!remainingTasks) {
                  callback(null, results);
              }
          });
  
          _arrayEach(keys, function (k) {
              if (hasError) return;
              var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
              var taskCallback = _restParam(function(err, args) {
                  runningTasks--;
                  if (args.length <= 1) {
                      args = args[0];
                  }
                  if (err) {
                      var safeResults = {};
                      _forEachOf(results, function(val, rkey) {
                          safeResults[rkey] = val;
                      });
                      safeResults[k] = args;
                      hasError = true;
  
                      callback(err, safeResults);
                  }
                  else {
                      results[k] = args;
                      async.setImmediate(taskComplete);
                  }
              });
              var requires = task.slice(0, task.length - 1);
              // prevent dead-locks
              var len = requires.length;
              var dep;
              while (len--) {
                  if (!(dep = tasks[requires[len]])) {
                      throw new Error('Has nonexistent dependency in ' + requires.join(', '));
                  }
                  if (_isArray(dep) && _indexOf(dep, k) >= 0) {
                      throw new Error('Has cyclic dependencies');
                  }
              }
              function ready() {
                  return runningTasks < concurrency && _reduce(requires, function (a, x) {
                      return (a && results.hasOwnProperty(x));
                  }, true) && !results.hasOwnProperty(k);
              }
              if (ready()) {
                  runningTasks++;
                  task[task.length - 1](taskCallback, results);
              }
              else {
                  addListener(listener);
              }
              function listener() {
                  if (ready()) {
                      runningTasks++;
                      removeListener(listener);
                      task[task.length - 1](taskCallback, results);
                  }
              }
          });
      };
  
  
  
      async.retry = function(times, task, callback) {
          var DEFAULT_TIMES = 5;
          var DEFAULT_INTERVAL = 0;
  
          var attempts = [];
  
          var opts = {
              times: DEFAULT_TIMES,
              interval: DEFAULT_INTERVAL
          };
  
          function parseTimes(acc, t){
              if(typeof t === 'number'){
                  acc.times = parseInt(t, 10) || DEFAULT_TIMES;
              } else if(typeof t === 'object'){
                  acc.times = parseInt(t.times, 10) || DEFAULT_TIMES;
                  acc.interval = parseInt(t.interval, 10) || DEFAULT_INTERVAL;
              } else {
                  throw new Error('Unsupported argument type for \'times\': ' + typeof t);
              }
          }
  
          var length = arguments.length;
          if (length < 1 || length > 3) {
              throw new Error('Invalid arguments - must be either (task), (task, callback), (times, task) or (times, task, callback)');
          } else if (length <= 2 && typeof times === 'function') {
              callback = task;
              task = times;
          }
          if (typeof times !== 'function') {
              parseTimes(opts, times);
          }
          opts.callback = callback;
          opts.task = task;
  
          function wrappedTask(wrappedCallback, wrappedResults) {
              function retryAttempt(task, finalAttempt) {
                  return function(seriesCallback) {
                      task(function(err, result){
                          seriesCallback(!err || finalAttempt, {err: err, result: result});
                      }, wrappedResults);
                  };
              }
  
              function retryInterval(interval){
                  return function(seriesCallback){
                      setTimeout(function(){
                          seriesCallback(null);
                      }, interval);
                  };
              }
  
              while (opts.times) {
  
                  var finalAttempt = !(opts.times-=1);
                  attempts.push(retryAttempt(opts.task, finalAttempt));
                  if(!finalAttempt && opts.interval > 0){
                      attempts.push(retryInterval(opts.interval));
                  }
              }
  
              async.series(attempts, function(done, data){
                  data = data[data.length - 1];
                  (wrappedCallback || opts.callback)(data.err, data.result);
              });
          }
  
          // If a callback is passed, run this as a controll flow
          return opts.callback ? wrappedTask() : wrappedTask;
      };
  
      async.waterfall = function (tasks, callback) {
          callback = _once(callback || noop);
          if (!_isArray(tasks)) {
              var err = new Error('First argument to waterfall must be an array of functions');
              return callback(err);
          }
          if (!tasks.length) {
              return callback();
          }
          function wrapIterator(iterator) {
              return _restParam(function (err, args) {
                  if (err) {
                      callback.apply(null, [err].concat(args));
                  }
                  else {
                      var next = iterator.next();
                      if (next) {
                          args.push(wrapIterator(next));
                      }
                      else {
                          args.push(callback);
                      }
                      ensureAsync(iterator).apply(null, args);
                  }
              });
          }
          wrapIterator(async.iterator(tasks))();
      };
  
      function _parallel(eachfn, tasks, callback) {
          callback = callback || noop;
          var results = _isArrayLike(tasks) ? [] : {};
  
          eachfn(tasks, function (task, key, callback) {
              task(_restParam(function (err, args) {
                  if (args.length <= 1) {
                      args = args[0];
                  }
                  results[key] = args;
                  callback(err);
              }));
          }, function (err) {
              callback(err, results);
          });
      }
  
      async.parallel = function (tasks, callback) {
          _parallel(async.eachOf, tasks, callback);
      };
  
      async.parallelLimit = function(tasks, limit, callback) {
          _parallel(_eachOfLimit(limit), tasks, callback);
      };
  
      async.series = function(tasks, callback) {
          _parallel(async.eachOfSeries, tasks, callback);
      };
  
      async.iterator = function (tasks) {
          function makeCallback(index) {
              function fn() {
                  if (tasks.length) {
                      tasks[index].apply(null, arguments);
                  }
                  return fn.next();
              }
              fn.next = function () {
                  return (index < tasks.length - 1) ? makeCallback(index + 1): null;
              };
              return fn;
          }
          return makeCallback(0);
      };
  
      async.apply = _restParam(function (fn, args) {
          return _restParam(function (callArgs) {
              return fn.apply(
                  null, args.concat(callArgs)
              );
          });
      });
  
      function _concat(eachfn, arr, fn, callback) {
          var result = [];
          eachfn(arr, function (x, index, cb) {
              fn(x, function (err, y) {
                  result = result.concat(y || []);
                  cb(err);
              });
          }, function (err) {
              callback(err, result);
          });
      }
      async.concat = doParallel(_concat);
      async.concatSeries = doSeries(_concat);
  
      async.whilst = function (test, iterator, callback) {
          callback = callback || noop;
          if (test()) {
              var next = _restParam(function(err, args) {
                  if (err) {
                      callback(err);
                  } else if (test.apply(this, args)) {
                      iterator(next);
                  } else {
                      callback.apply(null, [null].concat(args));
                  }
              });
              iterator(next);
          } else {
              callback(null);
          }
      };
  
      async.doWhilst = function (iterator, test, callback) {
          var calls = 0;
          return async.whilst(function() {
              return ++calls <= 1 || test.apply(this, arguments);
          }, iterator, callback);
      };
  
      async.until = function (test, iterator, callback) {
          return async.whilst(function() {
              return !test.apply(this, arguments);
          }, iterator, callback);
      };
  
      async.doUntil = function (iterator, test, callback) {
          return async.doWhilst(iterator, function() {
              return !test.apply(this, arguments);
          }, callback);
      };
  
      async.during = function (test, iterator, callback) {
          callback = callback || noop;
  
          var next = _restParam(function(err, args) {
              if (err) {
                  callback(err);
              } else {
                  args.push(check);
                  test.apply(this, args);
              }
          });
  
          var check = function(err, truth) {
              if (err) {
                  callback(err);
              } else if (truth) {
                  iterator(next);
              } else {
                  callback(null);
              }
          };
  
          test(check);
      };
  
      async.doDuring = function (iterator, test, callback) {
          var calls = 0;
          async.during(function(next) {
              if (calls++ < 1) {
                  next(null, true);
              } else {
                  test.apply(this, arguments);
              }
          }, iterator, callback);
      };
  
      function _queue(worker, concurrency, payload) {
          if (concurrency == null) {
              concurrency = 1;
          }
          else if(concurrency === 0) {
              throw new Error('Concurrency must not be zero');
          }
          function _insert(q, data, pos, callback) {
              if (callback != null && typeof callback !== "function") {
                  throw new Error("task callback must be a function");
              }
              q.started = true;
              if (!_isArray(data)) {
                  data = [data];
              }
              if(data.length === 0 && q.idle()) {
                  // call drain immediately if there are no tasks
                  return async.setImmediate(function() {
                      q.drain();
                  });
              }
              _arrayEach(data, function(task) {
                  var item = {
                      data: task,
                      callback: callback || noop
                  };
  
                  if (pos) {
                      q.tasks.unshift(item);
                  } else {
                      q.tasks.push(item);
                  }
  
                  if (q.tasks.length === q.concurrency) {
                      q.saturated();
                  }
              });
              async.setImmediate(q.process);
          }
          function _next(q, tasks) {
              return function(){
                  workers -= 1;
  
                  var removed = false;
                  var args = arguments;
                  _arrayEach(tasks, function (task) {
                      _arrayEach(workersList, function (worker, index) {
                          if (worker === task && !removed) {
                              workersList.splice(index, 1);
                              removed = true;
                          }
                      });
  
                      task.callback.apply(task, args);
                  });
                  if (q.tasks.length + workers === 0) {
                      q.drain();
                  }
                  q.process();
              };
          }
  
          var workers = 0;
          var workersList = [];
          var q = {
              tasks: [],
              concurrency: concurrency,
              payload: payload,
              saturated: noop,
              empty: noop,
              drain: noop,
              started: false,
              paused: false,
              push: function (data, callback) {
                  _insert(q, data, false, callback);
              },
              kill: function () {
                  q.drain = noop;
                  q.tasks = [];
              },
              unshift: function (data, callback) {
                  _insert(q, data, true, callback);
              },
              process: function () {
                  while(!q.paused && workers < q.concurrency && q.tasks.length){
  
                      var tasks = q.payload ?
                          q.tasks.splice(0, q.payload) :
                          q.tasks.splice(0, q.tasks.length);
  
                      var data = _map(tasks, function (task) {
                          return task.data;
                      });
  
                      if (q.tasks.length === 0) {
                          q.empty();
                      }
                      workers += 1;
                      workersList.push(tasks[0]);
                      var cb = only_once(_next(q, tasks));
                      worker(data, cb);
                  }
              },
              length: function () {
                  return q.tasks.length;
              },
              running: function () {
                  return workers;
              },
              workersList: function () {
                  return workersList;
              },
              idle: function() {
                  return q.tasks.length + workers === 0;
              },
              pause: function () {
                  q.paused = true;
              },
              resume: function () {
                  if (q.paused === false) { return; }
                  q.paused = false;
                  var resumeCount = Math.min(q.concurrency, q.tasks.length);
                  // Need to call q.process once per concurrent
                  // worker to preserve full concurrency after pause
                  for (var w = 1; w <= resumeCount; w++) {
                      async.setImmediate(q.process);
                  }
              }
          };
          return q;
      }
  
      async.queue = function (worker, concurrency) {
          var q = _queue(function (items, cb) {
              worker(items[0], cb);
          }, concurrency, 1);
  
          return q;
      };
  
      async.priorityQueue = function (worker, concurrency) {
  
          function _compareTasks(a, b){
              return a.priority - b.priority;
          }
  
          function _binarySearch(sequence, item, compare) {
              var beg = -1,
                  end = sequence.length - 1;
              while (beg < end) {
                  var mid = beg + ((end - beg + 1) >>> 1);
                  if (compare(item, sequence[mid]) >= 0) {
                      beg = mid;
                  } else {
                      end = mid - 1;
                  }
              }
              return beg;
          }
  
          function _insert(q, data, priority, callback) {
              if (callback != null && typeof callback !== "function") {
                  throw new Error("task callback must be a function");
              }
              q.started = true;
              if (!_isArray(data)) {
                  data = [data];
              }
              if(data.length === 0) {
                  // call drain immediately if there are no tasks
                  return async.setImmediate(function() {
                      q.drain();
                  });
              }
              _arrayEach(data, function(task) {
                  var item = {
                      data: task,
                      priority: priority,
                      callback: typeof callback === 'function' ? callback : noop
                  };
  
                  q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
  
                  if (q.tasks.length === q.concurrency) {
                      q.saturated();
                  }
                  async.setImmediate(q.process);
              });
          }
  
          // Start with a normal queue
          var q = async.queue(worker, concurrency);
  
          // Override push to accept second parameter representing priority
          q.push = function (data, priority, callback) {
              _insert(q, data, priority, callback);
          };
  
          // Remove unshift function
          delete q.unshift;
  
          return q;
      };
  
      async.cargo = function (worker, payload) {
          return _queue(worker, 1, payload);
      };
  
      function _console_fn(name) {
          return _restParam(function (fn, args) {
              fn.apply(null, args.concat([_restParam(function (err, args) {
                  if (typeof console === 'object') {
                      if (err) {
                          if (console.error) {
                              console.error(err);
                          }
                      }
                      else if (console[name]) {
                          _arrayEach(args, function (x) {
                              console[name](x);
                          });
                      }
                  }
              })]));
          });
      }
      async.log = _console_fn('log');
      async.dir = _console_fn('dir');
      /*async.info = _console_fn('info');
      async.warn = _console_fn('warn');
      async.error = _console_fn('error');*/
  
      async.memoize = function (fn, hasher) {
          var memo = {};
          var queues = {};
          var has = Object.prototype.hasOwnProperty;
          hasher = hasher || identity;
          var memoized = _restParam(function memoized(args) {
              var callback = args.pop();
              var key = hasher.apply(null, args);
              if (has.call(memo, key)) {
                  async.setImmediate(function () {
                      callback.apply(null, memo[key]);
                  });
              }
              else if (has.call(queues, key)) {
                  queues[key].push(callback);
              }
              else {
                  queues[key] = [callback];
                  fn.apply(null, args.concat([_restParam(function (args) {
                      memo[key] = args;
                      var q = queues[key];
                      delete queues[key];
                      for (var i = 0, l = q.length; i < l; i++) {
                          q[i].apply(null, args);
                      }
                  })]));
              }
          });
          memoized.memo = memo;
          memoized.unmemoized = fn;
          return memoized;
      };
  
      async.unmemoize = function (fn) {
          return function () {
              return (fn.unmemoized || fn).apply(null, arguments);
          };
      };
  
      function _times(mapper) {
          return function (count, iterator, callback) {
              mapper(_range(count), iterator, callback);
          };
      }
  
      async.times = _times(async.map);
      async.timesSeries = _times(async.mapSeries);
      async.timesLimit = function (count, limit, iterator, callback) {
          return async.mapLimit(_range(count), limit, iterator, callback);
      };
  
      async.seq = function (/* functions... */) {
          var fns = arguments;
          return _restParam(function (args) {
              var that = this;
  
              var callback = args[args.length - 1];
              if (typeof callback == 'function') {
                  args.pop();
              } else {
                  callback = noop;
              }
  
              async.reduce(fns, args, function (newargs, fn, cb) {
                      fn.apply(that, newargs.concat([_restParam(function (err, nextargs) {
                          cb(err, nextargs);
                      })]));
                  },
                  function (err, results) {
                      callback.apply(that, [err].concat(results));
                  });
          });
      };
  
      async.compose = function (/* functions... */) {
          return async.seq.apply(null, Array.prototype.reverse.call(arguments));
      };
  
  
      function _applyEach(eachfn) {
          return _restParam(function(fns, args) {
              var go = _restParam(function(args) {
                  var that = this;
                  var callback = args.pop();
                  return eachfn(fns, function (fn, _, cb) {
                          fn.apply(that, args.concat([cb]));
                      },
                      callback);
              });
              if (args.length) {
                  return go.apply(this, args);
              }
              else {
                  return go;
              }
          });
      }
  
      async.applyEach = _applyEach(async.eachOf);
      async.applyEachSeries = _applyEach(async.eachOfSeries);
  
  
      async.forever = function (fn, callback) {
          var done = only_once(callback || noop);
          var task = ensureAsync(fn);
          function next(err) {
              if (err) {
                  return done(err);
              }
              task(next);
          }
          next();
      };
  
      function ensureAsync(fn) {
          return _restParam(function (args) {
              var callback = args.pop();
              args.push(function () {
                  var innerArgs = arguments;
                  if (sync) {
                      async.setImmediate(function () {
                          callback.apply(null, innerArgs);
                      });
                  } else {
                      callback.apply(null, innerArgs);
                  }
              });
              var sync = true;
              fn.apply(this, args);
              sync = false;
          });
      }
  
      async.ensureAsync = ensureAsync;
  
      async.constant = _restParam(function(values) {
          var args = [null].concat(values);
          return function (callback) {
              return callback.apply(this, args);
          };
      });
  
      async.wrapSync =
          async.asyncify = function asyncify(func) {
              return _restParam(function (args) {
                  var callback = args.pop();
                  var result;
                  try {
                      result = func.apply(this, args);
                  } catch (e) {
                      return callback(e);
                  }
                  // if result is Promise object
                  if (_isObject(result) && typeof result.then === "function") {
                      result.then(function(value) {
                          callback(null, value);
                      })["catch"](function(err) {
                          callback(err.message ? err : new Error(err));
                      });
                  } else {
                      callback(null, result);
                  }
              });
          };
  
      // Node.js
      if (typeof module === 'object' && module.exports) {
          module.exports = async;
      }
      // AMD / RequireJS
      else if (typeof define === 'function' && define.amd) {
          define([], function () {
              return async;
          });
      }
      // included directly via <script> tag
      else {
          root.async = async;
      }
  
  }());