"use strict";

// credits: https://github.com/cowboy/javascript-sync-async-foreach
var asyncForEach = function(arr, eachFn, doneFn) {
    arr = arr || [];
    var i = -1;
    // Resolve array length to a valid (ToUint32) number.
    var len = arr.length >>> 0;

    (function next(result) {
        // This flag will be set to true if `this.async` is called inside the
        // eachFn` callback.
        var async;
        // Was false returned from the `eachFn` callback or passed to the
        // `this.async` done function?
        var abort = result === false;

        // Increment counter variable and skip any indices that don't exist. This
        // allows sparse arrays to be iterated.
        do { ++i; } while (!(i in arr) && i !== len);

        // Exit if result passed to `this.async` done function or returned from
        // the `eachFn` callback was false, or when done iterating.
        if (abort || i === len) {
            // If a `doneFn` callback was specified, invoke that now. Pass in a
            // boolean value representing "not aborted" state along with the array.
            if (doneFn) {
                doneFn(!abort, arr);
            }
            return;
        }

        // Invoke the `eachFn` callback, setting `this` inside the callback to a
        // custom object that contains one method, and passing in the array item,
        // index, and the array.
        result = eachFn.call({
            // If `this.async` is called inside the `eachFn` callback, set the async
            // flag and return a function that can be used to continue iterating.
            async: function() {
                async = true;
                return next;
            }
        }, arr[i], i, arr);

        // If the async flag wasn't set, continue by calling `next` synchronously,
        // passing in the result of the `eachFn` callback.
        if (!async) {
            next(result);
        }
    }());
};

module.exports = asyncForEach;
