<commit_before>(function () {
    var maxDependencyDepth = 20;
    var resolveFn = 'inject.resolveFn';
    var scopeFn = 'inject.scope'; // Key for current scope
    var locals = 'inject.locals';
    var _call = variadic(_apply);
    var call_ = variadic(apply_);
    var call = variadic(apply);
    var resolveChain = [];
    var uid = 1;

    window.inject = extend(function inject(registrations, parentResolve) {
        var scope = newScope(),
            resolveInjected = newLookup(registeredKeys(), registeredFactories(), parentResolve && parentResolve.injected);

        return parentResolve
            ? parentResolve(new Registration(constant(extendedResolve()), null))
            : extendedResolve();

        function extendedResolve() {
            return extend(resolve, {
                injected: resolveInjected,
                dispose: scope.dispose,
                function: resolveFunction,
                defaultFactory: defaultFactory
            });
        }

        function resolve(key) {
            if (!key)
                throw new Error("Tried to resolve " + name(key));

            if (resolveChain.length == maxDependencyDepth)
                throw new Error("Maximum dependency depth of " + maxDependencyDepth + " reached" + resolveChainMessage());

            resolveChain.push(key);
            try {
                return verifyType(key, resolveFunction(defaultFactory(key).withLifeTime)());
            } finally {
                resolveChain.pop();
            }
        }

        function resolveFunction(fn, localKeys, localValues) {
            return variadic(function (args) {
                return fn.apply(this, resolveDependencies(dependencyKeys(fn), localKeys || [], localValues || []).concat(args));
            });
        }

        function defaultFactory(key) {
            var injectedFn = resolve.injected(key);

            if (key instanceof Registration)
                return key.build(resolve, scope);
            if (injectedFn)
                return injectedFn;
            if (isFunction(key))
                return inject.type(key).build(resolve, scope);
            throw new Error('Failed to resolve key ' + name(key) + resolveChainMessage());
        }

        function resolveDependencies(required, localKeys, localValues) {
            var localLookup = newLocalLookup(localKeys, localValues);
            return required.map(unary(call_(localLookup, resolve)));
        }

        function registeredKeys() {
            return flatMap(
                    registrations || [],
                    function (r) { return r.keys(); })
                .concat(resolveFn, scopeFn);
        }

        function registeredFactories() {
            return flatMap(
                    registrations || [],
                    function (r) { return repeat(r.build(resolve, scope), r.keys().length); })
                .concat(noLifeTime(constant(resolve)), noLifeTime(constant(scope)));
        }
    }, {
        resolve: resolveFn,

        dependant: dependant,

        ctor: function (dependencies, fn) {
            verifyArity(dependencies, fn);

            return dependant(dependencies, fn);
        },

        fallback: function (fallbackFn, parentResolve) {
            var fallbackResolve = inject();
            parentResolve = parentResolve || { injected: { all: constant([]) } };
            fallbackResolve.injected.all = function (key) {
                var value = fallbackFn(key);
                return parentResolve.injected.all(key)
                    .concat(isDefined(value) ? [noLifeTime(constant(value))] : []);
            };
            return fallbackResolve;
        },

        forType: function (type) {
            verifyIsFunction(type, "Registration type");
            return new Registration().forType(type);
        },

        forTypes: function(types) {
            types.forEach(unary(call_(verifyIsFunction, "Registration type")));
            return new Registration().forTypes(types);
        },

        type: function (type) {
            verifyIsFunction(type, "Registration type");
            return new Registration().create(type);
        },

        forKey: function (key) {
            return new Registration().forKey(key);
        },

        forKeys: function(keys) {
            return new Registration().forKeys(keys);
        },

        single: function (type) {
            return inject.forType(type).create(type).once();
        },

        factory: function (fn) {
            return new Registration(fn);
        },

        value: function (value) {
            return new Registration().use(value);
        },

        function: function (fn) {
            return new Registration().resolveFunction(fn);
        },

        func: function (key, funcDependencies) {
            return new Registration(dependant([resolveFn, scopeFn], function (resolve, scope) {
                return variadic(function (args) {
                    return scope(null, resolve.function(resolve.defaultFactory(key), funcDependencies, args));
                });
            }), null);
        },

        optional: function (key, defaultValue) {
            return new Registration(dependant([resolveFn], function (resolve) {
                var defaultValueFactory = constant(defaultArg(defaultValue, null));
                return resolve.function(
                        resolve.injected(key, constant(defaultValueFactory)))
                    ();
            }), null);
        },

        named: function (type, key) {
            return new Registration(dependant([key], _call(verifyType, type)), null);
        },

        all: function (key) {
            return new Registration(dependant([resolveFn], function (resolve) {
                return resolve.injected.all(key)
                    .map(unary(resolve.function))
                    .map(unary(call))
                    .map(_call(verifyType, key));
            }), null);
        }
    });

    function Registration(factory, key) {
        var self = this,
            _keys = isDefined(key) ? [key] : [],
            _factory,
            _lifeTime,
            _constructor,
            _value,
            _function;

        extend(self, {
            keys: function () {
                return !_keys.length && _constructor
                    ? [_constructor]
                    : _keys;
            },

            forType: chain(function (type) {
                self.forTypes([type]);
            }),

            forTypes: chain(function(types) {
                _keys = _keys.concat(types);
                validate();
            }),

            forKey: chain(function (key) {
                self.forKeys([key]);
            }),

            forKeys: chain(function(keys) {
                if (keys.some(function(key) {return typeof key != 'string';}))
                    throw new Error("Registration key is not a string");

                _keys = _keys.concat(keys);
                validate();
            }),

            create: chain(function (type) {
                _constructor = defaultArg(type, null);
                _factory = constructor(type);

                validate();
            }),

            use: chain(function (value) {
                if (notDefined(value))
                    throw new Error("Value is undefined");

                _value = value;
                _factory = _lifeTime = constant(defaultArg(value, null));

                validate();
            }),

            call: chain(function (fn) {
                _factory = defaultArg(fn, null);
                validate();
            }),

            resolveFunction: chain(function (fn) {
                _function = fn;
                _factory = dependant([resolveFn, locals], function (resolve, local) {
                    return resolve.function(fn, local.keys, local.values);
                });

                validate();
            }),

            once: chain(function () {
                _lifeTime = function (factory, registeredResolve, registeredScope, resolve, currentScope) {
                    return registeredScope(self.keys()[0], registeredResolve.function(factory));
                };
            }),

            perContainer: chain(function () {
                _lifeTime = function (factory, registeredResolve, registeredScope, resolve, currentScope) {
                    return currentScope(self.keys()[0], resolve.function(factory));
                };
            }),

            perDependency: chain(function () {
                _lifeTime = function (factory, registeredResolve, registeredScope, resolve, currentScope) {
                    return currentScope(null, resolve.function(factory));
                };
            }),

            then: chain(function (callback) {
                _factory = dependant(dependencyKeys(_factory),
                    compose(_factory, function (value) {
                        callback(value);
                        return value;
                    }));
            }),

            useParameterHook: chain(function (hook) {
                verifyIsFunction(hook, 'Parameter hook');

                var innerFactory = _factory;
                _factory = dependant([resolveFn, locals], function (resolve, local) {
                    var originalKeys = dependencyKeys(innerFactory);
                    var hookedKeys = originalKeys.map(function (key) {
                        var hookValue = hook(resolve, key);
                        return isDefined(hookValue)
                            ? new Registration(constant(hookValue), null)
                            : key;
                    });

                    return resolve.function(dependant(hookedKeys, _call(innerFactory)), local.keys, local.values)();
                });
            }),

            withDependency: chain(function (key, value) {
                verifyType(key, value);

                self.useParameterHook(function (resolve, paramKey) {
                    if (paramKey === key) return value;
                });
            }),

            withArguments: chain(variadic(function (args) {
                _factory = dependant(
                    dependencyKeys(_factory).slice(args.length),
                    _apply(_factory, args));
            })),

            build: function (registeredResolve, registeredScope) {
                if (!self.keys().length)
                    throw new Error("No key defined for registration");
                if (notDefined(_factory))
                    throw new Error("No factory defined for " + name(self.keys()[0]) + " registration");

                return extend(_factory, {
                    withLifeTime: dependant([resolveFn, scopeFn, locals],
                        _call(_lifeTime, _factory, registeredResolve, registeredScope))
                });
            }
        });

        function validate() {
            if (isDefined(_constructor)) {
                verifyIsFunction(_constructor, "Constructor");
                self.keys().forEach(function (key) {
                    if (isFunction(key) && _constructor != key && !(_constructor.prototype instanceof key))
                        throw new Error(
                            (_constructor.name || "Anonymous type")
                                + " does not inherit from "
                                + (key.name || "anonymous base type"));
                });
            }

            if (isDefined(_value))
                self.keys()
                    .filter(isFunction)
                    .forEach(unary(call_(verifyType, _value)));

            if (isDefined(_function)) {
                verifyIsFunction(_function);
                if (self.keys().some(isFunction))
                    throw new Error("A type cannot be resolved to a function");
            }

            if (isDefined(_factory))
                verifyIsFunction(_factory, "Factory");
        }

        if (isDefined(factory))
            self.call(factory);
        self.perContainer();
    }

    function resolveChainMessage() {
        return resolveChain.length > 1
            ? " while attempting to resolve "
                + resolveChain
                .slice(0, -1)
                .map(name)
                .join(' -> ')
            : '';
    }

    function dependencyKeys(ctor) {
        return ctor.dependencies || [];
    }

    function name(key) {
        return isFunction(key)
            ? key.name || "Type"
            : "'" + key + "'";
    }

    function newScope() {
        var lookup = newLookup([], []);

        return extend(
            function scope(key, resolveForKey) {
                return lookup(key, function () {
                    var value = disposable(key, resolveForKey());
                    lookup.add(key, value);
                    return value;
                });
            }, {
                dispose: function () {
                    lookup.values()
                        .filter(isDisposable)
                        .forEach(function (value) { value.dispose(); });
                }
            });

        function isDisposable(value) {
            return value
                && value.dispose;
        }

        function disposable(key, value) {
            if (isDisposable(value))
                value.dispose = compose(value.dispose, _call(lookup.remove, key, value));
            return value;
        }
    }

    function newLocalLookup(keys, values) {
        return function localLookup(key, fallback) {
            var i = keys.indexOf(key);
            return key == locals ? { keys: keys, values: values }
                : i < 0 ? fallback(key)
                : (keys.splice(i, 1), values.splice(i, 1)[0]);
        };
    }

    function newLookup(keys, values, parent) {
        var map = {};
        keys.forEach(function (key, i) {
            add(key, values[i]);
        });

        parent = parent || { all: constant([]) };
        return extend(
            function lookup(key, fallback) {
                var value = last(key ? all(key) : []);
                return isDefined(value)
                    ? value
                    : (fallback || constant())();
            },
            {
                add: add,
                remove: function (key, value) {
                    var values = map[mapKey(key)] || [];
                    var i = values.indexOf(value);
                    if (i >= 0)
                        values.splice(i, 1);
                },
                values: function () {
                    return flatMap(Object.keys(map), function(key) { return map[key]; });
                },
                all: all
            }
        );

        function add(key, value) {
            key = mapKey(key);
            if (!map[key])
                map[key] = [];
            map[key].push(value);
        }

        function all(key) {
            return parent.all(key)
                .concat(map[mapKey(key)] || []);
        }

        function mapKey(key) {
            return !key ? '\0'
                : typeof key == 'string' ? key
                : key._injectKey ? key._injectKey
                : (key._injectKey = uid++);
        }
    }

    function verifyType(key, value) {
        if (notDefined(value))
            throw new Error(name(key) + " resolved to undefined" + resolveChainMessage());
        if (value === null)
            return value;
        if (isFunction(key) && !(value instanceof key))
            throw new Error('Value does not inherit from ' + name(key));
        return value;
    }

    function verifyIsFunction(fn, name) {
        if (!isFunction(fn))
            throw new Error(name + " is not a function");
    }

    function verifyArity(dependencies, fn) {
        if (dependencies.length != fn.length) {
            var dependenciesMsg = dependencies.length == 1 ? "1 dependency" : dependencies.length + " dependencies",
                paramsMsg = fn.length == 1 ? "1 parameter" : fn.length + " parameters";
            throw new Error(name(fn) + " has " + dependenciesMsg + ", but " + paramsMsg);
        }
    }

    function defaultArg(arg, defaultValue) {
        return notDefined(arg)
            ? defaultValue
            : arg;
    }

    function constant(value) {
        return function () {
            return value;
        };
    }

    function noLifeTime(factory) {
        return extend(factory, { withLifeTime: factory });
    }

    function constructor(fn) {
        return dependant(dependencyKeys(fn), variadic(function constructorFn(args) {
            return new (Function.prototype.bind.apply(fn, [null].concat(args)));
        }));
    }

    function dependant(dependencies, fn) {
        if (dependencies.some(notDefined))
            throw new Error(name(fn) + " has an undefined dependency");

        return extend(fn, { dependencies: dependencies });
    }

    function chain(fn) {
        return compose(fn, function () {
            return this;
        });
    }

    function compose(f, g) {
        return function composed() {
            return g.call(this, f.apply(this, arguments));
        };
    }

    function extend(obj, extra) {
        Object.keys(extra).forEach(function (key) {
            obj[key] = extra[key];
        });
        return obj;
    }

    function repeat(value, times) {
        var array = [];
        while (times--)
            array.push(value);
        return array;
    }

    function flatMap(array, mapFn) {
        return Array.prototype.concat.apply([], array.map(mapFn));
    }

    function last(array) {
        return array[array.length - 1];
    }

    function _apply(fn, args) {
        return variadic(function partial(moreArgs) {
            return fn.apply(this, args.concat(moreArgs));
        });
    }

    function apply_(fn, args) {
        return variadic(function partial(moreArgs) {
            return fn.apply(this, moreArgs.concat(args));
        });
    }

    function apply(fn, args) {
        return fn.apply(this, args);
    }

    function variadic(fn) {
        var singularArgs = fn.length - 1;
        return function variadic() {
            var args = Array.prototype.slice.call(arguments, 0, singularArgs);
            var rest = Array.prototype.slice.call(arguments, singularArgs);
            args.push(rest);
            return fn.apply(this, args);
        };
    }

    function unary(fn) {
        return function unary(arg) {
            return fn.call(this, arg);
        }
    }

    function isFunction(fn) {
        return typeof fn == 'function';
    }

    function notDefined(value) {
        return !isDefined(value);
    }

    function isDefined(value) {
        return typeof value != 'undefined';
    }
})();
<commit_msg>Disposing container empties lookups.<commit_after>(function () {
    var maxDependencyDepth = 20;
    var resolveFn = 'inject.resolveFn';
    var scopeFn = 'inject.scope'; // Key for current scope
    var locals = 'inject.locals';
    var _call = variadic(_apply);
    var call_ = variadic(apply_);
    var call = variadic(apply);
    var resolveChain = [];
    var uid = 1;

    window.inject = extend(function inject(registrations, parentResolve) {
        var scope = newScope(),
            resolveInjected = newLookup(registeredKeys(), registeredFactories(), parentResolve && parentResolve.injected);

        return parentResolve
            ? parentResolve(new Registration(constant(extendedResolve()), null))
            : extendedResolve();

        function extendedResolve() {
            return extend(resolve, {
                injected: resolveInjected,
                dispose: compose(scope.dispose, resolveInjected.dispose),
                function: resolveFunction,
                defaultFactory: defaultFactory
            });
        }

        function resolve(key) {
            if (!key)
                throw new Error("Tried to resolve " + name(key));

            if (resolveChain.length == maxDependencyDepth)
                throw new Error("Maximum dependency depth of " + maxDependencyDepth + " reached" + resolveChainMessage());

            resolveChain.push(key);
            try {
                return verifyType(key, resolveFunction(defaultFactory(key).withLifeTime)());
            } finally {
                resolveChain.pop();
            }
        }

        function resolveFunction(fn, localKeys, localValues) {
            return variadic(function (args) {
                return fn.apply(this, resolveDependencies(dependencyKeys(fn), localKeys || [], localValues || []).concat(args));
            });
        }

        function defaultFactory(key) {
            var injectedFn = resolve.injected(key);

            if (key instanceof Registration)
                return key.build(resolve, scope);
            if (injectedFn)
                return injectedFn;
            if (isFunction(key))
                return inject.type(key).build(resolve, scope);
            throw new Error('Failed to resolve key ' + name(key) + resolveChainMessage());
        }

        function resolveDependencies(required, localKeys, localValues) {
            var localLookup = newLocalLookup(localKeys, localValues);
            return required.map(unary(call_(localLookup, resolve)));
        }

        function registeredKeys() {
            return flatMap(
                    registrations || [],
                    function (r) { return r.keys(); })
                .concat(resolveFn, scopeFn);
        }

        function registeredFactories() {
            return flatMap(
                    registrations || [],
                    function (r) { return repeat(r.build(resolve, scope), r.keys().length); })
                .concat(noLifeTime(constant(resolve)), noLifeTime(constant(scope)));
        }
    }, {
        resolve: resolveFn,

        dependant: dependant,

        ctor: function (dependencies, fn) {
            verifyArity(dependencies, fn);

            return dependant(dependencies, fn);
        },

        fallback: function (fallbackFn, parentResolve) {
            var fallbackResolve = inject();
            parentResolve = parentResolve || { injected: { all: constant([]) } };
            fallbackResolve.injected.all = function (key) {
                var value = fallbackFn(key);
                return parentResolve.injected.all(key)
                    .concat(isDefined(value) ? [noLifeTime(constant(value))] : []);
            };
            return fallbackResolve;
        },

        forType: function (type) {
            verifyIsFunction(type, "Registration type");
            return new Registration().forType(type);
        },

        forTypes: function(types) {
            types.forEach(unary(call_(verifyIsFunction, "Registration type")));
            return new Registration().forTypes(types);
        },

        type: function (type) {
            verifyIsFunction(type, "Registration type");
            return new Registration().create(type);
        },

        forKey: function (key) {
            return new Registration().forKey(key);
        },

        forKeys: function(keys) {
            return new Registration().forKeys(keys);
        },

        single: function (type) {
            return inject.forType(type).create(type).once();
        },

        factory: function (fn) {
            return new Registration(fn);
        },

        value: function (value) {
            return new Registration().use(value);
        },

        function: function (fn) {
            return new Registration().resolveFunction(fn);
        },

        func: function (key, funcDependencies) {
            return new Registration(dependant([resolveFn, scopeFn], function (resolve, scope) {
                return variadic(function (args) {
                    return scope(null, resolve.function(resolve.defaultFactory(key), funcDependencies, args));
                });
            }), null);
        },

        optional: function (key, defaultValue) {
            return new Registration(dependant([resolveFn], function (resolve) {
                var defaultValueFactory = constant(defaultArg(defaultValue, null));
                return resolve.function(
                        resolve.injected(key, constant(defaultValueFactory)))
                    ();
            }), null);
        },

        named: function (type, key) {
            return new Registration(dependant([key], _call(verifyType, type)), null);
        },

        all: function (key) {
            return new Registration(dependant([resolveFn], function (resolve) {
                return resolve.injected.all(key)
                    .map(unary(resolve.function))
                    .map(unary(call))
                    .map(_call(verifyType, key));
            }), null);
        }
    });

    function Registration(factory, key) {
        var self = this,
            _keys = isDefined(key) ? [key] : [],
            _factory,
            _lifeTime,
            _constructor,
            _value,
            _function;

        extend(self, {
            keys: function () {
                return !_keys.length && _constructor
                    ? [_constructor]
                    : _keys;
            },

            forType: chain(function (type) {
                self.forTypes([type]);
            }),

            forTypes: chain(function(types) {
                _keys = _keys.concat(types);
                validate();
            }),

            forKey: chain(function (key) {
                self.forKeys([key]);
            }),

            forKeys: chain(function(keys) {
                if (keys.some(function(key) {return typeof key != 'string';}))
                    throw new Error("Registration key is not a string");

                _keys = _keys.concat(keys);
                validate();
            }),

            create: chain(function (type) {
                _constructor = defaultArg(type, null);
                _factory = constructor(type);

                validate();
            }),

            use: chain(function (value) {
                if (notDefined(value))
                    throw new Error("Value is undefined");

                _value = value;
                _factory = _lifeTime = constant(defaultArg(value, null));

                validate();
            }),

            call: chain(function (fn) {
                _factory = defaultArg(fn, null);
                validate();
            }),

            resolveFunction: chain(function (fn) {
                _function = fn;
                _factory = dependant([resolveFn, locals], function (resolve, local) {
                    return resolve.function(fn, local.keys, local.values);
                });

                validate();
            }),

            once: chain(function () {
                _lifeTime = function (factory, registeredResolve, registeredScope, resolve, currentScope) {
                    return registeredScope(self.keys()[0], registeredResolve.function(factory));
                };
            }),

            perContainer: chain(function () {
                _lifeTime = function (factory, registeredResolve, registeredScope, resolve, currentScope) {
                    return currentScope(self.keys()[0], resolve.function(factory));
                };
            }),

            perDependency: chain(function () {
                _lifeTime = function (factory, registeredResolve, registeredScope, resolve, currentScope) {
                    return currentScope(null, resolve.function(factory));
                };
            }),

            then: chain(function (callback) {
                _factory = dependant(dependencyKeys(_factory),
                    compose(_factory, function (value) {
                        callback(value);
                        return value;
                    }));
            }),

            useParameterHook: chain(function (hook) {
                verifyIsFunction(hook, 'Parameter hook');

                var innerFactory = _factory;
                _factory = dependant([resolveFn, locals], function (resolve, local) {
                    var originalKeys = dependencyKeys(innerFactory);
                    var hookedKeys = originalKeys.map(function (key) {
                        var hookValue = hook(resolve, key);
                        return isDefined(hookValue)
                            ? new Registration(constant(hookValue), null)
                            : key;
                    });

                    return resolve.function(dependant(hookedKeys, _call(innerFactory)), local.keys, local.values)();
                });
            }),

            withDependency: chain(function (key, value) {
                verifyType(key, value);

                self.useParameterHook(function (resolve, paramKey) {
                    if (paramKey === key) return value;
                });
            }),

            withArguments: chain(variadic(function (args) {
                _factory = dependant(
                    dependencyKeys(_factory).slice(args.length),
                    _apply(_factory, args));
            })),

            build: function (registeredResolve, registeredScope) {
                if (!self.keys().length)
                    throw new Error("No key defined for registration");
                if (notDefined(_factory))
                    throw new Error("No factory defined for " + name(self.keys()[0]) + " registration");

                return extend(_factory, {
                    withLifeTime: dependant([resolveFn, scopeFn, locals],
                        _call(_lifeTime, _factory, registeredResolve, registeredScope))
                });
            }
        });

        function validate() {
            if (isDefined(_constructor)) {
                verifyIsFunction(_constructor, "Constructor");
                self.keys().forEach(function (key) {
                    if (isFunction(key) && _constructor != key && !(_constructor.prototype instanceof key))
                        throw new Error(
                            (_constructor.name || "Anonymous type")
                                + " does not inherit from "
                                + (key.name || "anonymous base type"));
                });
            }

            if (isDefined(_value))
                self.keys()
                    .filter(isFunction)
                    .forEach(unary(call_(verifyType, _value)));

            if (isDefined(_function)) {
                verifyIsFunction(_function);
                if (self.keys().some(isFunction))
                    throw new Error("A type cannot be resolved to a function");
            }

            if (isDefined(_factory))
                verifyIsFunction(_factory, "Factory");
        }

        if (isDefined(factory))
            self.call(factory);
        self.perContainer();
    }

    function resolveChainMessage() {
        return resolveChain.length > 1
            ? " while attempting to resolve "
                + resolveChain
                .slice(0, -1)
                .map(name)
                .join(' -> ')
            : '';
    }

    function dependencyKeys(ctor) {
        return ctor.dependencies || [];
    }

    function name(key) {
        return isFunction(key)
            ? key.name || "Type"
            : "'" + key + "'";
    }

    function newScope() {
        var lookup = newLookup([], []);

        return extend(
            function scope(key, resolveForKey) {
                return lookup(key, function () {
                    var value = disposable(key, resolveForKey());
                    lookup.add(key, value);
                    return value;
                });
            }, {
                dispose: function () {
                    lookup.values()
                        .filter(isDisposable)
                        .forEach(function (value) { value.dispose(); });
                    lookup.dispose();
                }
            });

        function isDisposable(value) {
            return value
                && value.dispose;
        }

        function disposable(key, value) {
            if (isDisposable(value))
                value.dispose = compose(value.dispose, _call(lookup.remove, key, value));
            return value;
        }
    }

    function newLocalLookup(keys, values) {
        return function localLookup(key, fallback) {
            var i = keys.indexOf(key);
            return key == locals ? { keys: keys, values: values }
                : i < 0 ? fallback(key)
                : (keys.splice(i, 1), values.splice(i, 1)[0]);
        };
    }

    function newLookup(initialKeys, initialValues, parent) {
        var map = {};
        initialKeys.forEach(function (key, i) {
            add(key, initialValues[i]);
        });

        parent = parent || { all: constant([]) };
        return extend(
            function lookup(key, fallback) {
                var value = last(key ? all(key) : []);
                return isDefined(value)
                    ? value
                    : (fallback || constant())();
            },
            {
                add: add,
                remove: function (key, value) {
                    var values = map[mapKey(key)] || [];
                    var i = values.indexOf(value);
                    if (i >= 0)
                        values.splice(i, 1);
                },
                values: function () {
                    return flatMap(Object.keys(map), function(key) { return map[key]; });
                },
                all: all,
                dispose: function () {
                    Object.keys(map).forEach(function (key) {
                        delete map[key];
                    });
                }
            }
        );

        function add(key, value) {
            key = mapKey(key);
            if (!map[key])
                map[key] = [];
            map[key].push(value);
        }

        function all(key) {
            return parent.all(key)
                .concat(map[mapKey(key)] || []);
        }

        function mapKey(key) {
            return !key ? '\0'
                : typeof key == 'string' ? key
                : key._injectKey ? key._injectKey
                : (key._injectKey = uid++);
        }
    }

    function verifyType(key, value) {
        if (notDefined(value))
            throw new Error(name(key) + " resolved to undefined" + resolveChainMessage());
        if (value === null)
            return value;
        if (isFunction(key) && !(value instanceof key))
            throw new Error('Value does not inherit from ' + name(key));
        return value;
    }

    function verifyIsFunction(fn, name) {
        if (!isFunction(fn))
            throw new Error(name + " is not a function");
    }

    function verifyArity(dependencies, fn) {
        if (dependencies.length != fn.length) {
            var dependenciesMsg = dependencies.length == 1 ? "1 dependency" : dependencies.length + " dependencies",
                paramsMsg = fn.length == 1 ? "1 parameter" : fn.length + " parameters";
            throw new Error(name(fn) + " has " + dependenciesMsg + ", but " + paramsMsg);
        }
    }

    function defaultArg(arg, defaultValue) {
        return notDefined(arg)
            ? defaultValue
            : arg;
    }

    function constant(value) {
        return function () {
            return value;
        };
    }

    function noLifeTime(factory) {
        return extend(factory, { withLifeTime: factory });
    }

    function constructor(fn) {
        return dependant(dependencyKeys(fn), variadic(function constructorFn(args) {
            return new (Function.prototype.bind.apply(fn, [null].concat(args)));
        }));
    }

    function dependant(dependencies, fn) {
        if (dependencies.some(notDefined))
            throw new Error(name(fn) + " has an undefined dependency");

        return extend(fn, { dependencies: dependencies });
    }

    function chain(fn) {
        return compose(fn, function () {
            return this;
        });
    }

    function compose(f, g) {
        return function composed() {
            return g.call(this, f.apply(this, arguments));
        };
    }

    function extend(obj, extra) {
        Object.keys(extra).forEach(function (key) {
            obj[key] = extra[key];
        });
        return obj;
    }

    function repeat(value, times) {
        var array = [];
        while (times--)
            array.push(value);
        return array;
    }

    function flatMap(array, mapFn) {
        return Array.prototype.concat.apply([], array.map(mapFn));
    }

    function last(array) {
        return array[array.length - 1];
    }

    function _apply(fn, args) {
        return variadic(function partial(moreArgs) {
            return fn.apply(this, args.concat(moreArgs));
        });
    }

    function apply_(fn, args) {
        return variadic(function partial(moreArgs) {
            return fn.apply(this, moreArgs.concat(args));
        });
    }

    function apply(fn, args) {
        return fn.apply(this, args);
    }

    function variadic(fn) {
        var singularArgs = fn.length - 1;
        return function variadic() {
            var args = Array.prototype.slice.call(arguments, 0, singularArgs);
            var rest = Array.prototype.slice.call(arguments, singularArgs);
            args.push(rest);
            return fn.apply(this, args);
        };
    }

    function unary(fn) {
        return function unary(arg) {
            return fn.call(this, arg);
        }
    }

    function isFunction(fn) {
        return typeof fn == 'function';
    }

    function notDefined(value) {
        return !isDefined(value);
    }

    function isDefined(value) {
        return typeof value != 'undefined';
    }
})();
<|endoftext|>


<commit_before>var VideoRecircList = function() {
  $videoItem = 'video-item';
  $videoRecircList = $('#video-list');
  $bettyUrl = $videoRecircList.data('betty-url');
  $source = $videoRecircList.data('source');
  $recircCount = $videoRecircList.data('count');
  this.init();
};

VideoRecircList.prototype.init = function() {
  this.loadVideoRecirc();
};

VideoRecircList.prototype.loadVideoRecirc = function() {
  $.getJSON($source, function(data) {
    console.log(data);
    $.each(data.results.slice(0,$recircCount), function(index, video) {
      $videoTitle = video.title;
      $videoHref = '/v/' + video.id;
      $posterSource = $bettyUrl + '/' + video.poster.id
      $('',{
        'class' : $videoItem,
        'href' : $videoHref,
        'html': $('
',{
          'class' : 'content',
          'html' : '

'
        }).add($('
',{
          'html' : $videoTitle
        }))
      }).appendTo($videoRecircList);
    });
  });
};

new VideoRecircList();<commit_msg>Add analytics<commit_after>var VideoRecircList = function() {
  $videoItem = 'video-item';
  $videoRecircList = $('#video-list');
  $bettyUrl = $videoRecircList.data('betty-url');
  $source = $videoRecircList.data('source');
  $recircCount = $videoRecircList.data('count');
  this.init();
};

VideoRecircList.prototype.init = function() {
  this.loadVideoRecirc();
};

VideoRecircList.prototype.loadVideoRecirc = function() {
  $.getJSON($source, function(data) {
    $.each(data.results.slice(0,$recircCount), function(index, video) {
      $videoTitle = video.title;
      $videoHref = '/v/' + video.id;
      $posterSource = $bettyUrl + '/' + video.poster.id
      $('',{
        'class' : $videoItem,
        'href' : $videoHref,
        'data-track-action' : 'Video: Recirc',
        'data-track-label' : $videoHref,
        'html': $('
',{
          'class' : 'content',
          'html' : '

'
        }).add($('
',{
          'html' : $videoTitle
        }))
      }).appendTo($videoRecircList);
    });
  });
};

new VideoRecircList();<|endoftext|>


<commit_before>import _ from 'underscore';
import validateDeck from '../deck-validator.js';

function selectDeck(state, deck) {
    if(state.decks && state.decks.length !== 0) {
        state.selectedDeck = deck;
    } else {
        delete state.selectedDeck;
    }

    return state;
}

function processDecks(decks, state) {
    _.each(decks, deck => {
        if(!state.cards || !deck.faction) {
            deck.validation = {};
            return;
        }

        deck.faction = state.factions[deck.faction.value];
        if(deck.agenda) {
            deck.agenda = state.agendas[deck.agenda.code];
        }

        if(deck.bannerCards) {
            deck.bannerCards = _.map(deck.bannerCards, card => state.cards[card.code]);
        }

        deck.plotCards = _.map(deck.plotCards, card => {
            return { count: card.count, card: card.card.custom ? card.card : state.cards[card.card.code] };
        });

        deck.drawCards = _.map(deck.drawCards, card => {
            return { count: card.count, card: card.card.custom ? card.card : state.cards[card.card.code] };
        });

        deck.validation = validateDeck(deck, state.packs);
    });
}

export default function(state = {}, action) {
    let newState;
    switch(action.type) {
        case 'RECEIVE_CARDS':
            var agendas = {};

            _.each(action.response.cards, card => {
                if(card.type_code === 'agenda') {
                    agendas[card.code] = card;
                }
            });

            var banners = _.filter(agendas, card => {
                return card.label.startsWith('Banner of the');
            });

            return Object.assign({}, state, {
                cards: action.response.cards,
                agendas: agendas,
                banners: banners
            });
        case 'RECEIVE_PACKS':
            return Object.assign({}, state, {
                packs: action.response.packs
            });
        case 'RECEIVE_FACTIONS':
            var factions = {};

            _.each(action.response.factions, faction => {
                factions[faction.value] = faction;
            });

            return Object.assign({}, state, {
                factions: factions
            });
        case 'ZOOM_CARD':
            return Object.assign({}, state, {
                zoomCard: action.card
            });
        case 'CLEAR_ZOOM':
            return Object.assign({}, state, {
                zoomCard: undefined
            });
        case 'RECEIVE_DECKS':
            processDecks(action.response.decks, state);
            newState = Object.assign({}, state, {
                singleDeck: false,
                decks: action.response.decks
            });

            newState = selectDeck(newState, newState.decks[0]);

            return newState;
        case 'REQUEST_DECK':
            return Object.assign({}, state, {
                deckSaved: false,
                deckDeleted: false
            });
        case 'REQUEST_DECKS':
            newState = Object.assign({}, state, {
                deckSaved: false,
                deckDeleted: false
            });

            if(newState.selectedDeck && !newState.selectedDeck._id) {
                if(_.size(newState.decks) > 0) {
                    newState.selectedDeck = newState.decks[0];
                }
            }

            return newState;
        case 'RECEIVE_DECK':
            newState = Object.assign({}, state, {
                singleDeck: true,
                deckSaved: false
            });

            processDecks([action.response.deck], state);

            newState.decks = _.map(state.decks, deck => {
                if(action.response.deck._id === deck.id) {
                    return deck;
                }

                return deck;
            });

            if(!_.any(newState.decks, deck => {
                return deck._id === action.response.deck._id;
            })) {
                newState.decks.push(action.response.deck);
            }

            var selected = _.find(newState.decks, deck => {
                return deck._id === action.response.deck._id;
            });

            newState = selectDeck(newState, selected);

            return newState;
        case 'SELECT_DECK':
            newState = Object.assign({}, state, {
                selectedDeck: action.deck,
                deckSaved: false
            });

            if(newState.selectedDeck) {
                processDecks([newState.selectedDeck], state);
            }

            return newState;
        case 'ADD_DECK':
            var newDeck = { name: 'New Deck' };

            newState = Object.assign({}, state, {
                selectedDeck: newDeck,
                deckSaved: false
            });

            processDecks([newState.selectedDeck], state);

            return newState;
        case 'UPDATE_DECK':
            newState = Object.assign({}, state, {
                selectedDeck: action.deck,
                deckSaved: false
            });

            if(newState.selectedDeck) {
                processDecks([newState.selectedDeck], state);
            }

            return newState;
        case 'SAVE_DECK':
            newState = Object.assign({}, state, {
                deckSaved: false
            });

            return newState;
        case 'DECK_SAVED':
            newState = Object.assign({}, state, {
                deckSaved: true,
                decks: undefined
            });

            return newState;
        case 'DECK_DELETED':
            newState = Object.assign({}, state, {
                deckDeleted: true
            });

            newState.decks = _.reject(newState.decks, deck => {
                return deck._id === action.response.deckId;
            });

            newState.selectedDeck = _.first(newState.decks);

            return newState;
        case 'CLEAR_DECK_STATUS':
            return Object.assign({}, state, {
                deckDeleted: false,
                deckSaved: false
            });
        default:
            return state;
    }
}
<commit_msg>Filter out cards missing card data from decks (#1666)<commit_after>import _ from 'underscore';
import validateDeck from '../deck-validator.js';

function selectDeck(state, deck) {
    if(state.decks && state.decks.length !== 0) {
        state.selectedDeck = deck;
    } else {
        delete state.selectedDeck;
    }

    return state;
}

function processDecks(decks, state) {
    _.each(decks, deck => {
        if(!state.cards || !deck.faction) {
            deck.validation = {};
            return;
        }

        deck.faction = state.factions[deck.faction.value];
        if(deck.agenda) {
            deck.agenda = state.agendas[deck.agenda.code];
        }

        if(deck.bannerCards) {
            deck.bannerCards = _.map(deck.bannerCards, card => state.cards[card.code]);
        }

        deck.plotCards = processCardCounts(deck.plotCards, state.cards);

        deck.drawCards = processCardCounts(deck.drawCards, state.cards);

        deck.validation = validateDeck(deck, state.packs);
    });
}

function processCardCounts(cardCounts, cardData) {
    let cardCountsWithData = cardCounts.map(cardCount => {
        return { count: cardCount.count, card: cardCount.card.custom ? cardCount.card : cardData[cardCount.card.code] };
    });

    // Filter out any cards that aren't available in the card data.
    return cardCountsWithData.filter(cardCount => !!cardCount.card);
}

export default function(state = {}, action) {
    let newState;
    switch(action.type) {
        case 'RECEIVE_CARDS':
            var agendas = {};

            _.each(action.response.cards, card => {
                if(card.type_code === 'agenda') {
                    agendas[card.code] = card;
                }
            });

            var banners = _.filter(agendas, card => {
                return card.label.startsWith('Banner of the');
            });

            return Object.assign({}, state, {
                cards: action.response.cards,
                agendas: agendas,
                banners: banners
            });
        case 'RECEIVE_PACKS':
            return Object.assign({}, state, {
                packs: action.response.packs
            });
        case 'RECEIVE_FACTIONS':
            var factions = {};

            _.each(action.response.factions, faction => {
                factions[faction.value] = faction;
            });

            return Object.assign({}, state, {
                factions: factions
            });
        case 'ZOOM_CARD':
            return Object.assign({}, state, {
                zoomCard: action.card
            });
        case 'CLEAR_ZOOM':
            return Object.assign({}, state, {
                zoomCard: undefined
            });
        case 'RECEIVE_DECKS':
            processDecks(action.response.decks, state);
            newState = Object.assign({}, state, {
                singleDeck: false,
                decks: action.response.decks
            });

            newState = selectDeck(newState, newState.decks[0]);

            return newState;
        case 'REQUEST_DECK':
            return Object.assign({}, state, {
                deckSaved: false,
                deckDeleted: false
            });
        case 'REQUEST_DECKS':
            newState = Object.assign({}, state, {
                deckSaved: false,
                deckDeleted: false
            });

            if(newState.selectedDeck && !newState.selectedDeck._id) {
                if(_.size(newState.decks) > 0) {
                    newState.selectedDeck = newState.decks[0];
                }
            }

            return newState;
        case 'RECEIVE_DECK':
            newState = Object.assign({}, state, {
                singleDeck: true,
                deckSaved: false
            });

            processDecks([action.response.deck], state);

            newState.decks = _.map(state.decks, deck => {
                if(action.response.deck._id === deck.id) {
                    return deck;
                }

                return deck;
            });

            if(!_.any(newState.decks, deck => {
                return deck._id === action.response.deck._id;
            })) {
                newState.decks.push(action.response.deck);
            }

            var selected = _.find(newState.decks, deck => {
                return deck._id === action.response.deck._id;
            });

            newState = selectDeck(newState, selected);

            return newState;
        case 'SELECT_DECK':
            newState = Object.assign({}, state, {
                selectedDeck: action.deck,
                deckSaved: false
            });

            if(newState.selectedDeck) {
                processDecks([newState.selectedDeck], state);
            }

            return newState;
        case 'ADD_DECK':
            var newDeck = { name: 'New Deck' };

            newState = Object.assign({}, state, {
                selectedDeck: newDeck,
                deckSaved: false
            });

            processDecks([newState.selectedDeck], state);

            return newState;
        case 'UPDATE_DECK':
            newState = Object.assign({}, state, {
                selectedDeck: action.deck,
                deckSaved: false
            });

            if(newState.selectedDeck) {
                processDecks([newState.selectedDeck], state);
            }

            return newState;
        case 'SAVE_DECK':
            newState = Object.assign({}, state, {
                deckSaved: false
            });

            return newState;
        case 'DECK_SAVED':
            newState = Object.assign({}, state, {
                deckSaved: true,
                decks: undefined
            });

            return newState;
        case 'DECK_DELETED':
            newState = Object.assign({}, state, {
                deckDeleted: true
            });

            newState.decks = _.reject(newState.decks, deck => {
                return deck._id === action.response.deckId;
            });

            newState.selectedDeck = _.first(newState.decks);

            return newState;
        case 'CLEAR_DECK_STATUS':
            return Object.assign({}, state, {
                deckDeleted: false,
                deckSaved: false
            });
        default:
            return state;
    }
}
<|endoftext|>


<commit_before>// Use

'use strict';

var ensureType = require('dbjs/valid-dbjs-type')
  , serialize = require('es5-ext/object/serialize')
  , writeFile = require('fs2/write-file')
  , db        = require('mano').db
  , Currency  = require('dbjs-ext/number/currency')(db)
  , Cost      = require('eregistrations/model/cost')(db);

var defineForEach = function (object) {
	object.forEach = new Function('cb, thisArg', '$.dbjsMapForEach(this, cb, thisArg);');
};

module.exports = function (businessProcessType, filename/*, options*/) {
	var options = Object(arguments[2]), legacyDb = {}, businessProcessProto, certificates = {}
	  , costs = {}, registrations = {}, requirements = {}, currencyType = {}, costType = {};

	ensureType(businessProcessType);

	// Get BusinessProcess prototype.
	businessProcessProto = businessProcessType.prototype;

	// Get all interesting fields from BusinessProcess prototype.
	var getPropertyValue = function (multipleProcessName, propertyName) {
		return businessProcessProto[multipleProcessName].getDescriptor(propertyName)._value_;
	};

	var copyMapEntities = function (propertyName, entitiesPropertyNames) {
		var result = {};

		// To allow copying 'empty' entities.
		if (!entitiesPropertyNames) entitiesPropertyNames = [];

		businessProcessProto[propertyName].map.forEach(function (item, key) {
			var entity = {};

			entitiesPropertyNames.forEach(function (entityPropertyName) {
				entity[entityPropertyName] = item.getDescriptor(entityPropertyName)._value_;
			});

			result[key] = entity;
		});

		return result;
	};

	// Assure each map implements forEach.
	[certificates.map, costs.map, registrations.map, requirements.map].forEach(defineForEach);

	// Copy maps.
	certificates.map  = copyMapEntities('certificates');
	costs.map         = copyMapEntities('costs', ['amount']);
	registrations.map = copyMapEntities('registrations', ['isApplicable', 'isMandatory',
		'isRequested', 'certificates', 'requirements', 'costs']);
	requirements.map  = copyMapEntities('requirements', ['isApplicable']);

	// Copy MultipleProcess resolvers.
	certificates.applicable  = getPropertyValue('certificates', 'applicable');
	costs.applicable         = getPropertyValue('costs', 'applicable');
	costs.payable            = getPropertyValue('costs', 'payable');
	costs.totalAmount        = getPropertyValue('costs', 'totalAmount');
	registrations.applicable = getPropertyValue('registrations', 'applicable');
	registrations.mandatory  = getPropertyValue('registrations', 'mandatory');
	registrations.optional   = getPropertyValue('registrations', 'optional');
	registrations.requested  = getPropertyValue('registrations', 'requested');
	requirements.resolved    = getPropertyValue('requirements', 'resolved');
	requirements.applicable  = getPropertyValue('requirements', 'applicable');

	// Copy less interesting (support) stuff also.
	currencyType.format = Currency.format;
	currencyType.symbol = db.Cost.prototype.getDescriptor('amount').type.symbol;
	currencyType.isoCode = db.Cost.prototype.getDescriptor('amount').type.isoCode;
	costType.step = Cost.prototype.getDescriptor('amount').step;

	// Add everything to legacyDb
	legacyDb[businessProcessType.__id__] = {
		prototype: {
			certificates: certificates,
			costs: costs,
			registrations: registrations,
			requirements: requirements
		}
	};
	legacyDb.Currency = currencyType;
	legacyDb.Cost = costType;

	// Allow customizations on legacyDb.
	if (options.customizeCb) options.customizeCb(legacyDb);

	// Store file.
	return writeFile(filename,
			'\'use strict\';\n\n' +
			'var $ = require(\'mano-legacy\');\n' +
			'require(\'mano-legacy/dbjs-map-for-each\');\n\n' +
			'module.exports = ' + serialize(legacyDb) + ';\n');
};
<commit_msg>Issue #713. Moved forEach declarations down the file.<commit_after>// Use

'use strict';

var ensureType = require('dbjs/valid-dbjs-type')
  , serialize = require('es5-ext/object/serialize')
  , writeFile = require('fs2/write-file')
  , db        = require('mano').db
  , Currency  = require('dbjs-ext/number/currency')(db)
  , Cost      = require('eregistrations/model/cost')(db);

var defineForEach = function (object) {
	object.forEach = new Function('cb, thisArg', '$.dbjsMapForEach(this, cb, thisArg);');
};

module.exports = function (businessProcessType, filename/*, options*/) {
	var options = Object(arguments[2]), legacyDb = {}, businessProcessProto, certificates = {}
	  , costs = {}, registrations = {}, requirements = {}, currencyType = {}, costType = {};

	ensureType(businessProcessType);

	// Get BusinessProcess prototype.
	businessProcessProto = businessProcessType.prototype;

	// Get all interesting fields from BusinessProcess prototype.
	var getPropertyValue = function (multipleProcessName, propertyName) {
		return businessProcessProto[multipleProcessName].getDescriptor(propertyName)._value_;
	};

	var copyMapEntities = function (propertyName, entitiesPropertyNames) {
		var result = {};

		// To allow copying 'empty' entities.
		if (!entitiesPropertyNames) entitiesPropertyNames = [];

		businessProcessProto[propertyName].map.forEach(function (item, key) {
			var entity = {};

			entitiesPropertyNames.forEach(function (entityPropertyName) {
				entity[entityPropertyName] = item.getDescriptor(entityPropertyName)._value_;
			});

			result[key] = entity;
		});

		return result;
	};

	// Copy maps.
	certificates.map  = copyMapEntities('certificates');
	costs.map         = copyMapEntities('costs', ['amount']);
	registrations.map = copyMapEntities('registrations', ['isApplicable', 'isMandatory',
		'isRequested', 'certificates', 'requirements', 'costs']);
	requirements.map  = copyMapEntities('requirements', ['isApplicable']);

	// Assure each map implements forEach.
	[certificates.map, costs.map, registrations.map, requirements.map].forEach(defineForEach);

	// Copy MultipleProcess resolvers.
	certificates.applicable  = getPropertyValue('certificates', 'applicable');
	costs.applicable         = getPropertyValue('costs', 'applicable');
	costs.payable            = getPropertyValue('costs', 'payable');
	costs.totalAmount        = getPropertyValue('costs', 'totalAmount');
	registrations.applicable = getPropertyValue('registrations', 'applicable');
	registrations.mandatory  = getPropertyValue('registrations', 'mandatory');
	registrations.optional   = getPropertyValue('registrations', 'optional');
	registrations.requested  = getPropertyValue('registrations', 'requested');
	requirements.resolved    = getPropertyValue('requirements', 'resolved');
	requirements.applicable  = getPropertyValue('requirements', 'applicable');

	// Copy less interesting (support) stuff also.
	currencyType.format = Currency.format;
	currencyType.symbol = db.Cost.prototype.getDescriptor('amount').type.symbol;
	currencyType.isoCode = db.Cost.prototype.getDescriptor('amount').type.isoCode;
	costType.step = Cost.prototype.getDescriptor('amount').step;

	// Add everything to legacyDb
	legacyDb[businessProcessType.__id__] = {
		prototype: {
			certificates: certificates,
			costs: costs,
			registrations: registrations,
			requirements: requirements
		}
	};
	legacyDb.Currency = currencyType;
	legacyDb.Cost = costType;

	// Allow customizations on legacyDb.
	if (options.customizeCb) options.customizeCb(legacyDb);

	// Store file.
	return writeFile(filename,
			'\'use strict\';\n\n' +
			'var $ = require(\'mano-legacy\');\n' +
			'require(\'mano-legacy/dbjs-map-for-each\');\n\n' +
			'module.exports = ' + serialize(legacyDb) + ';\n');
};
<|endoftext|>


<commit_before>'use strict';

var findNodeModules = require('find-node-modules'),
    getModuleName = require('filename-to-module-name'),
    validateNpmPackageName = require('validate-npm-package-name'),
    zipmap = require('zipmap'),
    xtend = require('xtend'),
    callsites = require('callsites');

var fs = require('fs'),
    path = require('path');

var nodeModulesPaths = function (basedir) {
  return findNodeModules({
    cwd: basedir,
    relative: false
  });
};

var validateModuleName = function (name) {
  var v = validateNpmPackageName(name);
  return v.validForOldPackages || v.validForNewPackages;
};

var safeResolve = function (modulePath) {
  try {
    return require.resolve(modulePath);
  }
  catch (e) {}
};

var acquire = function acquire(opts) {
  var modules = acquire.resolve(opts);
  Object.keys(modules).forEach(function (m) {
    modules[m] = require(modules[m]);
  });
  return modules;
};

acquire.resolve = function (opts) {
  opts = opts || {};
  opts.basedir = opts.basedir || path.dirname(callsites()[1].getFileName());
  opts.depth = opts.depth || 1;

  var dirs = nodeModulesPaths(opts.basedir)
        .slice(0, opts.depth)
        .reverse();

  return xtend.apply(null, dirs.map(function (dir) {
    return zipmap(fs.readdirSync(dir).map(function (basename) {
      if (!validateModuleName(basename)) {
        return;
      }

      var modulePath = path.resolve(dir, basename);
      var moduleName = getModuleName(modulePath);
      modulePath = (opts.skipFailures ? safeResolve : require.resolve)(modulePath);
      return modulePath ? [moduleName, modulePath] : undefined;
    }).filter(Boolean));
  }));
};

module.exports = acquire;
<commit_msg>Fix callsite position for acquire<commit_after>'use strict';

var findNodeModules = require('find-node-modules'),
    getModuleName = require('filename-to-module-name'),
    validateNpmPackageName = require('validate-npm-package-name'),
    zipmap = require('zipmap'),
    xtend = require('xtend'),
    callsites = require('callsites');

var fs = require('fs'),
    path = require('path');

var nodeModulesPaths = function (basedir) {
  return findNodeModules({
    cwd: basedir,
    relative: false
  });
};

var validateModuleName = function (name) {
  var v = validateNpmPackageName(name);
  return v.validForOldPackages || v.validForNewPackages;
};

var safeResolve = function (modulePath) {
  try {
    return require.resolve(modulePath);
  }
  catch (e) {}
};

var acquire = function acquire(opts) {
  opts = opts || {};
  opts.basedir = opts.basedir || path.dirname(callsites()[1].getFileName());

  var modules = acquire.resolve(opts);
  Object.keys(modules).forEach(function (m) {
    modules[m] = require(modules[m]);
  });
  return modules;
};

acquire.resolve = function (opts) {
  opts = opts || {};
  opts.basedir = opts.basedir || path.dirname(callsites()[1].getFileName());
  opts.depth = opts.depth || 1;

  var dirs = nodeModulesPaths(opts.basedir)
        .slice(0, opts.depth)
        .reverse();

  return xtend.apply(null, dirs.map(function (dir) {
    return zipmap(fs.readdirSync(dir).map(function (basename) {
      if (!validateModuleName(basename)) {
        return;
      }

      var modulePath = path.resolve(dir, basename);
      var moduleName = getModuleName(modulePath);
      modulePath = (opts.skipFailures ? safeResolve : require.resolve)(modulePath);
      return modulePath ? [moduleName, modulePath] : undefined;
    }).filter(Boolean));
  }));
};

module.exports = acquire;
<|endoftext|>


<commit_before>define([
	"dojo/_base/array", // array.forEach array.some
	"dojo/aspect",
	"dojo/_base/declare", // declare
	"dojo/dom", // dom.isDescendant
	"dojo/dom-attr", // domAttr.set
	"dojo/dom-construct", // domConstruct.create domConstruct.destroy
	"dojo/dom-geometry", // domGeometry.isBodyLtr
	"dojo/dom-style", // domStyle.set
	"dojo/has", // has("config-bgIframe")
	"dojo/keys",
	"dojo/_base/lang", // lang.hitch
	"dojo/on",
	"./place",
	"./BackgroundIframe",
	"./main"    // dijit (defining dijit.popup to match API doc)
], function(array, aspect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, has, keys, lang, on, place, BackgroundIframe, dijit){

	// module:
	//		dijit/popup

	/*=====
	 var __OpenArgs = {
	 // popup: Widget
	 //		widget to display
	 // parent: Widget
	 //		the button etc. that is displaying this popup
	 // around: DomNode
	 //		DOM node (typically a button); place popup relative to this node.  (Specify this *or* "x" and "y" parameters.)
	 // x: Integer
	 //		Absolute horizontal position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
	 // y: Integer
	 //		Absolute vertical position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
	 // orient: Object|String
	 //		When the around parameter is specified, orient should be a list of positions to try, ex:
	 //	|	[ "below", "above" ]
	 //		For backwards compatibility it can also be an (ordered) hash of tuples of the form
	 //		(around-node-corner, popup-node-corner), ex:
	 //	|	{ "BL": "TL", "TL": "BL" }
	 //		where BL means "bottom left" and "TL" means "top left", etc.
	 //
	 //		dijit/popup.open() tries to position the popup according to each specified position, in order,
	 //		until the popup appears fully within the viewport.
	 //
	 //		The default value is ["below", "above"]
	 //
	 //		When an (x,y) position is specified rather than an around node, orient is either
	 //		"R" or "L".  R (for right) means that it tries to put the popup to the right of the mouse,
	 //		specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
	 //		fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
	 //		and the top-right corner.
	 // onCancel: Function
	 //		callback when user has canceled the popup by:
	 //
	 //		1. hitting ESC or
	 //		2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
	 //		   i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
	 // onClose: Function
	 //		callback whenever this popup is closed
	 // onExecute: Function
	 //		callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
	 // padding: place.__Position
	 //		adding a buffer around the opening position. This is only useful when around is not set.
	 };
	 =====*/

	function destroyWrapper(){
		// summary:
		//		Function to destroy wrapper when popup widget is destroyed.
		//		Left in this scope to avoid memory leak on IE8 on refresh page, see #15206.
		if(this._popupWrapper){
			domConstruct.destroy(this._popupWrapper);
			delete this._popupWrapper;
		}
	}

	var PopupManager = declare(null, {
		// summary:
		//		Used to show drop downs (ex: the select list of a ComboBox)
		//		or popups (ex: right-click context menus).

		// _stack: dijit/_WidgetBase[]
		//		Stack of currently popped up widgets.
		//		(someone opened _stack[0], and then it opened _stack[1], etc.)
		_stack: [],

		// _beginZIndex: Number
		//		Z-index of the first popup.   (If first popup opens other
		//		popups they get a higher z-index.)
		_beginZIndex: 1000,

		_idGen: 1,

		_createWrapper: function(/*Widget*/ widget){
			// summary:
			//		Initialization for widgets that will be used as popups.
			//		Puts widget inside a wrapper DIV (if not already in one),
			//		and returns pointer to that wrapper DIV.

			var wrapper = widget._popupWrapper,
				node = widget.domNode;

			if(!wrapper){
				// Create wrapper <div> for when this widget [in the future] will be used as a popup.
				// This is done early because of IE bugs where creating/moving DOM nodes causes focus
				// to go wonky, see tests/robot/Toolbar.html to reproduce
				wrapper = domConstruct.create("div", {
					"class": "dijitPopup",
					style: { display: "none"},
					role: "region",
					"aria-label": widget["aria-label"] || widget.label || widget.name || widget.id
				}, widget.ownerDocumentBody);
				wrapper.appendChild(node);

				var s = node.style;
				s.display = "";
				s.visibility = "";
				s.position = "";
				s.top = "0px";

				widget._popupWrapper = wrapper;
				aspect.after(widget, "destroy", destroyWrapper, true);
			}

			return wrapper;
		},

		moveOffScreen: function(/*Widget*/ widget){
			// summary:
			//		Moves the popup widget off-screen.
			//		Do not use this method to hide popups when not in use, because
			//		that will create an accessibility issue: the offscreen popup is
			//		still in the tabbing order.

			// Create wrapper if not already there
			var wrapper = this._createWrapper(widget);

			// Besides setting visibility:hidden, move it out of the viewport, see #5776, #10111, #13604
			var ltr = domGeometry.isBodyLtr(widget.ownerDocument),
				style = {
					visibility: "hidden",
					top: "-9999px",
					display: ""
				};
			style[ltr ? "left" : "right"] = "-9999px";
			style[ltr ? "right" : "left"] = "auto";
			domStyle.set(wrapper, style);
		},

		hide: function(/*Widget*/ widget){
			// summary:
			//		Hide this popup widget (until it is ready to be shown).
			//		Initialization for widgets that will be used as popups
			//
			//		Also puts widget inside a wrapper DIV (if not already in one)
			//
			//		If popup widget needs to layout it should
			//		do so when it is made visible, and popup._onShow() is called.

			// Create wrapper if not already there
			var wrapper = this._createWrapper(widget);

			domStyle.set(wrapper, "display", "none");
		},

		getTopPopup: function(){
			// summary:
			//		Compute the closest ancestor popup that's *not* a child of another popup.
			//		Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
			var stack = this._stack;
			for(var pi = stack.length - 1; pi > 0 && stack[pi].parent === stack[pi - 1].widget; pi--){
				/* do nothing, just trying to get right value for pi */
			}
			return stack[pi];
		},

		open: function(/*__OpenArgs*/ args){
			// summary:
			//		Popup the widget at the specified position
			//
			// example:
			//		opening at the mouse position
			//		|		popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
			//
			// example:
			//		opening the widget as a dropdown
			//		|		popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
			//
			//		Note that whatever widget called dijit/popup.open() should also listen to its own _onBlur callback
			//		(fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.

			var stack = this._stack,
				widget = args.popup,
				orient = args.orient || ["below", "below-alt", "above", "above-alt"],
				ltr = args.parent ? args.parent.isLeftToRight() : domGeometry.isBodyLtr(widget.ownerDocument),
				around = args.around,
				id = (args.around && args.around.id) ? (args.around.id + "_dropdown") : ("popup_" + this._idGen++);

			// If we are opening a new popup that isn't a child of a currently opened popup, then
			// close currently opened popup(s).   This should happen automatically when the old popups
			// gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
			while(stack.length && (!args.parent || !dom.isDescendant(args.parent.domNode, stack[stack.length - 1].widget.domNode))){
				this.close(stack[stack.length - 1].widget);
			}

			// Get pointer to popup wrapper, and create wrapper if it doesn't exist
			var wrapper = this._createWrapper(widget);

			domAttr.set(wrapper, {
				id: id,
				style: {
					zIndex: this._beginZIndex + stack.length
				},
				"class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] + "Popup",
				dijitPopupParent: args.parent ? args.parent.id : ""
			});

			if(has("config-bgIframe") && !widget.bgIframe){
				// setting widget.bgIframe triggers cleanup in _Widget.destroy()
				widget.bgIframe = new BackgroundIframe(wrapper);
			}

			// position the wrapper node and make it visible
			var layoutFunc = widget.orient ? lang.hitch(widget, "orient") : null,
				best = around ?
					place.around(wrapper, around, orient, ltr, layoutFunc) :
					place.at(wrapper, args, orient == 'R' ? ['TR', 'BR', 'TL', 'BL'] : ['TL', 'BL', 'TR', 'BR'], args.padding,
						layoutFunc);

			wrapper.style.display = "";
			wrapper.style.visibility = "visible";
			widget.domNode.style.visibility = "visible";	// counteract effects from _HasDropDown

			var handlers = [];

			// provide default escape and tab key handling
			// (this will work for any widget, not just menu)
			handlers.push(on(wrapper, "keydown", lang.hitch(this, function(evt){
				if(evt.keyCode == keys.ESCAPE && args.onCancel){
					evt.stopPropagation();
					evt.preventDefault();
					args.onCancel();
				}else if(evt.keyCode == keys.TAB){
					evt.stopPropagation();
					evt.preventDefault();
					var topPopup = this.getTopPopup();
					if(topPopup && topPopup.onCancel){
						topPopup.onCancel();
					}
				}
			})));

			// watch for cancel/execute events on the popup and notify the caller
			// (for a menu, "execute" means clicking an item)
			if(widget.onCancel && args.onCancel){
				handlers.push(widget.on("cancel", args.onCancel));
			}

			handlers.push(widget.on(widget.onExecute ? "execute" : "change", lang.hitch(this, function(){
				var topPopup = this.getTopPopup();
				if(topPopup && topPopup.onExecute){
					topPopup.onExecute();
				}
			})));

			stack.push({
				widget: widget,
				parent: args.parent,
				onExecute: args.onExecute,
				onCancel: args.onCancel,
				onClose: args.onClose,
				handlers: handlers
			});

			if(widget.onOpen){
				// TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
				widget.onOpen(best);
			}

			return best;
		},

		close: function(/*Widget?*/ popup){
			// summary:
			//		Close specified popup and any popups that it parented.
			//		If no popup is specified, closes all popups.

			var stack = this._stack;

			// Basically work backwards from the top of the stack closing popups
			// until we hit the specified popup, but IIRC there was some issue where closing
			// a popup would cause others to close too.  Thus if we are trying to close B in [A,B,C]
			// closing C might close B indirectly and then the while() condition will run where stack==[A]...
			// so the while condition is constructed defensively.
			while((popup && array.some(stack, function(elem){
				return elem.widget == popup;
			})) ||
				(!popup && stack.length)){
				var top = stack.pop(),
					widget = top.widget,
					onClose = top.onClose;

				if(widget.onClose){
					// TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here).
					// Actually, StackContainer also calls onClose(), but to mean that the pane is being deleted
					// (i.e. that the TabContainer's tab's [x] icon was clicked)
					widget.onClose();
				}

				var h;
				while(h = top.handlers.pop()){
					h.remove();
				}

				// Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
				if(widget && widget.domNode){
					this.hide(widget);
				}

				if(onClose){
					onClose();
				}
			}
		}
	});

	return (dijit.popup = new PopupManager());
});
<commit_msg>fix indentation, refs #7390 !strict<commit_after>define([
	"dojo/_base/array", // array.forEach array.some
	"dojo/aspect",
	"dojo/_base/declare", // declare
	"dojo/dom", // dom.isDescendant
	"dojo/dom-attr", // domAttr.set
	"dojo/dom-construct", // domConstruct.create domConstruct.destroy
	"dojo/dom-geometry", // domGeometry.isBodyLtr
	"dojo/dom-style", // domStyle.set
	"dojo/has", // has("config-bgIframe")
	"dojo/keys",
	"dojo/_base/lang", // lang.hitch
	"dojo/on",
	"./place",
	"./BackgroundIframe",
	"./main"    // dijit (defining dijit.popup to match API doc)
], function(array, aspect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, has, keys, lang, on, place, BackgroundIframe, dijit){

	// module:
	//		dijit/popup

	/*=====
	 var __OpenArgs = {
		 // popup: Widget
		 //		widget to display
		 // parent: Widget
		 //		the button etc. that is displaying this popup
		 // around: DomNode
		 //		DOM node (typically a button); place popup relative to this node.  (Specify this *or* "x" and "y" parameters.)
		 // x: Integer
		 //		Absolute horizontal position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
		 // y: Integer
		 //		Absolute vertical position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
		 // orient: Object|String
		 //		When the around parameter is specified, orient should be a list of positions to try, ex:
		 //	|	[ "below", "above" ]
		 //		For backwards compatibility it can also be an (ordered) hash of tuples of the form
		 //		(around-node-corner, popup-node-corner), ex:
		 //	|	{ "BL": "TL", "TL": "BL" }
		 //		where BL means "bottom left" and "TL" means "top left", etc.
		 //
		 //		dijit/popup.open() tries to position the popup according to each specified position, in order,
		 //		until the popup appears fully within the viewport.
		 //
		 //		The default value is ["below", "above"]
		 //
		 //		When an (x,y) position is specified rather than an around node, orient is either
		 //		"R" or "L".  R (for right) means that it tries to put the popup to the right of the mouse,
		 //		specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
		 //		fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
		 //		and the top-right corner.
		 // onCancel: Function
		 //		callback when user has canceled the popup by:
		 //
		 //		1. hitting ESC or
		 //		2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
		 //		   i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
		 // onClose: Function
		 //		callback whenever this popup is closed
		 // onExecute: Function
		 //		callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
		 // padding: place.__Position
		 //		adding a buffer around the opening position. This is only useful when around is not set.
	 };
	 =====*/

	function destroyWrapper(){
		// summary:
		//		Function to destroy wrapper when popup widget is destroyed.
		//		Left in this scope to avoid memory leak on IE8 on refresh page, see #15206.
		if(this._popupWrapper){
			domConstruct.destroy(this._popupWrapper);
			delete this._popupWrapper;
		}
	}

	var PopupManager = declare(null, {
		// summary:
		//		Used to show drop downs (ex: the select list of a ComboBox)
		//		or popups (ex: right-click context menus).

		// _stack: dijit/_WidgetBase[]
		//		Stack of currently popped up widgets.
		//		(someone opened _stack[0], and then it opened _stack[1], etc.)
		_stack: [],

		// _beginZIndex: Number
		//		Z-index of the first popup.   (If first popup opens other
		//		popups they get a higher z-index.)
		_beginZIndex: 1000,

		_idGen: 1,

		_createWrapper: function(/*Widget*/ widget){
			// summary:
			//		Initialization for widgets that will be used as popups.
			//		Puts widget inside a wrapper DIV (if not already in one),
			//		and returns pointer to that wrapper DIV.

			var wrapper = widget._popupWrapper,
				node = widget.domNode;

			if(!wrapper){
				// Create wrapper <div> for when this widget [in the future] will be used as a popup.
				// This is done early because of IE bugs where creating/moving DOM nodes causes focus
				// to go wonky, see tests/robot/Toolbar.html to reproduce
				wrapper = domConstruct.create("div", {
					"class": "dijitPopup",
					style: { display: "none"},
					role: "region",
					"aria-label": widget["aria-label"] || widget.label || widget.name || widget.id
				}, widget.ownerDocumentBody);
				wrapper.appendChild(node);

				var s = node.style;
				s.display = "";
				s.visibility = "";
				s.position = "";
				s.top = "0px";

				widget._popupWrapper = wrapper;
				aspect.after(widget, "destroy", destroyWrapper, true);
			}

			return wrapper;
		},

		moveOffScreen: function(/*Widget*/ widget){
			// summary:
			//		Moves the popup widget off-screen.
			//		Do not use this method to hide popups when not in use, because
			//		that will create an accessibility issue: the offscreen popup is
			//		still in the tabbing order.

			// Create wrapper if not already there
			var wrapper = this._createWrapper(widget);

			// Besides setting visibility:hidden, move it out of the viewport, see #5776, #10111, #13604
			var ltr = domGeometry.isBodyLtr(widget.ownerDocument),
				style = {
					visibility: "hidden",
					top: "-9999px",
					display: ""
				};
			style[ltr ? "left" : "right"] = "-9999px";
			style[ltr ? "right" : "left"] = "auto";
			domStyle.set(wrapper, style);
		},

		hide: function(/*Widget*/ widget){
			// summary:
			//		Hide this popup widget (until it is ready to be shown).
			//		Initialization for widgets that will be used as popups
			//
			//		Also puts widget inside a wrapper DIV (if not already in one)
			//
			//		If popup widget needs to layout it should
			//		do so when it is made visible, and popup._onShow() is called.

			// Create wrapper if not already there
			var wrapper = this._createWrapper(widget);

			domStyle.set(wrapper, "display", "none");
		},

		getTopPopup: function(){
			// summary:
			//		Compute the closest ancestor popup that's *not* a child of another popup.
			//		Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
			var stack = this._stack;
			for(var pi = stack.length - 1; pi > 0 && stack[pi].parent === stack[pi - 1].widget; pi--){
				/* do nothing, just trying to get right value for pi */
			}
			return stack[pi];
		},

		open: function(/*__OpenArgs*/ args){
			// summary:
			//		Popup the widget at the specified position
			//
			// example:
			//		opening at the mouse position
			//		|		popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
			//
			// example:
			//		opening the widget as a dropdown
			//		|		popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
			//
			//		Note that whatever widget called dijit/popup.open() should also listen to its own _onBlur callback
			//		(fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.

			var stack = this._stack,
				widget = args.popup,
				orient = args.orient || ["below", "below-alt", "above", "above-alt"],
				ltr = args.parent ? args.parent.isLeftToRight() : domGeometry.isBodyLtr(widget.ownerDocument),
				around = args.around,
				id = (args.around && args.around.id) ? (args.around.id + "_dropdown") : ("popup_" + this._idGen++);

			// If we are opening a new popup that isn't a child of a currently opened popup, then
			// close currently opened popup(s).   This should happen automatically when the old popups
			// gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
			while(stack.length && (!args.parent || !dom.isDescendant(args.parent.domNode, stack[stack.length - 1].widget.domNode))){
				this.close(stack[stack.length - 1].widget);
			}

			// Get pointer to popup wrapper, and create wrapper if it doesn't exist
			var wrapper = this._createWrapper(widget);

			domAttr.set(wrapper, {
				id: id,
				style: {
					zIndex: this._beginZIndex + stack.length
				},
				"class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] + "Popup",
				dijitPopupParent: args.parent ? args.parent.id : ""
			});

			if(has("config-bgIframe") && !widget.bgIframe){
				// setting widget.bgIframe triggers cleanup in _Widget.destroy()
				widget.bgIframe = new BackgroundIframe(wrapper);
			}

			// position the wrapper node and make it visible
			var layoutFunc = widget.orient ? lang.hitch(widget, "orient") : null,
				best = around ?
					place.around(wrapper, around, orient, ltr, layoutFunc) :
					place.at(wrapper, args, orient == 'R' ? ['TR', 'BR', 'TL', 'BL'] : ['TL', 'BL', 'TR', 'BR'], args.padding,
						layoutFunc);

			wrapper.style.display = "";
			wrapper.style.visibility = "visible";
			widget.domNode.style.visibility = "visible";	// counteract effects from _HasDropDown

			var handlers = [];

			// provide default escape and tab key handling
			// (this will work for any widget, not just menu)
			handlers.push(on(wrapper, "keydown", lang.hitch(this, function(evt){
				if(evt.keyCode == keys.ESCAPE && args.onCancel){
					evt.stopPropagation();
					evt.preventDefault();
					args.onCancel();
				}else if(evt.keyCode == keys.TAB){
					evt.stopPropagation();
					evt.preventDefault();
					var topPopup = this.getTopPopup();
					if(topPopup && topPopup.onCancel){
						topPopup.onCancel();
					}
				}
			})));

			// watch for cancel/execute events on the popup and notify the caller
			// (for a menu, "execute" means clicking an item)
			if(widget.onCancel && args.onCancel){
				handlers.push(widget.on("cancel", args.onCancel));
			}

			handlers.push(widget.on(widget.onExecute ? "execute" : "change", lang.hitch(this, function(){
				var topPopup = this.getTopPopup();
				if(topPopup && topPopup.onExecute){
					topPopup.onExecute();
				}
			})));

			stack.push({
				widget: widget,
				parent: args.parent,
				onExecute: args.onExecute,
				onCancel: args.onCancel,
				onClose: args.onClose,
				handlers: handlers
			});

			if(widget.onOpen){
				// TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
				widget.onOpen(best);
			}

			return best;
		},

		close: function(/*Widget?*/ popup){
			// summary:
			//		Close specified popup and any popups that it parented.
			//		If no popup is specified, closes all popups.

			var stack = this._stack;

			// Basically work backwards from the top of the stack closing popups
			// until we hit the specified popup, but IIRC there was some issue where closing
			// a popup would cause others to close too.  Thus if we are trying to close B in [A,B,C]
			// closing C might close B indirectly and then the while() condition will run where stack==[A]...
			// so the while condition is constructed defensively.
			while((popup && array.some(stack, function(elem){
				return elem.widget == popup;
			})) ||
				(!popup && stack.length)){
				var top = stack.pop(),
					widget = top.widget,
					onClose = top.onClose;

				if(widget.onClose){
					// TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here).
					// Actually, StackContainer also calls onClose(), but to mean that the pane is being deleted
					// (i.e. that the TabContainer's tab's [x] icon was clicked)
					widget.onClose();
				}

				var h;
				while(h = top.handlers.pop()){
					h.remove();
				}

				// Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
				if(widget && widget.domNode){
					this.hide(widget);
				}

				if(onClose){
					onClose();
				}
			}
		}
	});

	return (dijit.popup = new PopupManager());
});
<|endoftext|>