diff --git a/bin/pixi.js b/bin/pixi.js index 3d044ab..bfef279 100644 --- a/bin/pixi.js +++ b/bin/pixi.js @@ -1,7 +1,7 @@ /** * @license * pixi.js - v3.0.9-dev - * Compiled 2015-12-15T09:41:59.125Z + * Compiled 2015-12-15T22:28:08.934Z * * pixi.js is licensed under the MIT License. * http://www.opensource.org/licenses/mit-license.php @@ -112,12 +112,6 @@ ); } - function _each(coll, iterator) { - return _isArrayLike(coll) ? - _arrayEach(coll, iterator) : - _forEachOf(coll, iterator); - } - function _arrayEach(arr, iterator) { var index = -1, length = arr.length; @@ -265,23 +259,26 @@ async.eachOf = function (object, iterator, callback) { callback = _once(callback || noop); object = object || []; - var size = _isArrayLike(object) ? object.length : _keys(object).length; - var completed = 0; - if (!size) { - return callback(null); - } - _each(object, function (value, key) { + + 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); } - else { - completed += 1; - if (completed >= size) { - callback(null); - } + // Check key is null in case iterator isn't exhausted + // and done resolved synchronously. + else if (key === null && completed <= 0) { + callback(null); } } }; @@ -307,7 +304,7 @@ return callback(null); } else { if (sync) { - async.nextTick(iterate); + async.setImmediate(iterate); } else { iterate(); } @@ -388,7 +385,8 @@ function _asyncMap(eachfn, arr, iterator, callback) { callback = _once(callback || noop); - var results = []; + arr = arr || []; + var results = _isArrayLike(arr) ? [] : {}; eachfn(arr, function (value, index, callback) { iterator(value, function (err, v) { results[index] = v; @@ -414,7 +412,7 @@ callback(err); }); }, function (err) { - callback(err || null, memo); + callback(err, memo); }); }; @@ -424,6 +422,20 @@ 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) { @@ -532,15 +544,24 @@ } }; - async.auto = function (tasks, callback) { + async.auto = function (tasks, concurrency, callback) { + if (!callback) { + // 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 listeners = []; function addListener(fn) { @@ -566,6 +587,7 @@ _arrayEach(keys, function (k) { var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]]; var taskCallback = _restParam(function(err, args) { + runningTasks--; if (args.length <= 1) { args = args[0]; } @@ -595,11 +617,12 @@ } } function ready() { - return _reduce(requires, function (a, x) { + 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 { @@ -607,6 +630,7 @@ } function listener() { if (ready()) { + runningTasks++; removeListener(listener); task[task.length - 1](taskCallback, results); } @@ -898,8 +922,17 @@ 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) { @@ -910,6 +943,7 @@ } var workers = 0; + var workersList = []; var q = { tasks: [], concurrency: concurrency, @@ -944,6 +978,7 @@ q.empty(); } workers += 1; + workersList.push(tasks[0]); var cb = only_once(_next(q, tasks)); worker(data, cb); } @@ -955,6 +990,9 @@ running: function () { return workers; }, + workersList: function () { + return workersList; + }, idle: function() { return q.tasks.length + workers === 0; }, @@ -1083,7 +1121,7 @@ var callback = args.pop(); var key = hasher.apply(null, args); if (key in memo) { - async.nextTick(function () { + async.setImmediate(function () { callback.apply(null, memo[key]); }); } @@ -1582,7 +1620,7 @@ },{}],4:[function(require,module,exports){ (function (global){ -/*! https://mths.be/punycode v1.3.2 by @mathias */ +/*! https://mths.be/punycode v1.4.0 by @mathias */ ;(function(root) { /** Detect free variables */ @@ -1648,7 +1686,7 @@ * @returns {Error} Throws a `RangeError` with the applicable error message. */ function error(type) { - throw RangeError(errors[type]); + throw new RangeError(errors[type]); } /** @@ -1795,7 +1833,7 @@ /** * Bias adaptation function as per section 3.4 of RFC 3492. - * http://tools.ietf.org/html/rfc3492#section-3.4 + * https://tools.ietf.org/html/rfc3492#section-3.4 * @private */ function adapt(delta, numPoints, firstTime) { @@ -2100,14 +2138,17 @@ return punycode; }); } else if (freeExports && freeModule) { - if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+ + if (module.exports == freeExports) { + // in Node.js, io.js, or RingoJS v0.8.0+ freeModule.exports = punycode; - } else { // in Narwhal or RingoJS v0.7.0- + } else { + // in Narwhal or RingoJS v0.7.0- for (key in punycode) { punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); } } - } else { // in Rhino or a web browser + } else { + // in Rhino or a web browser root.punycode = punycode; } @@ -3014,7 +3055,7 @@ var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[0] * dim : data.length, - outerNode = filterPoints(data, linkedList(data, 0, outerLen, dim, true)), + outerNode = linkedList(data, 0, outerLen, dim, true), triangles = []; if (!outerNode) return triangles; @@ -3041,7 +3082,7 @@ size = Math.max(maxX - minX, maxY - minY); } - earcutLinked(data, outerNode, triangles, dim, minX, minY, size); + earcutLinked(outerNode, triangles, dim, minX, minY, size); return triangles; } @@ -3059,51 +3100,44 @@ // link points into circular doubly-linked list in the specified winding order if (clockwise === (sum > 0)) { - for (i = start; i < end; i += dim) last = insertNode(i, last); + for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { - for (i = end - dim; i >= start; i -= dim) last = insertNode(i, last); + for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } return last; } // eliminate colinear or duplicate points -function filterPoints(data, start, end) { +function filterPoints(start, end) { + if (!start) return start; if (!end) end = start; - var node = start, + var p = start, again; do { again = false; - if (!node.steiner && (equals(data, node.i, node.next.i) || orient(data, node.prev.i, node.i, node.next.i) === 0)) { - - // remove node - node.prev.next = node.next; - node.next.prev = node.prev; - - if (node.prevZ) node.prevZ.nextZ = node.nextZ; - if (node.nextZ) node.nextZ.prevZ = node.prevZ; - - node = end = node.prev; - - if (node === node.next) return null; + if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { + removeNode(p); + p = end = p.prev; + if (p === p.next) return null; again = true; } else { - node = node.next; + p = p.next; } - } while (again || node !== end); + } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) -function earcutLinked(data, ear, triangles, dim, minX, minY, size, pass) { +function earcutLinked(ear, triangles, dim, minX, minY, size, pass) { if (!ear) return; // interlink polygon nodes in z-order - if (!pass && minX !== undefined) indexCurve(data, ear, minX, minY, size); + if (!pass && size) indexCurve(ear, minX, minY, size); var stop = ear, prev, next; @@ -3113,18 +3147,13 @@ prev = ear.prev; next = ear.next; - if (isEar(data, ear, minX, minY, size)) { + if (size ? isEarHashed(ear, minX, minY, size) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim); triangles.push(ear.i / dim); triangles.push(next.i / dim); - // remove ear node - next.prev = prev; - prev.next = next; - - if (ear.prevZ) ear.prevZ.nextZ = ear.nextZ; - if (ear.nextZ) ear.nextZ.prevZ = ear.prevZ; + removeNode(ear); // skipping the next vertice leads to less sliver triangles ear = next.next; @@ -3139,16 +3168,16 @@ if (ear === stop) { // try filtering points and slicing again if (!pass) { - earcutLinked(data, filterPoints(data, ear), triangles, dim, minX, minY, size, 1); + earcutLinked(filterPoints(ear), triangles, dim, minX, minY, size, 1); // if this didn't work, try curing all small self-intersections locally } else if (pass === 1) { - ear = cureLocalIntersections(data, ear, triangles, dim); - earcutLinked(data, ear, triangles, dim, minX, minY, size, 2); + ear = cureLocalIntersections(ear, triangles, dim); + earcutLinked(ear, triangles, dim, minX, minY, size, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { - splitEarcut(data, ear, triangles, dim, minX, minY, size); + splitEarcut(ear, triangles, dim, minX, minY, size); } break; @@ -3157,163 +3186,109 @@ } // check whether a polygon node forms a valid ear with adjacent nodes -function isEar(data, ear, minX, minY, size) { +function isEar(ear) { + var a = ear.prev, + b = ear, + c = ear.next; - var a = ear.prev.i, - b = ear.i, - c = ear.next.i, + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear - ax = data[a], ay = data[a + 1], - bx = data[b], by = data[b + 1], - cx = data[c], cy = data[c + 1], + // now make sure we don't have other points inside the potential ear + var p = ear.next.next; - abd = ax * by - ay * bx, - acd = ax * cy - ay * cx, - cbd = cx * by - cy * bx, - A = abd - acd - cbd; + while (p !== ear.prev) { + if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && + area(p.prev, p, p.next) >= 0) return false; + p = p.next; + } - if (A <= 0) return false; // reflex, can't be an ear + return true; +} - // now make sure we don't have other points inside the potential ear; - // the code below is a bit verbose and repetitive but this is done for performance +function isEarHashed(ear, minX, minY, size) { + var a = ear.prev, + b = ear, + c = ear.next; - var cay = cy - ay, - acx = ax - cx, - aby = ay - by, - bax = bx - ax, - i, px, py, s, t, k, node; + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear - // if we use z-order curve hashing, iterate through the curve - if (minX !== undefined) { + // triangle bbox; min & max are calculated like this for speed + var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x), + minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y), + maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x), + maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y); - // triangle bbox; min & max are calculated like this for speed - var minTX = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx), - minTY = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy), - maxTX = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx), - maxTY = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy), + // z-order range for the current triangle bbox; + var minZ = zOrder(minTX, minTY, minX, minY, size), + maxZ = zOrder(maxTX, maxTY, minX, minY, size); - // z-order range for the current triangle bbox; - minZ = zOrder(minTX, minTY, minX, minY, size), - maxZ = zOrder(maxTX, maxTY, minX, minY, size); + // first look for points inside the triangle in increasing z-order + var p = ear.nextZ; - // first look for points inside the triangle in increasing z-order - node = ear.nextZ; + while (p && p.z <= maxZ) { + if (p !== ear.prev && p !== ear.next && + pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && + area(p.prev, p, p.next) >= 0) return false; + p = p.nextZ; + } - while (node && node.z <= maxZ) { - i = node.i; - node = node.nextZ; - if (i === a || i === c) continue; + // then look for points in decreasing z-order + p = ear.prevZ; - px = data[i]; - py = data[i + 1]; - - s = cay * px + acx * py - acd; - if (s >= 0) { - t = aby * px + bax * py + abd; - if (t >= 0) { - k = A - s - t; - if ((k >= 0) && ((s && t) || (s && k) || (t && k))) return false; - } - } - } - - // then look for points in decreasing z-order - node = ear.prevZ; - - while (node && node.z >= minZ) { - i = node.i; - node = node.prevZ; - if (i === a || i === c) continue; - - px = data[i]; - py = data[i + 1]; - - s = cay * px + acx * py - acd; - if (s >= 0) { - t = aby * px + bax * py + abd; - if (t >= 0) { - k = A - s - t; - if ((k >= 0) && ((s && t) || (s && k) || (t && k))) return false; - } - } - } - - // if we don't use z-order curve hash, simply iterate through all other points - } else { - node = ear.next.next; - - while (node !== ear.prev) { - i = node.i; - node = node.next; - - px = data[i]; - py = data[i + 1]; - - s = cay * px + acx * py - acd; - if (s >= 0) { - t = aby * px + bax * py + abd; - if (t >= 0) { - k = A - s - t; - if ((k >= 0) && ((s && t) || (s && k) || (t && k))) return false; - } - } - } + while (p && p.z >= minZ) { + if (p !== ear.prev && p !== ear.next && + pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && + area(p.prev, p, p.next) >= 0) return false; + p = p.prevZ; } return true; } // go through all polygon nodes and cure small local self-intersections -function cureLocalIntersections(data, start, triangles, dim) { - var node = start; +function cureLocalIntersections(start, triangles, dim) { + var p = start; do { - var a = node.prev, - b = node.next.next; + var a = p.prev, + b = p.next.next; // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) - if (a.i !== b.i && intersects(data, a.i, node.i, node.next.i, b.i) && - locallyInside(data, a, b) && locallyInside(data, b, a)) { + if (intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim); - triangles.push(node.i / dim); + triangles.push(p.i / dim); triangles.push(b.i / dim); // remove two nodes involved - a.next = b; - b.prev = a; + removeNode(p); + removeNode(p.next); - var az = node.prevZ, - bz = node.nextZ && node.nextZ.nextZ; - - if (az) az.nextZ = bz; - if (bz) bz.prevZ = az; - - node = start = b; + p = start = b; } - node = node.next; - } while (node !== start); + p = p.next; + } while (p !== start); - return node; + return p; } // try splitting polygon into two and triangulate them independently -function splitEarcut(data, start, triangles, dim, minX, minY, size) { +function splitEarcut(start, triangles, dim, minX, minY, size) { // look for a valid diagonal that divides the polygon into two var a = start; do { var b = a.next.next; while (b !== a.prev) { - if (a.i !== b.i && isValidDiagonal(data, a, b)) { + if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal var c = splitPolygon(a, b); // filter colinear points around the cuts - a = filterPoints(data, a, a.next); - c = filterPoints(data, c, c.next); + a = filterPoints(a, a.next); + c = filterPoints(c, c.next); // run earcut on each half - earcutLinked(data, a, triangles, dim, minX, minY, size); - earcutLinked(data, c, triangles, dim, minX, minY, size); + earcutLinked(a, triangles, dim, minX, minY, size); + earcutLinked(c, triangles, dim, minX, minY, size); return; } b = b.next; @@ -3332,121 +3307,98 @@ end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; - list = filterPoints(data, list); - if (list) queue.push(getLeftmost(data, list)); + queue.push(getLeftmost(list)); } - queue.sort(function (a, b) { - return data[a.i] - data[b.i]; - }); + queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { - eliminateHole(data, queue[i], outerNode); - outerNode = filterPoints(data, outerNode, outerNode.next); + eliminateHole(queue[i], outerNode); + outerNode = filterPoints(outerNode, outerNode.next); } return outerNode; } +function compareX(a, b) { + return a.x - b.x; +} + // find a bridge between vertices that connects hole with an outer ring and and link it -function eliminateHole(data, holeNode, outerNode) { - outerNode = findHoleBridge(data, holeNode, outerNode); +function eliminateHole(hole, outerNode) { + outerNode = findHoleBridge(hole, outerNode); if (outerNode) { - var b = splitPolygon(outerNode, holeNode); - filterPoints(data, b, b.next); + var b = splitPolygon(outerNode, hole); + filterPoints(b, b.next); } } // David Eberly's algorithm for finding a bridge between hole and outer polygon -function findHoleBridge(data, holeNode, outerNode) { - var node = outerNode, - i = holeNode.i, - px = data[i], - py = data[i + 1], - qMax = -Infinity, - mNode, a, b; +function findHoleBridge(hole, outerNode) { + var p = outerNode, + hx = hole.x, + hy = hole.y, + qx = -Infinity, + m; // find a segment intersected by a ray from the hole's leftmost point to the left; // segment's endpoint with lesser x will be potential connection point do { - a = node.i; - b = node.next.i; - - if (py <= data[a + 1] && py >= data[b + 1]) { - var qx = data[a] + (py - data[a + 1]) * (data[b] - data[a]) / (data[b + 1] - data[a + 1]); - if (qx <= px && qx > qMax) { - qMax = qx; - mNode = data[a] < data[b] ? node : node.next; + if (hy <= p.y && hy >= p.next.y) { + var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); + if (x <= hx && x > qx) { + qx = x; + m = p.x < p.next.x ? p : p.next; } } - node = node.next; - } while (node !== outerNode); + p = p.next; + } while (p !== outerNode); - if (!mNode) return null; + if (!m) return null; - // look for points strictly inside the triangle of hole point, segment intersection and endpoint; + // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point - var bx = data[mNode.i], - by = data[mNode.i + 1], - pbd = px * by - py * bx, - pcd = px * py - py * qMax, - cpy = py - py, - pcx = px - qMax, - pby = py - by, - bpx = bx - px, - A = pbd - pcd - (qMax * by - py * bx), - sign = A <= 0 ? -1 : 1, - stop = mNode, + var stop = m, tanMin = Infinity, - mx, my, amx, s, t, tan; + tan; - node = mNode.next; + p = m.next; - while (node !== stop) { + while (p !== stop) { + if (hx >= p.x && p.x >= m.x && + pointInTriangle(hy < m.y ? hx : qx, hy, m.x, m.y, hy < m.y ? qx : hx, hy, p.x, p.y)) { - mx = data[node.i]; - my = data[node.i + 1]; - amx = px - mx; + tan = Math.abs(hy - p.y) / (hx - p.x); // tangential - if (amx >= 0 && mx >= bx) { - s = (cpy * mx + pcx * my - pcd) * sign; - if (s >= 0) { - t = (pby * mx + bpx * my + pbd) * sign; - - if (t >= 0 && A * sign - s - t >= 0) { - tan = Math.abs(py - my) / amx; // tangential - if (tan < tanMin && locallyInside(data, node, holeNode)) { - mNode = node; - tanMin = tan; - } - } + if ((tan < tanMin || (tan === tanMin && p.x > m.x)) && locallyInside(p, hole)) { + m = p; + tanMin = tan; } } - node = node.next; + p = p.next; } - return mNode; + return m; } // interlink polygon nodes in z-order -function indexCurve(data, start, minX, minY, size) { - var node = start; - +function indexCurve(start, minX, minY, size) { + var p = start; do { - if (node.z === null) node.z = zOrder(data[node.i], data[node.i + 1], minX, minY, size); - node.prevZ = node.prev; - node.nextZ = node.next; - node = node.next; - } while (node !== start); + if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, size); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; + } while (p !== start); - node.prevZ.nextZ = null; - node.prevZ = null; + p.prevZ.nextZ = null; + p.prevZ = null; - sortLinked(node); + sortLinked(p); } // Simon Tatham's linked list merge sort algorithm @@ -3513,14 +3465,15 @@ // z-order of a point given coords and size of the data bounding box function zOrder(x, y, minX, minY, size) { - // coords are transformed into (0..1000) integer range - x = 1000 * (x - minX) / size; + // coords are transformed into non-negative 15-bit integer range + x = 32767 * (x - minX) / size; + y = 32767 * (y - minY) / size; + x = (x | (x << 8)) & 0x00FF00FF; x = (x | (x << 4)) & 0x0F0F0F0F; x = (x | (x << 2)) & 0x33333333; x = (x | (x << 1)) & 0x55555555; - y = 1000 * (y - minY) / size; y = (y | (y << 8)) & 0x00FF00FF; y = (y | (y << 4)) & 0x0F0F0F0F; y = (y | (y << 2)) & 0x33333333; @@ -3530,81 +3483,76 @@ } // find the leftmost node of a polygon ring -function getLeftmost(data, start) { - var node = start, +function getLeftmost(start) { + var p = start, leftmost = start; do { - if (data[node.i] < data[leftmost.i]) leftmost = node; - node = node.next; - } while (node !== start); + if (p.x < leftmost.x) leftmost = p; + p = p.next; + } while (p !== start); return leftmost; } -// check if a diagonal between two polygon nodes is valid (lies in polygon interior) -function isValidDiagonal(data, a, b) { - return a.next.i !== b.i && a.prev.i !== b.i && - !intersectsPolygon(data, a, a.i, b.i) && - locallyInside(data, a, b) && locallyInside(data, b, a) && - middleInside(data, a, a.i, b.i); +// check if a point lies within a convex triangle +function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { + return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && + (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && + (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; } -// winding order of triangle formed by 3 given points -function orient(data, p, q, r) { - var o = (data[q + 1] - data[p + 1]) * (data[r] - data[q]) - (data[q] - data[p]) * (data[r + 1] - data[q + 1]); - return o > 0 ? 1 : - o < 0 ? -1 : 0; +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +function isValidDiagonal(a, b) { + return equals(a, b) || a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && + locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b); +} + +// signed area of a triangle +function area(p, q, r) { + return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal -function equals(data, p1, p2) { - return data[p1] === data[p2] && data[p1 + 1] === data[p2 + 1]; +function equals(p1, p2) { + return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect -function intersects(data, p1, q1, p2, q2) { - return orient(data, p1, q1, p2) !== orient(data, p1, q1, q2) && - orient(data, p2, q2, p1) !== orient(data, p2, q2, q1); +function intersects(p1, q1, p2, q2) { + return area(p1, q1, p2) > 0 !== area(p1, q1, q2) > 0 && + area(p2, q2, p1) > 0 !== area(p2, q2, q1) > 0; } // check if a polygon diagonal intersects any polygon segments -function intersectsPolygon(data, start, a, b) { - var node = start; +function intersectsPolygon(a, b) { + var p = a; do { - var p1 = node.i, - p2 = node.next.i; - - if (p1 !== a && p2 !== a && p1 !== b && p2 !== b && intersects(data, p1, p2, a, b)) return true; - - node = node.next; - } while (node !== start); + if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && + intersects(p, p.next, a, b)) return true; + p = p.next; + } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon -function locallyInside(data, a, b) { - return orient(data, a.prev.i, a.i, a.next.i) === -1 ? - orient(data, a.i, b.i, a.next.i) !== -1 && orient(data, a.i, a.prev.i, b.i) !== -1 : - orient(data, a.i, b.i, a.prev.i) === -1 || orient(data, a.i, a.next.i, b.i) === -1; +function locallyInside(a, b) { + return area(a.prev, a, a.next) < 0 ? + area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : + area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon -function middleInside(data, start, a, b) { - var node = start, +function middleInside(a, b) { + var p = a, inside = false, - px = (data[a] + data[b]) / 2, - py = (data[a + 1] + data[b + 1]) / 2; + px = (a.x + b.x) / 2, + py = (a.y + b.y) / 2; do { - var p1 = node.i, - p2 = node.next.i; - - if (((data[p1 + 1] > py) !== (data[p2 + 1] > py)) && - (px < (data[p2] - data[p1]) * (py - data[p1 + 1]) / (data[p2 + 1] - data[p1 + 1]) + data[p1])) - inside = !inside; - - node = node.next; - } while (node !== start); + if (((p.y > py) !== (p.next.y > py)) && (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) + inside = !inside; + p = p.next; + } while (p !== a); return inside; } @@ -3612,8 +3560,8 @@ // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { - var a2 = new Node(a.i), - b2 = new Node(b.i), + var a2 = new Node(a.i, a.x, a.y), + b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; @@ -3633,26 +3581,38 @@ } // create a node and optionally link it with previous one (in a circular doubly linked list) -function insertNode(i, last) { - var node = new Node(i); +function insertNode(i, x, y, last) { + var p = new Node(i, x, y); if (!last) { - node.prev = node; - node.next = node; + p.prev = p; + p.next = p; } else { - node.next = last.next; - node.prev = last; - last.next.prev = node; - last.next = node; + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; } - return node; + return p; } -function Node(i) { - // vertex coordinates +function removeNode(p) { + p.next.prev = p.prev; + p.prev.next = p.next; + + if (p.prevZ) p.prevZ.nextZ = p.nextZ; + if (p.nextZ) p.nextZ.prevZ = p.prevZ; +} + +function Node(i, x, y) { + // vertice index in coordinates array this.i = i; + // vertex coordinates + this.x = x; + this.y = y; + // previous and next vertice nodes in a polygon ring this.prev = null; this.next = null; @@ -3974,6 +3934,1134 @@ }; },{}],12:[function(require,module,exports){ +(function (process){ +/*! + * async + * https://github.com/caolan/async + * + * Copyright 2010-2014 Caolan McMahon + * Released under the MIT license + */ +/*jshint onevar: false, indent:4 */ +/*global setImmediate: false, setTimeout: false, console: false */ +(function () { + + var async = {}; + + // global on the server, window in the browser + var root, previous_async; + + root = this; + if (root != null) { + previous_async = root.async; + } + + async.noConflict = function () { + root.async = previous_async; + return async; + }; + + function only_once(fn) { + var called = false; + return function() { + if (called) throw new Error("Callback was already called."); + called = true; + fn.apply(root, arguments); + } + } + + //// cross-browser compatiblity functions //// + + var _toString = Object.prototype.toString; + + var _isArray = Array.isArray || function (obj) { + return _toString.call(obj) === '[object Array]'; + }; + + var _each = function (arr, iterator) { + for (var i = 0; i < arr.length; i += 1) { + iterator(arr[i], i, arr); + } + }; + + var _map = function (arr, iterator) { + if (arr.map) { + return arr.map(iterator); + } + var results = []; + _each(arr, function (x, i, a) { + results.push(iterator(x, i, a)); + }); + return results; + }; + + var _reduce = function (arr, iterator, memo) { + if (arr.reduce) { + return arr.reduce(iterator, memo); + } + _each(arr, function (x, i, a) { + memo = iterator(memo, x, i, a); + }); + return memo; + }; + + var _keys = function (obj) { + if (Object.keys) { + return Object.keys(obj); + } + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + } + } + return keys; + }; + + //// exported async module functions //// + + //// nextTick implementation with browser-compatible fallback //// + if (typeof process === 'undefined' || !(process.nextTick)) { + if (typeof setImmediate === 'function') { + async.nextTick = function (fn) { + // not a direct alias for IE10 compatibility + setImmediate(fn); + }; + async.setImmediate = async.nextTick; + } + else { + async.nextTick = function (fn) { + setTimeout(fn, 0); + }; + async.setImmediate = async.nextTick; + } + } + else { + async.nextTick = process.nextTick; + if (typeof setImmediate !== 'undefined') { + async.setImmediate = function (fn) { + // not a direct alias for IE10 compatibility + setImmediate(fn); + }; + } + else { + async.setImmediate = async.nextTick; + } + } + + async.each = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + _each(arr, function (x) { + iterator(x, only_once(done) ); + }); + function done(err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed >= arr.length) { + callback(); + } + } + } + }; + async.forEach = async.each; + + async.eachSeries = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + var iterate = function () { + iterator(arr[completed], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed >= arr.length) { + callback(); + } + else { + iterate(); + } + } + }); + }; + iterate(); + }; + async.forEachSeries = async.eachSeries; + + async.eachLimit = function (arr, limit, iterator, callback) { + var fn = _eachLimit(limit); + fn.apply(null, [arr, iterator, callback]); + }; + async.forEachLimit = async.eachLimit; + + var _eachLimit = function (limit) { + + return function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length || limit <= 0) { + return callback(); + } + var completed = 0; + var started = 0; + var running = 0; + + (function replenish () { + if (completed >= arr.length) { + return callback(); + } + + while (running < limit && started < arr.length) { + started += 1; + running += 1; + iterator(arr[started - 1], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + running -= 1; + if (completed >= arr.length) { + callback(); + } + else { + replenish(); + } + } + }); + } + })(); + }; + }; + + + var doParallel = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.each].concat(args)); + }; + }; + var doParallelLimit = function(limit, fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [_eachLimit(limit)].concat(args)); + }; + }; + var doSeries = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.eachSeries].concat(args)); + }; + }; + + + var _asyncMap = function (eachfn, arr, iterator, callback) { + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + if (!callback) { + eachfn(arr, function (x, callback) { + iterator(x.value, function (err) { + callback(err); + }); + }); + } else { + var results = []; + eachfn(arr, function (x, callback) { + iterator(x.value, function (err, v) { + results[x.index] = v; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + async.map = doParallel(_asyncMap); + async.mapSeries = doSeries(_asyncMap); + async.mapLimit = function (arr, limit, iterator, callback) { + return _mapLimit(limit)(arr, iterator, callback); + }; + + var _mapLimit = function(limit) { + return doParallelLimit(limit, _asyncMap); + }; + + // reduce only has a series version, as doing reduce in parallel won't + // work in many situations. + async.reduce = function (arr, memo, iterator, callback) { + async.eachSeries(arr, function (x, callback) { + iterator(memo, x, function (err, v) { + memo = v; + callback(err); + }); + }, function (err) { + callback(err, memo); + }); + }; + // inject alias + async.inject = async.reduce; + // foldl alias + async.foldl = async.reduce; + + async.reduceRight = function (arr, memo, iterator, callback) { + var reversed = _map(arr, function (x) { + return x; + }).reverse(); + async.reduce(reversed, memo, iterator, callback); + }; + // foldr alias + async.foldr = async.reduceRight; + + var _filter = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.filter = doParallel(_filter); + async.filterSeries = doSeries(_filter); + // select alias + async.select = async.filter; + async.selectSeries = async.filterSeries; + + var _reject = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (!v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.reject = doParallel(_reject); + async.rejectSeries = doSeries(_reject); + + var _detect = function (eachfn, arr, iterator, main_callback) { + eachfn(arr, function (x, callback) { + iterator(x, function (result) { + if (result) { + main_callback(x); + main_callback = function () {}; + } + else { + callback(); + } + }); + }, function (err) { + main_callback(); + }); + }; + async.detect = doParallel(_detect); + async.detectSeries = doSeries(_detect); + + async.some = function (arr, iterator, main_callback) { + async.each(arr, function (x, callback) { + iterator(x, function (v) { + if (v) { + main_callback(true); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(false); + }); + }; + // any alias + async.any = async.some; + + async.every = function (arr, iterator, main_callback) { + async.each(arr, function (x, callback) { + iterator(x, function (v) { + if (!v) { + main_callback(false); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(true); + }); + }; + // all alias + async.all = async.every; + + 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 { + var fn = function (left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }; + callback(null, _map(results.sort(fn), function (x) { + return x.value; + })); + } + }); + }; + + async.auto = function (tasks, callback) { + callback = callback || function () {}; + var keys = _keys(tasks); + var remainingTasks = keys.length + if (!remainingTasks) { + return callback(); + } + + var results = {}; + + var listeners = []; + var addListener = function (fn) { + listeners.unshift(fn); + }; + var removeListener = function (fn) { + for (var i = 0; i < listeners.length; i += 1) { + if (listeners[i] === fn) { + listeners.splice(i, 1); + return; + } + } + }; + var taskComplete = function () { + remainingTasks-- + _each(listeners.slice(0), function (fn) { + fn(); + }); + }; + + addListener(function () { + if (!remainingTasks) { + var theCallback = callback; + // prevent final callback from calling itself if it errors + callback = function () {}; + + theCallback(null, results); + } + }); + + _each(keys, function (k) { + var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]]; + var taskCallback = function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + if (err) { + var safeResults = {}; + _each(_keys(results), function(rkey) { + safeResults[rkey] = results[rkey]; + }); + safeResults[k] = args; + callback(err, safeResults); + // stop subsequent errors hitting callback multiple times + callback = function () {}; + } + else { + results[k] = args; + async.setImmediate(taskComplete); + } + }; + var requires = task.slice(0, Math.abs(task.length - 1)) || []; + var ready = function () { + return _reduce(requires, function (a, x) { + return (a && results.hasOwnProperty(x)); + }, true) && !results.hasOwnProperty(k); + }; + if (ready()) { + task[task.length - 1](taskCallback, results); + } + else { + var listener = function () { + if (ready()) { + removeListener(listener); + task[task.length - 1](taskCallback, results); + } + }; + addListener(listener); + } + }); + }; + + async.retry = function(times, task, callback) { + var DEFAULT_TIMES = 5; + var attempts = []; + // Use defaults if times not passed + if (typeof times === 'function') { + callback = task; + task = times; + times = DEFAULT_TIMES; + } + // Make sure times is a number + times = parseInt(times, 10) || DEFAULT_TIMES; + var wrappedTask = function(wrappedCallback, wrappedResults) { + var retryAttempt = function(task, finalAttempt) { + return function(seriesCallback) { + task(function(err, result){ + seriesCallback(!err || finalAttempt, {err: err, result: result}); + }, wrappedResults); + }; + }; + while (times) { + attempts.push(retryAttempt(task, !(times-=1))); + } + async.series(attempts, function(done, data){ + data = data[data.length - 1]; + (wrappedCallback || callback)(data.err, data.result); + }); + } + // If a callback is passed, run this as a controll flow + return callback ? wrappedTask() : wrappedTask + }; + + async.waterfall = function (tasks, callback) { + callback = callback || function () {}; + 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(); + } + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback.apply(null, arguments); + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } + else { + args.push(callback); + } + async.setImmediate(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(async.iterator(tasks))(); + }; + + var _parallel = function(eachfn, tasks, callback) { + callback = callback || function () {}; + if (_isArray(tasks)) { + eachfn.map(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + eachfn.each(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.parallel = function (tasks, callback) { + _parallel({ map: async.map, each: async.each }, tasks, callback); + }; + + async.parallelLimit = function(tasks, limit, callback) { + _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback); + }; + + async.series = function (tasks, callback) { + callback = callback || function () {}; + if (_isArray(tasks)) { + async.mapSeries(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + async.eachSeries(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.iterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + 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 = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + return function () { + return fn.apply( + null, args.concat(Array.prototype.slice.call(arguments)) + ); + }; + }; + + var _concat = function (eachfn, arr, fn, callback) { + var r = []; + eachfn(arr, function (x, cb) { + fn(x, function (err, y) { + r = r.concat(y || []); + cb(err); + }); + }, function (err) { + callback(err, r); + }); + }; + async.concat = doParallel(_concat); + async.concatSeries = doSeries(_concat); + + async.whilst = function (test, iterator, callback) { + if (test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.whilst(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.doWhilst = function (iterator, test, callback) { + iterator(function (err) { + if (err) { + return callback(err); + } + var args = Array.prototype.slice.call(arguments, 1); + if (test.apply(null, args)) { + async.doWhilst(iterator, test, callback); + } + else { + callback(); + } + }); + }; + + async.until = function (test, iterator, callback) { + if (!test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.until(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.doUntil = function (iterator, test, callback) { + iterator(function (err) { + if (err) { + return callback(err); + } + var args = Array.prototype.slice.call(arguments, 1); + if (!test.apply(null, args)) { + async.doUntil(iterator, test, callback); + } + else { + callback(); + } + }); + }; + + async.queue = function (worker, concurrency) { + if (concurrency === undefined) { + concurrency = 1; + } + function _insert(q, data, pos, callback) { + if (!q.started){ + q.started = true; + } + if (!_isArray(data)) { + data = [data]; + } + if(data.length == 0) { + // call drain immediately if there are no tasks + return async.setImmediate(function() { + if (q.drain) { + q.drain(); + } + }); + } + _each(data, function(task) { + var item = { + data: task, + callback: typeof callback === 'function' ? callback : null + }; + + if (pos) { + q.tasks.unshift(item); + } else { + q.tasks.push(item); + } + + if (q.saturated && q.tasks.length === q.concurrency) { + q.saturated(); + } + async.setImmediate(q.process); + }); + } + + var workers = 0; + var q = { + tasks: [], + concurrency: concurrency, + saturated: null, + empty: null, + drain: null, + started: false, + paused: false, + push: function (data, callback) { + _insert(q, data, false, callback); + }, + kill: function () { + q.drain = null; + q.tasks = []; + }, + unshift: function (data, callback) { + _insert(q, data, true, callback); + }, + process: function () { + if (!q.paused && workers < q.concurrency && q.tasks.length) { + var task = q.tasks.shift(); + if (q.empty && q.tasks.length === 0) { + q.empty(); + } + workers += 1; + var next = function () { + workers -= 1; + if (task.callback) { + task.callback.apply(task, arguments); + } + if (q.drain && q.tasks.length + workers === 0) { + q.drain(); + } + q.process(); + }; + var cb = only_once(next); + worker(task.data, cb); + } + }, + length: function () { + return q.tasks.length; + }, + running: function () { + return workers; + }, + idle: function() { + return q.tasks.length + workers === 0; + }, + pause: function () { + if (q.paused === true) { return; } + q.paused = true; + }, + resume: function () { + if (q.paused === false) { return; } + q.paused = false; + // Need to call q.process once per concurrent + // worker to preserve full concurrency after pause + for (var w = 1; w <= q.concurrency; w++) { + async.setImmediate(q.process); + } + } + }; + 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 (!q.started){ + q.started = true; + } + if (!_isArray(data)) { + data = [data]; + } + if(data.length == 0) { + // call drain immediately if there are no tasks + return async.setImmediate(function() { + if (q.drain) { + q.drain(); + } + }); + } + _each(data, function(task) { + var item = { + data: task, + priority: priority, + callback: typeof callback === 'function' ? callback : null + }; + + q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item); + + if (q.saturated && 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) { + var working = false, + tasks = []; + + var cargo = { + tasks: tasks, + payload: payload, + saturated: null, + empty: null, + drain: null, + drained: true, + push: function (data, callback) { + if (!_isArray(data)) { + data = [data]; + } + _each(data, function(task) { + tasks.push({ + data: task, + callback: typeof callback === 'function' ? callback : null + }); + cargo.drained = false; + if (cargo.saturated && tasks.length === payload) { + cargo.saturated(); + } + }); + async.setImmediate(cargo.process); + }, + process: function process() { + if (working) return; + if (tasks.length === 0) { + if(cargo.drain && !cargo.drained) cargo.drain(); + cargo.drained = true; + return; + } + + var ts = typeof payload === 'number' + ? tasks.splice(0, payload) + : tasks.splice(0, tasks.length); + + var ds = _map(ts, function (task) { + return task.data; + }); + + if(cargo.empty) cargo.empty(); + working = true; + worker(ds, function () { + working = false; + + var args = arguments; + _each(ts, function (data) { + if (data.callback) { + data.callback.apply(null, args); + } + }); + + process(); + }); + }, + length: function () { + return tasks.length; + }, + running: function () { + return working; + } + }; + return cargo; + }; + + var _console_fn = function (name) { + return function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + fn.apply(null, args.concat([function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (typeof console !== 'undefined') { + if (err) { + if (console.error) { + console.error(err); + } + } + else if (console[name]) { + _each(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 = {}; + hasher = hasher || function (x) { + return x; + }; + var memoized = function () { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + var key = hasher.apply(null, args); + if (key in memo) { + async.nextTick(function () { + callback.apply(null, memo[key]); + }); + } + else if (key in queues) { + queues[key].push(callback); + } + else { + queues[key] = [callback]; + fn.apply(null, args.concat([function () { + memo[key] = arguments; + var q = queues[key]; + delete queues[key]; + for (var i = 0, l = q.length; i < l; i++) { + q[i].apply(null, arguments); + } + }])); + } + }; + memoized.memo = memo; + memoized.unmemoized = fn; + return memoized; + }; + + async.unmemoize = function (fn) { + return function () { + return (fn.unmemoized || fn).apply(null, arguments); + }; + }; + + async.times = function (count, iterator, callback) { + var counter = []; + for (var i = 0; i < count; i++) { + counter.push(i); + } + return async.map(counter, iterator, callback); + }; + + async.timesSeries = function (count, iterator, callback) { + var counter = []; + for (var i = 0; i < count; i++) { + counter.push(i); + } + return async.mapSeries(counter, iterator, callback); + }; + + async.seq = function (/* functions... */) { + var fns = arguments; + return function () { + var that = this; + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + async.reduce(fns, args, function (newargs, fn, cb) { + fn.apply(that, newargs.concat([function () { + var err = arguments[0]; + var nextargs = Array.prototype.slice.call(arguments, 1); + 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)); + }; + + var _applyEach = function (eachfn, fns /*args...*/) { + var go = function () { + var that = this; + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + return eachfn(fns, function (fn, cb) { + fn.apply(that, args.concat([cb])); + }, + callback); + }; + if (arguments.length > 2) { + var args = Array.prototype.slice.call(arguments, 2); + return go.apply(this, args); + } + else { + return go; + } + }; + async.applyEach = doParallel(_applyEach); + async.applyEachSeries = doSeries(_applyEach); + + async.forever = function (fn, callback) { + function next(err) { + if (err) { + if (callback) { + return callback(err); + } + throw err; + } + fn(next); + } + next(); + }; + + // Node.js + if (typeof module !== 'undefined' && module.exports) { + module.exports = async; + } + // AMD / RequireJS + else if (typeof define !== 'undefined' && define.amd) { + define([], function () { + return async; + }); + } + // included directly via