<commit_before>var _ = require("lodash");
var when = require("when");
var mm = require("minimatch");
var invalid = require("./invalid-error");
var bResource = require("./resource");
var fr = require("./resource-file-resolver");
var combiner = require("./resource-combiner");
var loadPath = require("./load-path");

function rejected(err) {
    var deferred = when.defer();
    deferred.resolver.reject(err);
    return deferred.promise;
}

function rotateIndices(rs, resource) {
    var i, l;
    for (i = 0, l = rs.length; i < l; ++i) {
        if (rs[i] === resource) {
            for (i; i < l; ++i) {
                rs[i] = rs[i + 1];
            }
        }
    }
}

function serialize(cache, resource) {
    var d = when.defer();
    cache = cache || {};
    resource.serialize({
        includeContent: (cache[resource.path] || []).indexOf(resource.etag) < 0
    }).then(function (serialized) {
        if (resource.combine) {
            delete serialized.content;
            serialized.combine = resource.combine;
        }
        d.resolver.resolve(serialized);
    }, d.reject);
    return d.promise;
}

function partition(n, xs) {
    var res = [];
    var numFullGroups = Math.floor(xs.length / n);

    for (var i = 0; i < numFullGroups; i++) {
        res.push(xs.slice(i * n, ((i + 1) * n) - 1));
    }
    res.push(xs.slice(numFullGroups * n));
    return res;
}

function whenSequenceConcatIter(promises, deferred, result) {
    if (promises.length === 0) {
        deferred.resolve(result);
    } else {
        promises[0]().then(function (xs) {
            whenSequenceConcatIter(promises.slice(1), deferred, result.concat(xs));
        }, deferred.reject);
    }
}

function whenSequenceConcat(promises) {
    var deferred = when.defer();
    whenSequenceConcatIter(promises, deferred, []);
    return deferred.promise;
};

/**
 * Create new resource set, resolved from the provided rootPath,
 * or process.cwd()
 */
exports.create = function (rootPath) {
    rootPath = rootPath || process.cwd();
    var resources = {};
    var promises = [];
    var processors = [];
    var resourceSet;

    function deferredAdder() {
        var deferred = when.defer();
        promises.push(deferred);
        return deferred;
    }

    function whenAllAdded(cb, eb) {
        when.all(promises).then(cb, eb);
    }

    function addResource(resource) {
        if (!bResource.isResource(resource)) {
            resource = bResource.create(resource.path, resource);
        }
        var existingPaths = resourceSet.map(function (r) { return r.path; });
        var index = existingPaths.indexOf(resource.path);
        resources[resource.path] = resource;
        resourceSet[index >= 0 ? index : resourceSet.length++] = resource;
        processors.forEach(_.bind(resource, "addProcessor"));
        return resource;
    }

    function unmatchedPatterns(resourceSet, patterns) {
        var paths = resourceSet.map(function (r) { return r.path; });
        var options = { matchBase: true };
        return patterns.filter(function (pattern) {

            // prevent exclude pattern to be interpreted as unmatched
            if (pattern.charAt(0) === "!") {
                return false;
            }

            pattern = pattern.replace(/^\/?/, "/");
            return mm.match(paths, pattern, options).length === 0;
        });
    }

    function addMissingResources(rs, paths, matches, missing, callback) {
        var errorLabel = "Failed loading configuration: ";

        rs.addFileResources(missing).then(function () {
            var unmatched = unmatchedPatterns(rs, paths);
            if (unmatched.length > 0) {
                return callback(new Error(
                    errorLabel + "'" + unmatched.join("', '") +
                        "' matched no files or resources"
                ));
            }

            callback(null, matches);
        }, callback);
    }

    function resolvePathsAndAddMissingResources(paths, callback) {
        if (paths.length === 0) { return callback(null, []); }
        paths = Array.isArray(paths) ? paths : [paths];
        var rs = resourceSet;
        var errorLabel = "Failed loading " + paths.join(", ") + ": ";
        fr.resolvePaths(rs, paths, function (err, matches) {
            if (err) {
                err.message = errorLabel + err.message;
                return callback(err);
            }
            var missing = matches.filter(function (p) { return !rs.get(p); });
            addMissingResources(rs, paths, matches, missing, callback);
        }, { strict: false });
    }

    function cacheManifest() {
        return Object.keys(resources).reduce(function (manifest, path) {
            manifest[path] = [resources[path].etag];
            return manifest;
        }, {});
    }

    resourceSet = {
        length: 0,
        rootPath: rootPath,

        /**
         * Add all resources in array resources. Returns a promise.
         */
        addResources: function (resources) {

            var globResources = [];

            var nonGlobResources = resources.filter(function (resource) {
                if (typeof resource === "string") {
                    if (bResource.isQualified(resource)) {
                        return true;
                    } else {
                        globResources.push(resource);
                        return false;
                    }
                }
                return true;
            });

            var promises = [];
            if (globResources.length > 0) {
                promises.push(this.addGlobResources(globResources));
            }
            promises = promises.concat(
                nonGlobResources.map(_.bind(this, "addResource"))
            );

            return when.all(promises);
        },

        /**
         * Add single resource.
         *
         * Resource may be a resource object, a string, or an object of
         * properties supported by resource.create();
         *
         * When the resource is a string, it is passed on to addGlobResources.
         *
         * When the resource is an object of properties to pass on to
         * resource.create(), a couple of additional properties are supported:
         *
         * - path    Path is passed as first argument to resource.create()
         * - combine Array of resources to combine. Fails if any of the
         *           referenced resources are not part of the resource set.
         * - file    Add file as resource.
         *
         * Returns a promise. Promise will be rejected if adding an invalid
         * resource (see resource#validate).
         */
        addResource: function (resource) {
            if (typeof resource === "string") {
                if (bResource.isQualified(resource)) {
                    return this.addResource({ path: resource });
                } else {
                    return this.addGlobResources([resource]);
                }
            }
            var err = exports.validate(resource);
            if (err) { return rejected(err); }
            if (resource.file) {
                return this.addFileResource(resource.file, resource);
            }
            if (resource.combine) {
                return this.addCombinedResource(resource.combine, resource);
            }
            return when(addResource(resource));
        },

        /**
         * Add a single resource, synchronously.
         *
         * Only supports raw resource objects, no async resolving will take
         * place.
         *
         * Returns the resource.
         */
        add: function (resource) {
            return addResource(resource);
        },

        /**
         * Add a string as a resource. String will be taken as glob, and
         * all resulting paths are added as files (relative to rootPath).
         *
         * Returns a promise. Promise is rejected if glob pattern matches
         * no files, or if adding any resource fails.
         */
        addGlobResources: function (paths) {
            var d = deferredAdder();
            fr.resolvePaths(this, paths, function (e, resolvedPaths) {
                if (e || resolvedPaths.length === 0) {
                    var err = e || {
                        message: "'" + paths + "' matched no files"
                    };
                    return d.resolver.reject(err);
                }
                this.addFileResources(resolvedPaths).then(d.resolve, d.reject);
            }, { strict: false });
            return d.promise;
        },

        /**
         * Adds an array of paths as file resources.
         *
         * Returns a promise.
         */
        addFileResources: function (paths, rs) {
            return when.all((paths || []).map(function (path) {
                return this.addFileResource(path, rs);
            }.bind(this)));
        },

        /**
         * Adds a resource from a file on disk. Resource argument is
         * optional and can contain path and headers etc.
         *
         * Returns a promise.
         */
        addFileResource: function (path, rs) {
            var d = deferredAdder();
            fr.prepareResource(this.rootPath, path, rs || {}).then(
                function (res) {
                    res.path = res.path || path;
                    d.resolver.resolve(addResource(res));
                }.bind(this),
                d.reject
            );
            return d.promise;
        },

        /**
         * Adds a resource that combines other resources' contents for
         * its contents
         */
        addCombinedResource: function (sources, options) {
            var rs = this, d = when.defer();
            var attemptCombinedResource = function () {
                combiner.prepareResource(rs, sources, options).then(
                    function (res) {
                        var resource = addResource(res);
                        resource.combine = sources;
                        d.resolver.resolve(resource);
                    },
                    d.reject
                );
            };
            whenAllAdded(attemptCombinedResource, attemptCombinedResource);
            promises.push(d);
            return d.promise;
        },

        /**
         * Add processors to all existing and all future resources
         */
        addProcessor: function (processor) {
            processors.push(processor);
            Object.keys(resources).forEach(function (path) {
                resources[path].addProcessor(processor);
            });
        },

        /**
         * Process all resources. Only prods resources that actually have
         * processors. The method can optionally accept a cache manifest.
         * Resources whose etag matches one in the cache manifest will not be
         * processed.
         *
         * Returns a cache manifest with etag of processed resources.
         */
        process: function (manifest) {
            manifest = manifest || [];
            var d = when.defer();
            when.all(Object.keys(resources).filter(function (path) {
                return (manifest[path] || []).indexOf(resources[path].etag) < 0;
            }).map(function (path) {
                return resources[path].process();
            })).then(function () {
                d.resolver.resolve(cacheManifest());
            }, d.reject);
            return d.promise;
        },

        /**
         * Get the resource at path. Path is normalized, so:
         * rs.get("foo.js") ==== rs.get("/foo.js")
         */
        get: function (path) {
            return resources[bResource.normalizePath(path)];
        },

        /**
         * Remove resource at path. Returns true when successfully removed,
         * false if resource does not exist. Also removes from load.
         */
        remove: function (path) {
            path = bResource.normalizePath(path);
            rotateIndices(this, resources[path]);
            this.length -= 1;
            delete resources[path];
            this.loadPath.remove(path);
        },

        /**
         * Serializes the resource set as a fully resolved data structure.
         * Suitable for transmission over the wire. Returns a promise.
         */
        serialize: function (cached) {
            var d = when.defer();
            whenAllAdded(function () {
                var s = serialize.bind(null, cached);
                var groupedResources = partition(100, Array.prototype.slice.call(this));
                whenSequenceConcat(groupedResources.map(function (resources) {
                    return function () {
                        return when.all(resources.map(s));
                    }
                })).then(function (resources) {
                    d.resolver.resolve({
                        resources: resources,
                        loadPath: this.loadPath.paths()
                    });
                }.bind(this), d.reject);
            }.bind(this), d.reject);
            return d.promise;
        },

        /**
         * Merge resource set with others, returning a new resource set.
         */
        concat: function () {
            var sets = [this].concat([].slice.call(arguments));
            return sets.reduce(function (resourceSet, rs) {
                resourceSet.addResources(rs);
                resourceSet.loadPath.append(rs.loadPath.paths());
                return resourceSet;
            }, exports.create(this.rootPath));
        },

        matchPaths: function (paths) {
            var allPaths = this.map(function (r) { return r.path; });
            return _.flatten(paths.map(function (path) {
                return this.get(path) ? path : mm.match(allPaths, path, {
                    matchBase: true
                });
            }.bind(this)));
        },

        /**
         * High-level interface to adding paths to the load path. Also adds
         * corresponding resources, if not present.
         */
        appendLoad: function (paths) {
            var d = deferredAdder();
            var rs = this;
            resolvePathsAndAddMissingResources(paths, function (err, matches) {
                if (err) { return d.resolver.reject(err); }
                rs.loadPath.append(matches);
                d.resolver.resolve(rs.loadPath);
            });
            return d.promise;
        },

        /**
         * High-level interface to adding paths to the load path. Also adds
         * corresponding resources, if not present.
         */
        prependLoad: function (paths) {
            var d = deferredAdder();
            var rs = this;
            resolvePathsAndAddMissingResources(paths, function (err, matches) {
                if (err) { return d.resolver.reject(err); }
                rs.loadPath.prepend(matches);
                d.resolver.resolve(rs.loadPath);
            });
            return d.promise;
        },

        /**
         * Recursively call whenAllAdded until new promises stops popping up
         */
        whenAllAdded: function (cb, eb) {
            var rs = this, next;
            next = function (promiseCount) {
                if (promiseCount < promises.length) {
                    var callback = next.bind(null, promises.length);
                    whenAllAdded(callback, callback);
                } else {
                    whenAllAdded(cb.bind(null, rs), eb);
                }
            };
            next(0);
        }
    };

    // Mix in enumerators from Array.prototype
    ["forEach", "map", "reduce", "filter"].forEach(function (method) {
        resourceSet[method] = Array.prototype[method];
    });

    resourceSet.loadPath = loadPath.create(resourceSet);
    return resourceSet;
};

function setCount(resource, properties) {
    return properties.reduce(function (count, property) {
        return count + (resource[property] ? 1 : 0);
    }, 0);
}

/**
 * Validates properties on a resource.
 */
exports.validate = function (resource) {
    if (!resource) {
        return invalid("Resource must be a string, a resource " +
                       "object or an object of resource properties");
    }
    if (bResource.isResource(resource)) { return; }
    var count = setCount(resource, ["backend", "file", "combine", "content"]);
    if (count > 1) {
        return invalid("Resource can only have one of content, " +
                       "file, backend, combine");
    }
    if (!resource.path) {
        return invalid("Resource must have path " + JSON.stringify(resource));
    }
    if (!resource.combine && !resource.file &&
            !bResource.isQualified(resource.path)) {
        return bResource.validate(resource);
    }
};

function addResourceWithAlternative(resource) {
    var d = when.defer();
    this.addResource(resource).then(function (r) {
        (resource.alternatives || []).forEach(function (alt) {
            r.addAlternative(alt);
        });
        d.resolve(r);
    }, d.reject);
    return d.promise;
}

/**
 * De-serializes data structures created by serialize(). Returns a
 * promise.
 */
exports.deserialize = function (data) {
    var d = when.defer();
    var rs = exports.create();
    data = data || {};
    var resources = data.resources || [];
    var promises = resources.map(_.bind(addResourceWithAlternative, rs));
    when.all(promises).then(function () {
        rs.loadPath.append(data.loadPath || []);
        d.resolver.resolve(rs);
    }, d.reject);
    return d.promise;
};
<commit_msg>Reject promise if an exception occurs.<commit_after>var _ = require("lodash");
var when = require("when");
var mm = require("minimatch");
var invalid = require("./invalid-error");
var bResource = require("./resource");
var fr = require("./resource-file-resolver");
var combiner = require("./resource-combiner");
var loadPath = require("./load-path");

function rejected(err) {
    var deferred = when.defer();
    deferred.resolver.reject(err);
    return deferred.promise;
}

function rotateIndices(rs, resource) {
    var i, l;
    for (i = 0, l = rs.length; i < l; ++i) {
        if (rs[i] === resource) {
            for (i; i < l; ++i) {
                rs[i] = rs[i + 1];
            }
        }
    }
}

function serialize(cache, resource) {
    var d = when.defer();
    cache = cache || {};
    resource.serialize({
        includeContent: (cache[resource.path] || []).indexOf(resource.etag) < 0
    }).then(function (serialized) {
        if (resource.combine) {
            delete serialized.content;
            serialized.combine = resource.combine;
        }
        d.resolver.resolve(serialized);
    }, d.reject);
    return d.promise;
}

function partition(n, xs) {
    var res = [];
    var numFullGroups = Math.floor(xs.length / n);

    for (var i = 0; i < numFullGroups; i++) {
        res.push(xs.slice(i * n, ((i + 1) * n) - 1));
    }
    res.push(xs.slice(numFullGroups * n));
    return res;
}

function whenSequenceConcatIter(promises, deferred, result) {
    if (promises.length === 0) {
        deferred.resolve(result);
    } else {
        promises[0]().then(function (xs) {
            whenSequenceConcatIter(promises.slice(1), deferred, result.concat(xs));
        }, deferred.reject);
    }
}

function whenSequenceConcat(promises) {
    var deferred = when.defer();
    whenSequenceConcatIter(promises, deferred, []);
    return deferred.promise;
};

/**
 * Create new resource set, resolved from the provided rootPath,
 * or process.cwd()
 */
exports.create = function (rootPath) {
    rootPath = rootPath || process.cwd();
    var resources = {};
    var promises = [];
    var processors = [];
    var resourceSet;

    function deferredAdder() {
        var deferred = when.defer();
        promises.push(deferred);
        return deferred;
    }

    function whenAllAdded(cb, eb) {
        when.all(promises).then(cb, eb);
    }

    function addResource(resource) {
        if (!bResource.isResource(resource)) {
            resource = bResource.create(resource.path, resource);
        }
        var existingPaths = resourceSet.map(function (r) { return r.path; });
        var index = existingPaths.indexOf(resource.path);
        resources[resource.path] = resource;
        resourceSet[index >= 0 ? index : resourceSet.length++] = resource;
        processors.forEach(_.bind(resource, "addProcessor"));
        return resource;
    }

    function unmatchedPatterns(resourceSet, patterns) {
        var paths = resourceSet.map(function (r) { return r.path; });
        var options = { matchBase: true };
        return patterns.filter(function (pattern) {

            // prevent exclude pattern to be interpreted as unmatched
            if (pattern.charAt(0) === "!") {
                return false;
            }

            pattern = pattern.replace(/^\/?/, "/");
            return mm.match(paths, pattern, options).length === 0;
        });
    }

    function addMissingResources(rs, paths, matches, missing, callback) {
        var errorLabel = "Failed loading configuration: ";

        rs.addFileResources(missing).then(function () {
            var unmatched = unmatchedPatterns(rs, paths);
            if (unmatched.length > 0) {
                return callback(new Error(
                    errorLabel + "'" + unmatched.join("', '") +
                        "' matched no files or resources"
                ));
            }

            callback(null, matches);
        }, callback);
    }

    function resolvePathsAndAddMissingResources(paths, callback) {
        if (paths.length === 0) { return callback(null, []); }
        paths = Array.isArray(paths) ? paths : [paths];
        var rs = resourceSet;
        var errorLabel = "Failed loading " + paths.join(", ") + ": ";
        fr.resolvePaths(rs, paths, function (err, matches) {
            if (err) {
                err.message = errorLabel + err.message;
                return callback(err);
            }
            var missing = matches.filter(function (p) { return !rs.get(p); });
            addMissingResources(rs, paths, matches, missing, callback);
        }, { strict: false });
    }

    function cacheManifest() {
        return Object.keys(resources).reduce(function (manifest, path) {
            manifest[path] = [resources[path].etag];
            return manifest;
        }, {});
    }

    resourceSet = {
        length: 0,
        rootPath: rootPath,

        /**
         * Add all resources in array resources. Returns a promise.
         */
        addResources: function (resources) {

            var globResources = [];

            var nonGlobResources = resources.filter(function (resource) {
                if (typeof resource === "string") {
                    if (bResource.isQualified(resource)) {
                        return true;
                    } else {
                        globResources.push(resource);
                        return false;
                    }
                }
                return true;
            });

            var promises = [];
            if (globResources.length > 0) {
                promises.push(this.addGlobResources(globResources));
            }
            promises = promises.concat(
                nonGlobResources.map(_.bind(this, "addResource"))
            );

            return when.all(promises);
        },

        /**
         * Add single resource.
         *
         * Resource may be a resource object, a string, or an object of
         * properties supported by resource.create();
         *
         * When the resource is a string, it is passed on to addGlobResources.
         *
         * When the resource is an object of properties to pass on to
         * resource.create(), a couple of additional properties are supported:
         *
         * - path    Path is passed as first argument to resource.create()
         * - combine Array of resources to combine. Fails if any of the
         *           referenced resources are not part of the resource set.
         * - file    Add file as resource.
         *
         * Returns a promise. Promise will be rejected if adding an invalid
         * resource (see resource#validate).
         */
        addResource: function (resource) {
            if (typeof resource === "string") {
                if (bResource.isQualified(resource)) {
                    return this.addResource({ path: resource });
                } else {
                    return this.addGlobResources([resource]);
                }
            }
            var err = exports.validate(resource);
            if (err) { return rejected(err); }
            if (resource.file) {
                return this.addFileResource(resource.file, resource);
            }
            if (resource.combine) {
                return this.addCombinedResource(resource.combine, resource);
            }
            return when(addResource(resource));
        },

        /**
         * Add a single resource, synchronously.
         *
         * Only supports raw resource objects, no async resolving will take
         * place.
         *
         * Returns the resource.
         */
        add: function (resource) {
            return addResource(resource);
        },

        /**
         * Add a string as a resource. String will be taken as glob, and
         * all resulting paths are added as files (relative to rootPath).
         *
         * Returns a promise. Promise is rejected if glob pattern matches
         * no files, or if adding any resource fails.
         */
        addGlobResources: function (paths) {
            var d = deferredAdder();
            fr.resolvePaths(this, paths, function (e, resolvedPaths) {
                if (e || resolvedPaths.length === 0) {
                    var err = e || {
                        message: "'" + paths + "' matched no files"
                    };
                    return d.resolver.reject(err);
                }
                this.addFileResources(resolvedPaths).then(d.resolve, d.reject);
            }, { strict: false });
            return d.promise;
        },

        /**
         * Adds an array of paths as file resources.
         *
         * Returns a promise.
         */
        addFileResources: function (paths, rs) {
            return when.all((paths || []).map(function (path) {
                return this.addFileResource(path, rs);
            }.bind(this)));
        },

        /**
         * Adds a resource from a file on disk. Resource argument is
         * optional and can contain path and headers etc.
         *
         * Returns a promise.
         */
        addFileResource: function (path, rs) {
            var d = deferredAdder();
            fr.prepareResource(this.rootPath, path, rs || {}).then(
                function (res) {
                    res.path = res.path || path;
                    d.resolver.resolve(addResource(res));
                }.bind(this),
                d.reject
            );
            return d.promise;
        },

        /**
         * Adds a resource that combines other resources' contents for
         * its contents
         */
        addCombinedResource: function (sources, options) {
            var rs = this, d = when.defer();
            var attemptCombinedResource = function () {
                combiner.prepareResource(rs, sources, options).then(
                    function (res) {
                        var resource = addResource(res);
                        resource.combine = sources;
                        d.resolver.resolve(resource);
                    },
                    d.reject
                );
            };
            whenAllAdded(attemptCombinedResource, attemptCombinedResource);
            promises.push(d);
            return d.promise;
        },

        /**
         * Add processors to all existing and all future resources
         */
        addProcessor: function (processor) {
            processors.push(processor);
            Object.keys(resources).forEach(function (path) {
                resources[path].addProcessor(processor);
            });
        },

        /**
         * Process all resources. Only prods resources that actually have
         * processors. The method can optionally accept a cache manifest.
         * Resources whose etag matches one in the cache manifest will not be
         * processed.
         *
         * Returns a cache manifest with etag of processed resources.
         */
        process: function (manifest) {
            manifest = manifest || [];
            var d = when.defer();
            when.all(Object.keys(resources).filter(function (path) {
                return (manifest[path] || []).indexOf(resources[path].etag) < 0;
            }).map(function (path) {
                return resources[path].process();
            })).then(function () {
                d.resolver.resolve(cacheManifest());
            }, d.reject);
            return d.promise;
        },

        /**
         * Get the resource at path. Path is normalized, so:
         * rs.get("foo.js") ==== rs.get("/foo.js")
         */
        get: function (path) {
            return resources[bResource.normalizePath(path)];
        },

        /**
         * Remove resource at path. Returns true when successfully removed,
         * false if resource does not exist. Also removes from load.
         */
        remove: function (path) {
            path = bResource.normalizePath(path);
            rotateIndices(this, resources[path]);
            this.length -= 1;
            delete resources[path];
            this.loadPath.remove(path);
        },

        /**
         * Serializes the resource set as a fully resolved data structure.
         * Suitable for transmission over the wire. Returns a promise.
         */
        serialize: function (cached) {
            var d = when.defer();
            whenAllAdded(function () {
                var s = serialize.bind(null, cached);
                var groupedResources = partition(100, Array.prototype.slice.call(this));
                whenSequenceConcat(groupedResources.map(function (resources) {
                    return function () {
                        return when.all(resources.map(s));
                    }
                })).then(function (resources) {
                    d.resolver.resolve({
                        resources: resources,
                        loadPath: this.loadPath.paths()
                    });
                }.bind(this), d.reject);
            }.bind(this), d.reject);
            return d.promise;
        },

        /**
         * Merge resource set with others, returning a new resource set.
         */
        concat: function () {
            var sets = [this].concat([].slice.call(arguments));
            return sets.reduce(function (resourceSet, rs) {
                resourceSet.addResources(rs);
                resourceSet.loadPath.append(rs.loadPath.paths());
                return resourceSet;
            }, exports.create(this.rootPath));
        },

        matchPaths: function (paths) {
            var allPaths = this.map(function (r) { return r.path; });
            return _.flatten(paths.map(function (path) {
                return this.get(path) ? path : mm.match(allPaths, path, {
                    matchBase: true
                });
            }.bind(this)));
        },

        /**
         * High-level interface to adding paths to the load path. Also adds
         * corresponding resources, if not present.
         */
        appendLoad: function (paths) {
            var d = deferredAdder();
            var rs = this;
            resolvePathsAndAddMissingResources(paths, function (err, matches) {
                if (err) { return d.resolver.reject(err); }
                rs.loadPath.append(matches);
                d.resolver.resolve(rs.loadPath);
            });
            return d.promise;
        },

        /**
         * High-level interface to adding paths to the load path. Also adds
         * corresponding resources, if not present.
         */
        prependLoad: function (paths) {
            var d = deferredAdder();
            var rs = this;
            resolvePathsAndAddMissingResources(paths, function (err, matches) {
                if (err) { return d.resolver.reject(err); }
                rs.loadPath.prepend(matches);
                d.resolver.resolve(rs.loadPath);
            });
            return d.promise;
        },

        /**
         * Recursively call whenAllAdded until new promises stops popping up
         */
        whenAllAdded: function (cb, eb) {
            var rs = this, next;
            next = function (promiseCount) {
                if (promiseCount < promises.length) {
                    var callback = next.bind(null, promises.length);
                    whenAllAdded(callback, callback);
                } else {
                    whenAllAdded(cb.bind(null, rs), eb);
                }
            };
            next(0);
        }
    };

    // Mix in enumerators from Array.prototype
    ["forEach", "map", "reduce", "filter"].forEach(function (method) {
        resourceSet[method] = Array.prototype[method];
    });

    resourceSet.loadPath = loadPath.create(resourceSet);
    return resourceSet;
};

function setCount(resource, properties) {
    return properties.reduce(function (count, property) {
        return count + (resource[property] ? 1 : 0);
    }, 0);
}

/**
 * Validates properties on a resource.
 */
exports.validate = function (resource) {
    if (!resource) {
        return invalid("Resource must be a string, a resource " +
                       "object or an object of resource properties");
    }
    if (bResource.isResource(resource)) { return; }
    var count = setCount(resource, ["backend", "file", "combine", "content"]);
    if (count > 1) {
        return invalid("Resource can only have one of content, " +
                       "file, backend, combine");
    }
    if (!resource.path) {
        return invalid("Resource must have path " + JSON.stringify(resource));
    }
    if (!resource.combine && !resource.file &&
            !bResource.isQualified(resource.path)) {
        return bResource.validate(resource);
    }
};

function addResourceWithAlternative(resource) {
    var d = when.defer();
    this.addResource(resource).then(function (r) {
        (resource.alternatives || []).forEach(function (alt) {
            r.addAlternative(alt);
        });
        d.resolve(r);
    }, d.reject);
    return d.promise;
}

/**
 * De-serializes data structures created by serialize(). Returns a
 * promise.
 */
exports.deserialize = function (data) {
    var d = when.defer();
    var rs = exports.create();
    data = data || {};
    var resources = data.resources || [];
    var promises = resources.map(_.bind(addResourceWithAlternative, rs));
    when.all(promises).then(function () {
        try {
            rs.loadPath.append(data.loadPath || []);
            d.resolver.resolve(rs);
        } catch (e) {
            d.reject(e.message);
        }
    }, d.reject);
    return d.promise;
};
<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

function isFillDeprecate({ pages, ...rest }) {
  return {
    pages: pages.map(reducePage),
    ...rest,
  };
}

function reducePage({ elements, ...rest }) {
  return {
    elements: elements.map(updateElement),
    ...rest,
  };
}

function updateElement(element) {
  if (typeof element.isFill !== 'undefined') {
    delete element.isFill;
  }
  return element;
}

export default isFillDeprecate;
<commit_msg>Remove redundant typeof check.<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

function isFillDeprecate({ pages, ...rest }) {
  return {
    pages: pages.map(reducePage),
    ...rest,
  };
}

function reducePage({ elements, ...rest }) {
  return {
    elements: elements.map(updateElement),
    ...rest,
  };
}

function updateElement(element) {
  delete element.isFill;
  return element;
}

export default isFillDeprecate;
<|endoftext|>


<commit_before>/**
 * We.js route alias feature
 */
var we ;

var alias = {
  // alias cache
  cache: {},

  /**
   * Initialize we.js alias feature
   *
   * @param  {O}   we
   * @param  {Object}   we we.js
   * @param  {Function} cb callback
   */
  initialize: function initialize(w, cb) {
    we = w;
    alias.loadAndCacheAllALias().spread(function (results){
      for (var i = results.length - 1; i >= 0; i--) {
        alias.cache[results[i].target] = results[i];
      }

      cb();
    }).catch(cb);
  },

  /**
   * Load all alias in memory for use with sync methods
   *
   * @param  {Object}   we we.js
   * @param  {Function} cb callback
   * @return {Object} Sequelize query promisse
   */
  loadAndCacheAllALias: function loadAndCacheAllALias() {
    var we = require('../index.js');
    return we.db.defaultConnection.query('SELECT * FROM urlAlias');
  },

  /**
   * http handler to use in http.createServer
   */
  httpHandler: function httpHandler(req, res) {
    var we = require('../index.js');
    var self = this;

    // is a a alias for current url
    // save old url
    req.urlBeforeAlias = req.url;

    // only works with GET requests
    if (req.method != 'GET') return we.express.bind(this)(req, res);

    // skip alias
    for (var i = we.config.router.alias.excludePaths.length - 1; i >= 0; i--) {
     if (req.url.indexOf(we.config.router.alias.excludePaths[i]) === 0){
       return we.express.bind(this)(req, res);
     }
    }

    var urlParts = req.url.split(/[?#]/);
    var path = urlParts[0];

    if (!path) return we.express.bind(this)(req, res);

    var cacheUrlAlias = we.router.alias.cache[path];
    if (cacheUrlAlias) {

      var query = '';
      if (urlParts[1]) query =  '?' + urlParts[1];
      // set alias attrs for use in html redirect
      req.haveAlias = cacheUrlAlias;
      req.aliasQuery = query;

      return we.express.bind(self)(req, res);
    }

    // then check if current path have alias
    we.db.models.urlAlias.findOne({
      where: { alias: path }
    }).then(function (urlAlias) {
      if (urlAlias) {
        // save the url alias record
        req.urlAlias = urlAlias;

        req.url = urlAlias.target;

        if (urlParts[1]) req.url += '?' + urlParts[1];

        we.log.verbose('ulrAlias set for: ' + path + ' to: '+ req.url);

        we.express.bind(this)(req, res);
      } else {
        we.log.verbose('ulrAlias not found for:', path);
        // slug not found then continue with default express middlewares
        we.express.bind(self)(req, res);
      }
    }).catch(function errorInSlugHandler(err){
      we.log.error(err);
      res.status(500).end();
    });
  },

  /**
   * Check if path have a alias
   *
   * @param  {String} path
   * @return {String} Alias or null
   */
  forPath: function forPath(path) {
    if (alias.cache[path]) {
      return alias.cache[path].alias;
    } else {
      return null;
    }
  },

  /**
   * Return the alias if avaible or path
   *
   * @param  {String} path
   * @return {String} Alias or the path
   */
  resolvePath: function resolvePath(path) {
    if (alias.cache[path]) {
      return alias.cache[path].alias;
    } else {
      return path;
    }
  },

  /**
   * Check if a sequelize model class have alias
   *
   * @param  {Object} Model Sequelize model class
   * @return {Boolean}
   */
  modelHaveUrlAlias: function modelHaveUrlAlias(Model) {
    return Boolean (
      Model.options &&
      (Model.options.enableAlias !== false) &&
      Model.options.classMethods &&
      Model.options.classMethods.urlAlias
    );
  },

  // Model hook functions
  //

  /**
   * Code to run after create a record with alias
   *
   * @param  {Object}   record Sequelize record
   * @param  {Object}   opts   sequelize options
   * @param  {Function} done   callback
   */
  afterCreatedRecord: function afterCreatedRecord(record, opts, done) {
    if (!we) return done();

    var aliasObj = opts.model.options.classMethods.urlAlias(record);

    if (!aliasObj) return done();

    var newAlias = record.setAlias;
    if (newAlias) {
      aliasObj.alias = record.setAlias;
    }

    if (aliasObj.alias[0] !== '/')  aliasObj.alias = '/'+aliasObj.alias;

    we.db.models.urlAlias.create(aliasObj)
    .then(function (alS) {
      we.log.verbose('New url alias:', alS.id);
      done();
    }).catch(function (err){
      we.log.error('Error in generate url alias:', err);
      done();
    });
  },

  /**
   * Code to run after update a record with alias
   *
   * DISABLED by default
   *
   * @param  {Object}   record Sequelize record
   * @param  {Object}   opts   sequelize options
   * @param  {Function} done   callback
   */
  afterUpdatedRecord: function afterUpdatedRecord(record, opts, done) {
    if (!we) return done();

    var newAlias = record.setAlias;
    // only update if set setAlias body field
    if (!newAlias) return done();

    // aff stash if dont starts with slash
    if (newAlias[0] !== '/') record.setAlias = '/'+newAlias

    var aliasObj = {
      alias: record.setAlias,
      target: record.getUrlPath()
    };

    if (alias.cache[aliasObj.target]) {
      // alias found then update
      if (alias.cache[aliasObj.target].alias == aliasObj.alias) {
        // salved alias is same as new then do nothing
        return done();
      }

      we.db.models.urlAlias.findById(alias.cache[aliasObj.target].id)
      .then(function(a){
        a.updateAttributes(aliasObj).then(function(){
          done();
        }).catch(function (err){
          we.log.error('Error in updateAttributes url alias:', err);
          done();
        });
      }).catch(function (err) {
        we.log.error('Error in update url alias:', err);
        done();
      });
    } else {
      // this model dont have a alias, then create one
      we.db.models.urlAlias.create(aliasObj)
      .then(function (alS) {
        we.log.verbose('New url alias:', alS.id);
        done();
      }).catch(function (err){
        we.log.error('Error in generate url alias for:', err);
        done();
      });
    }
  },

  /**
   * Code to run after delete a record with alias
   *
   * @param  {Object}   record Sequelize record
   * @param  {Object}   opts   sequelize options
   * @param  {Function} done   callback
   */
  afterDeleteRecord: function afterDeleteRecord(record, opts, done) {
    if (!we) return done();

    var p = record.getUrlPath();

    if (alias.cache[p]) {
      we.db.models.urlAlias.findById(alias.cache[p].id)
      .then(function(a){
        a.destroy().then(function () {
          done();
        }).catch(function (err) {
          we.log.error('Error in delete url alias:', err);
          done();
        });
      }).catch(function (err) {
        we.log.error('Error in delete url alias:', err);
        done();
      });
    } else {
      done();
    }
  }
}

module.exports = alias;<commit_msg>update urls alias delete after delete one related model<commit_after>/**
 * We.js route alias feature
 */
var we ;

var alias = {
  // alias cache
  cache: {},

  /**
   * Initialize we.js alias feature
   *
   * @param  {O}   we
   * @param  {Object}   we we.js
   * @param  {Function} cb callback
   */
  initialize: function initialize(w, cb) {
    we = w;
    alias.loadAndCacheAllALias().spread(function (results){
      for (var i = results.length - 1; i >= 0; i--) {
        alias.cache[results[i].target] = results[i];
      }

      cb();
    }).catch(cb);
  },

  /**
   * Load all alias in memory for use with sync methods
   *
   * @param  {Object}   we we.js
   * @param  {Function} cb callback
   * @return {Object} Sequelize query promisse
   */
  loadAndCacheAllALias: function loadAndCacheAllALias() {
    var we = require('../index.js');
    return we.db.defaultConnection.query('SELECT * FROM urlAlias');
  },

  /**
   * http handler to use in http.createServer
   */
  httpHandler: function httpHandler(req, res) {
    var we = require('../index.js');
    var self = this;

    // is a a alias for current url
    // save old url
    req.urlBeforeAlias = req.url;

    // only works with GET requests
    if (req.method != 'GET') return we.express.bind(this)(req, res);

    // skip alias
    for (var i = we.config.router.alias.excludePaths.length - 1; i >= 0; i--) {
     if (req.url.indexOf(we.config.router.alias.excludePaths[i]) === 0){
       return we.express.bind(this)(req, res);
     }
    }

    var urlParts = req.url.split(/[?#]/);
    var path = urlParts[0];

    if (!path) return we.express.bind(this)(req, res);

    var cacheUrlAlias = we.router.alias.cache[path];
    if (cacheUrlAlias) {

      var query = '';
      if (urlParts[1]) query =  '?' + urlParts[1];
      // set alias attrs for use in html redirect
      req.haveAlias = cacheUrlAlias;
      req.aliasQuery = query;

      return we.express.bind(self)(req, res);
    }

    // then check if current path have alias
    we.db.models.urlAlias.findOne({
      where: { alias: path }
    }).then(function (urlAlias) {
      if (urlAlias) {
        // save the url alias record
        req.urlAlias = urlAlias;

        req.url = urlAlias.target;

        if (urlParts[1]) req.url += '?' + urlParts[1];

        we.log.verbose('ulrAlias set for: ' + path + ' to: '+ req.url);

        we.express.bind(this)(req, res);
      } else {
        we.log.verbose('ulrAlias not found for:', path);
        // slug not found then continue with default express middlewares
        we.express.bind(self)(req, res);
      }
    }).catch(function errorInSlugHandler(err){
      we.log.error(err);
      res.status(500).end();
    });
  },

  /**
   * Check if path have a alias
   *
   * @param  {String} path
   * @return {String} Alias or null
   */
  forPath: function forPath(path) {
    if (alias.cache[path]) {
      return alias.cache[path].alias;
    } else {
      return null;
    }
  },

  /**
   * Return the alias if avaible or path
   *
   * @param  {String} path
   * @return {String} Alias or the path
   */
  resolvePath: function resolvePath(path) {
    if (alias.cache[path]) {
      return alias.cache[path].alias;
    } else {
      return path;
    }
  },

  /**
   * Check if a sequelize model class have alias
   *
   * @param  {Object} Model Sequelize model class
   * @return {Boolean}
   */
  modelHaveUrlAlias: function modelHaveUrlAlias(Model) {
    return Boolean (
      Model.options &&
      (Model.options.enableAlias !== false) &&
      Model.options.classMethods &&
      Model.options.classMethods.urlAlias
    );
  },

  // Model hook functions
  //

  /**
   * Code to run after create a record with alias
   *
   * @param  {Object}   record Sequelize record
   * @param  {Object}   opts   sequelize options
   * @param  {Function} done   callback
   */
  afterCreatedRecord: function afterCreatedRecord(record, opts, done) {
    if (!we) return done();

    var aliasObj = opts.model.options.classMethods.urlAlias(record);

    if (!aliasObj) return done();

    var newAlias = record.setAlias;
    if (newAlias) {
      aliasObj.alias = record.setAlias;
    }

    if (aliasObj.alias[0] !== '/')  aliasObj.alias = '/'+aliasObj.alias;

    we.db.models.urlAlias.create(aliasObj)
    .then(function (alS) {
      we.log.verbose('New url alias:', alS.id);
      done();
    }).catch(function (err){
      we.log.error('Error in generate url alias:', err);
      done();
    });
  },

  /**
   * Code to run after update a record with alias
   *
   * DISABLED by default
   *
   * @param  {Object}   record Sequelize record
   * @param  {Object}   opts   sequelize options
   * @param  {Function} done   callback
   */
  afterUpdatedRecord: function afterUpdatedRecord(record, opts, done) {
    if (!we) return done();

    var newAlias = record.setAlias;
    // only update if set setAlias body field
    if (!newAlias) return done();

    // aff stash if dont starts with slash
    if (newAlias[0] !== '/') record.setAlias = '/'+newAlias

    var aliasObj = {
      alias: record.setAlias,
      target: record.getUrlPath()
    };

    if (alias.cache[aliasObj.target]) {
      // alias found then update
      if (alias.cache[aliasObj.target].alias == aliasObj.alias) {
        // salved alias is same as new then do nothing
        return done();
      }

      we.db.models.urlAlias.findById(alias.cache[aliasObj.target].id)
      .then(function(a){
        a.updateAttributes(aliasObj).then(function(){
          done();
        }).catch(function (err){
          we.log.error('Error in updateAttributes url alias:', err);
          done();
        });
      }).catch(function (err) {
        we.log.error('Error in update url alias:', err);
        done();
      });
    } else {
      // this model dont have a alias, then create one
      we.db.models.urlAlias.create(aliasObj)
      .then(function (alS) {
        we.log.verbose('New url alias:', alS.id);
        done();
      }).catch(function (err){
        we.log.error('Error in generate url alias for:', err);
        done();
      });
    }
  },

  /**
   * Code to run after delete a record with alias
   *
   * @param  {Object}   record Sequelize record
   * @param  {Object}   opts   sequelize options
   * @param  {Function} done   callback
   */
  afterDeleteRecord: function afterDeleteRecord(record, opts, done) {
    if (!we || !record.getUrlPath) return done();

    var p = record.getUrlPath();

    if (!p) return done();

    we.db.models.urlAlias.destroy({
      individualHooks: true,
      where: { target: p }
    }).then(function (count) {
      we.log.verbose(count+' url alias deleted for path: '+p);
      done();
    }).catch(function (err) {
      we.log.error('Error in delete url alias:', err);
      done();
    });
  }
}

module.exports = alias;<|endoftext|>


<commit_before>// Blog routes
exports.blog = require('./blog');

// Portfolio page
exports.projects = function (req, res) {
  res.render('projects', {
    page: 'projects',
    title: 'Todd Wolfson - Projects',
    navMargin: false,
    seoKeywords: 'dev tools, web tools, spritesmith, sexy-bash-prompt, File Watcher, jsmin-sourcemap, Find++',
    seoDescription: 'Projects by Todd Wolfson: spritesmith, sexy-bash-prompt, File Watcher, jsmin-sourcemap, Find++'
  });
};

// Contact route
exports.contact = require('./contact');

// Kaleido page
exports.kaleido = function (req, res) {
  // DEV: Relocate js-yaml inside route for dev-only dependencies
  var yaml = require('js-yaml'),
      urls = require('../../test/perceptual-tests/urls');
  res.render('kaleido', {layout: false, urls: urls});
};

// Render a LICENSE page
var fs = require('fs'),
    license = fs.readFileSync(__dirname + '/../../LICENSE-MIT', 'utf8');
exports.license = function (req, res) {
  res.render('license', {
    page: 'license',
    license: license,
    title: 'Todd Wolfson - License',
    seoKeywords: 'license, mit',
    seoDescription: 'twolfson.com uses the MIT License'
  });
};

// Render a health page
var pkg = require('../../package.json'),
    pkgVersion = pkg.version;
exports.health = function (req, res) {
  var retObj = {
        'version': pkgVersion,
        'uptime': process.uptime(),
        'memory': process.memoryUsage(),
        'pid': process.pid
      };
  res.send(retObj);
};

// 404 page (no SEO here)
exports['404'] = function (req, res) {
  res.status(404);
  res.render('404', {'page': '404'});
};<commit_msg>Adding TODO for self<commit_after>// Blog routes
exports.blog = require('./blog');

// Portfolio page
exports.projects = function (req, res) {
  res.render('projects', {
    page: 'projects',
    title: 'Todd Wolfson - Projects',
    // TODO: I really dislike view configuration being placed in the route
    navMargin: false,
    seoKeywords: 'dev tools, web tools, spritesmith, sexy-bash-prompt, File Watcher, jsmin-sourcemap, Find++',
    seoDescription: 'Projects by Todd Wolfson: spritesmith, sexy-bash-prompt, File Watcher, jsmin-sourcemap, Find++'
  });
};

// Contact route
exports.contact = require('./contact');

// Kaleido page
exports.kaleido = function (req, res) {
  // DEV: Relocate js-yaml inside route for dev-only dependencies
  var yaml = require('js-yaml'),
      urls = require('../../test/perceptual-tests/urls');
  res.render('kaleido', {layout: false, urls: urls});
};

// Render a LICENSE page
var fs = require('fs'),
    license = fs.readFileSync(__dirname + '/../../LICENSE-MIT', 'utf8');
exports.license = function (req, res) {
  res.render('license', {
    page: 'license',
    license: license,
    title: 'Todd Wolfson - License',
    seoKeywords: 'license, mit',
    seoDescription: 'twolfson.com uses the MIT License'
  });
};

// Render a health page
var pkg = require('../../package.json'),
    pkgVersion = pkg.version;
exports.health = function (req, res) {
  var retObj = {
        'version': pkgVersion,
        'uptime': process.uptime(),
        'memory': process.memoryUsage(),
        'pid': process.pid
      };
  res.send(retObj);
};

// 404 page (no SEO here)
exports['404'] = function (req, res) {
  res.status(404);
  res.render('404', {'page': '404'});
};<|endoftext|>


<commit_before>'use strict';
const spawn = require('child_process').spawn, EventEmitter = require('events').EventEmitter, util = require('util');
var os = require('os');
var childProcess = require('child_process');
var path = require('path');
var fs = require('fs');
/**
 * Stream constructor
 * @param {object} options
 * @param {string} options.input Stream uri, for example rtsp://r3---sn-5hn7su76.c.youtube.com/CiILENy73wIaGQnup-1SztVOYBMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp
 * @constructor
 */
var Live555Client = function (options) {
	var _testbundle = childProcess.exec(getCmd(), function (error, stdout, stderr) {
		if (error) {
			//console.log('error on test bundle:', error);
			//build
			console.log('build project,please wait...');
			var _configurepath = path.join(__dirname, 'extras', 'src', 'configure');
			//__dirname + '/extras/src/configure';
			var _startbuild = childProcess.exec(_configurepath, function (error, stdout, stderr) {
				if (error) {
					console.log('build failed:', error);
				} else {
					fs.mkdir(getDir(), function (err) {
						if (err) {
							console.log('build failed:', err);
						} else {
							var _buildpath = path.join(__dirname, 'extras', 'src', 'live555client');
							fs.rename(_buildpath, getCmd(), function (err) {
								if (err) {

									console.log('build failed:', err);
								} else {
									console.log('build success');
								}
							});
						}
					});
				}
			});
		}
	});
	if (options.input) {
		this.input = options.input;
	} else {
		throw new Error('no `input` parameter');
	}
	this.arguments = [];

	this.on('newListener', newListener.bind(this));
	this.on('removeListener', removeListener.bind(this));
	if (Object.observe && (typeof Proxy !== 'function')) {
		Object.observe(this, observer.bind(this));
	}
};

util.inherits(Live555Client, EventEmitter);

/**
 * Live555Client command name
 * @type {string}
 */
//Live555Client.cmd = __dirname + '/extras/Live555Client';

function newListener(event) {
	if (event === 'data' && this.listeners(event).length === 0) {
		this.start();
	}
}

function removeListener(event) {
	if (event === 'data' && this.listeners(event).length === 0) {
		this.stop();
	}
}

function installBundle(callback) {
	var _testchild = spawn(getCmd(), []);
	_testchild.on('error', function (err) {
		if (err.code == 'ENOENT') {

			//make start for linux
			//var _dir = __dirname + '/extras/src/';
			//var _buildscript = ''
			callback(false);
		}
	});
	_testchild.on('close', function (code) {
		callback(true);
	});
};

function getCmd() {
	var _clientname = 'live555client';
	var _platformarch = os.arch();
	var _platform = os.platform();
	return path.join(__dirname, 'extras', _platform, _platformarch, _clientname);
	//__dirname + '/extras/' + platform + '/' + _clientname;
}

function getDir() {
	var _platformarch = os.arch();
	var _platform = os.platform();
	return path.join(__dirname, 'extras', _platform, _platformarch);
	//__dirname + '/extras/' + platform + '/' + _clientname;
}

function observer(changes) {
	if (changes.some(function (change) {
		return change.type === 'update';
	})) {
		this.restart();
	}
}

Live555Client.prototype._args = function () {
	return this.arguments.concat([
		this.input
	]);
};

/**
 * Start Live555Client spawn process
 */
Live555Client.prototype.start = function () {
	var _client = this;
	this.child = spawn(getCmd(), this._args());
	this.child.stdout.on('data', this.emit.bind(this, 'data'));
	this.child.stderr.on('data', function (data) {
		throw new Error(data);
	});
	this.emit('start');

	this.child.on('error', function (err) {
		if (err.code == 'ENOENT') {
			console.log('You have not build Live555Client yet,trying to build it');
			_client.stop();
		}
	});
	this.child.on('close', function (code) {
		if (code === 0) {
			setTimeout(Live555Client.prototype.start.bind(this), 1000);
		}
	}.bind(this));
	/*this.child.on('error', function(code) {
	});*/
};

/**
 * Stop Live555Client spawn process
 */
Live555Client.prototype.stop = function () {
	this.child.kill();
	delete this.child;
	this.emit('stop');
};

/**
 * Restart Live555Client spawn process
 */
Live555Client.prototype.restart = function () {
	if (this.child) {
		this.stop();
		this.start();
	}
};

if (typeof Proxy === 'function') {
	Live555Client = new Proxy(Live555Client, {
		set: function (target, property) {
			if (property !== 'super_' && target[property] !== undefined) {
				target.restart();
			}
			return true;
		}
	});
}

module.exports.Live555Client = Live555Client;
<commit_msg>add function mkdirs<commit_after>'use strict';
const spawn = require('child_process').spawn, EventEmitter = require('events').EventEmitter, util = require('util');
var os = require('os');
var childProcess = require('child_process');
var path = require('path');
var fs = require('fs');
/**
 * Stream constructor
 * @param {object} options
 * @param {string} options.input Stream uri, for example rtsp://r3---sn-5hn7su76.c.youtube.com/CiILENy73wIaGQnup-1SztVOYBMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp
 * @constructor
 */
var Live555Client = function (options) {
	var _testbundle = childProcess.exec(getCmd(), function (error, stdout, stderr) {
		if (error) {
			//console.log('error on test bundle:', error);
			//build
			console.log('build project,please wait...');
			var _configurepath = path.join(__dirname, 'extras', 'src', 'configure');
			//__dirname + '/extras/src/configure';
			var _startbuild = childProcess.exec(_configurepath, function (error, stdout, stderr) {
				if (error) {
					console.log('build failed:', error);
				} else {
					mkdirs(getDir(), function (err) {
						if (err) {
							console.log('build failed:', err);
						} else {
							var _buildpath = path.join(__dirname, 'extras', 'src', 'live555client');
							fs.rename(_buildpath, getCmd(), function (err) {
								if (err) {

									console.log('build failed:', err);
								} else {
									console.log('build success');
								}
							});
						}
					});
				}
			});
		}
	});
	if (options.input) {
		this.input = options.input;
	} else {
		throw new Error('no `input` parameter');
	}
	this.arguments = [];

	this.on('newListener', newListener.bind(this));
	this.on('removeListener', removeListener.bind(this));
	if (Object.observe && (typeof Proxy !== 'function')) {
		Object.observe(this, observer.bind(this));
	}
};

util.inherits(Live555Client, EventEmitter);

/**
 * Live555Client command name
 * @type {string}
 */
//Live555Client.cmd = __dirname + '/extras/Live555Client';

function newListener(event) {
	if (event === 'data' && this.listeners(event).length === 0) {
		this.start();
	}
}

function removeListener(event) {
	if (event === 'data' && this.listeners(event).length === 0) {
		this.stop();
	}
}

function installBundle(callback) {
	var _testchild = spawn(getCmd(), []);
	_testchild.on('error', function (err) {
		if (err.code == 'ENOENT') {

			//make start for linux
			//var _dir = __dirname + '/extras/src/';
			//var _buildscript = ''
			callback(false);
		}
	});
	_testchild.on('close', function (code) {
		callback(true);
	});
};

function getCmd() {
	var _clientname = 'live555client';
	var _platformarch = os.arch();
	var _platform = os.platform();
	return path.join(__dirname, 'extras', _platform, _platformarch, _clientname);
	//__dirname + '/extras/' + platform + '/' + _clientname;
}

function getDir() {
	var _platformarch = os.arch();
	var _platform = os.platform();
	return path.join(__dirname, 'extras', _platform, _platformarch);
	//__dirname + '/extras/' + platform + '/' + _clientname;
}

function mkdirs(dirname, callback) {
	fs.exists(dirname, function (exists) {
		if (exists) {
			callback();
		} else {
			console.log(path.dirname(dirname));
			mkdirs(path.dirname(dirname), function () {
				fs.mkdir(dirname, callback);
			});
		}
	});
}

function observer(changes) {
	if (changes.some(function (change) {
		return change.type === 'update';
	})) {
		this.restart();
	}
}

Live555Client.prototype._args = function () {
	return this.arguments.concat([
		this.input
	]);
};

/**
 * Start Live555Client spawn process
 */
Live555Client.prototype.start = function () {
	var _client = this;
	this.child = spawn(getCmd(), this._args());
	this.child.stdout.on('data', this.emit.bind(this, 'data'));
	this.child.stderr.on('data', function (data) {
		throw new Error(data);
	});
	this.emit('start');

	this.child.on('error', function (err) {
		if (err.code == 'ENOENT') {
			console.log('You have not build Live555Client yet,trying to build it');
			_client.stop();
		}
	});
	this.child.on('close', function (code) {
		if (code === 0) {
			setTimeout(Live555Client.prototype.start.bind(this), 1000);
		}
	}.bind(this));
	/*this.child.on('error', function(code) {
	});*/
};

/**
 * Stop Live555Client spawn process
 */
Live555Client.prototype.stop = function () {
	this.child.kill();
	delete this.child;
	this.emit('stop');
};

/**
 * Restart Live555Client spawn process
 */
Live555Client.prototype.restart = function () {
	if (this.child) {
		this.stop();
		this.start();
	}
};

if (typeof Proxy === 'function') {
	Live555Client = new Proxy(Live555Client, {
		set: function (target, property) {
			if (property !== 'super_' && target[property] !== undefined) {
				target.restart();
			}
			return true;
		}
	});
}

module.exports.Live555Client = Live555Client;
<|endoftext|>


<commit_before>export default function BuildAnchor($log, $filter) {
     // Returns a full resource_name HTML string if link can be derived from supplied context
     // returns name of resource if activity stream object doesn't contain enough data to build a UI url
     // arguments are: a summary_field object, a resource type, an activity stream object
     return function (obj, resource, activity) {
         var url = '/#/';
         // try/except pattern asserts that:
         // if we encounter a case where a UI url can't or shouldn't be generated, just supply the name of the resource
         try {
             // catch-all case to avoid generating urls if a resource has been deleted
             // if a resource still exists, it'll be serialized in the activity's summary_fields
             if (!activity.summary_fields[resource]){
                 throw {name : 'ResourceDeleted', message: 'The referenced resource no longer exists'};
             }
             switch (resource) {
                 case 'custom_inventory_script':
                     url += 'inventory_scripts/' + obj.id + '/';
                     break;
                 case 'group':
                     if (activity.operation === 'create' || activity.operation === 'delete'){
                         // the API formats the changes.inventory field as str 'myInventoryName-PrimaryKey'
                         var inventory_id = _.last(activity.changes.inventory.split('-'));
                         url += 'inventories/inventory/' + inventory_id + '/groups/edit/' + activity.changes.id;
                     }
                     else {
                         url += 'inventories/inventory/' + activity.summary_fields.inventory[0].id + '/groups/edit/' + (activity.changes.id || activity.changes.object1_pk);
                     }
                     break;
                 case 'host':
                     url += 'hosts/' + obj.id;
                     break;
                 case 'job':
                     url += 'jobs/' + obj.id;
                     break;
                 case 'inventory':
                     url += obj.kind && obj.kind === "smart" ? 'inventories/smart/' + obj.id + '/' : 'inventories/inventory/' + obj.id + '/';
                     break;
                 case 'schedule':
                     // schedule urls depend on the resource they're associated with
                     if (activity.summary_fields.job_template){
                         url += 'templates/job_template/' + activity.summary_fields.job_template.id + '/schedules/' + obj.id;
                     }
                     else if (activity.summary_fields.project){
                         url += 'projects/' + activity.summary_fields.project.id + '/schedules/' + obj.id;
                     }
                     else if (activity.summary_fields.system_job_template){
                         url += 'management_jobs/' + activity.summary_fields.system_job_template.id + '/schedules/edit/' + obj.id;
                     }
                     // urls for inventory sync schedules currently depend on having an inventory id and group id
                     else {
                         throw {name : 'NotImplementedError', message : 'activity.summary_fields to build this url not implemented yet'};
                     }
                     break;
                case 'setting':
                    if (activity.summary_fields.setting[0].category === 'jobs' ||
                        activity.summary_fields.setting[0].category === 'ui') {
                        url += `configuration/${activity.summary_fields.setting[0].category}`;
                    }
                    else if (activity.summary_fields.setting[0].category === 'system' ||
                        activity.summary_fields.setting[0].category === 'logging') {
                        url += `configuration/system`;
                    }
                    else {
                        url += `configuration/auth`;
                    }
                     break;
                 case 'notification_template':
                     url += `notification_templates/${obj.id}`;
                     break;
                 case 'role':
                     throw {name : 'NotImplementedError', message : 'role object management is not consolidated to a single UI view'};
                 case 'job_template':
                     url += `templates/job_template/${obj.id}`;
                     break;
                 case 'workflow_job_template':
                     url += `templates/workflow_job_template/${obj.id}`;
                     break;
                 case 'workflow_job':
                     url += `workflows/${obj.id}`;
                     break;
                 case 'label':
                     url = null;
                     break;
                 case 'inventory_source':
                     const inventoryId = _.get(obj, 'inventory', '').split('-').reverse()[0];
                     url += `inventories/inventory/${inventoryId}/inventory_sources/edit/${obj.id}`;
                     break;
                 default:
                     url += resource + 's/' + obj.id + '/';
             }

             const name = $filter('sanitize')(obj.name || obj.username);

             if (url) {
                return ` <a href=\"${url}\"> ${name} </a> `;
             }

             return ` <span> ${name} </span> `;
         }
         catch(err){
             $log.debug(err);
             return ' ' + $filter('sanitize')(obj.name || obj.username || '') + ' ';
         }
     };
}

 BuildAnchor.$inject = ['$log', '$filter'];
<commit_msg>fixed build anchor for application in activity stream<commit_after>export default function BuildAnchor($log, $filter) {
     // Returns a full resource_name HTML string if link can be derived from supplied context
     // returns name of resource if activity stream object doesn't contain enough data to build a UI url
     // arguments are: a summary_field object, a resource type, an activity stream object
     return function (obj, resource, activity) {
         var url = '/#/';
         // try/except pattern asserts that:
         // if we encounter a case where a UI url can't or shouldn't be generated, just supply the name of the resource
         try {
             // catch-all case to avoid generating urls if a resource has been deleted
             // if a resource still exists, it'll be serialized in the activity's summary_fields
             if (!activity.summary_fields[resource]){
                 throw {name : 'ResourceDeleted', message: 'The referenced resource no longer exists'};
             }
             switch (resource) {
                 case 'custom_inventory_script':
                     url += 'inventory_scripts/' + obj.id + '/';
                     break;
                 case 'group':
                     if (activity.operation === 'create' || activity.operation === 'delete'){
                         // the API formats the changes.inventory field as str 'myInventoryName-PrimaryKey'
                         var inventory_id = _.last(activity.changes.inventory.split('-'));
                         url += 'inventories/inventory/' + inventory_id + '/groups/edit/' + activity.changes.id;
                     }
                     else {
                         url += 'inventories/inventory/' + activity.summary_fields.inventory[0].id + '/groups/edit/' + (activity.changes.id || activity.changes.object1_pk);
                     }
                     break;
                 case 'host':
                     url += 'hosts/' + obj.id;
                     break;
                 case 'job':
                     url += 'jobs/' + obj.id;
                     break;
                 case 'inventory':
                     url += obj.kind && obj.kind === "smart" ? 'inventories/smart/' + obj.id + '/' : 'inventories/inventory/' + obj.id + '/';
                     break;
                 case 'schedule':
                     // schedule urls depend on the resource they're associated with
                     if (activity.summary_fields.job_template){
                         url += 'templates/job_template/' + activity.summary_fields.job_template.id + '/schedules/' + obj.id;
                     }
                     else if (activity.summary_fields.project){
                         url += 'projects/' + activity.summary_fields.project.id + '/schedules/' + obj.id;
                     }
                     else if (activity.summary_fields.system_job_template){
                         url += 'management_jobs/' + activity.summary_fields.system_job_template.id + '/schedules/edit/' + obj.id;
                     }
                     // urls for inventory sync schedules currently depend on having an inventory id and group id
                     else {
                         throw {name : 'NotImplementedError', message : 'activity.summary_fields to build this url not implemented yet'};
                     }
                     break;
                case 'setting':
                    if (activity.summary_fields.setting[0].category === 'jobs' ||
                        activity.summary_fields.setting[0].category === 'ui') {
                        url += `configuration/${activity.summary_fields.setting[0].category}`;
                    }
                    else if (activity.summary_fields.setting[0].category === 'system' ||
                        activity.summary_fields.setting[0].category === 'logging') {
                        url += `configuration/system`;
                    }
                    else {
                        url += `configuration/auth`;
                    }
                     break;
                 case 'notification_template':
                     url += `notification_templates/${obj.id}`;
                     break;
                 case 'role':
                     throw {name : 'NotImplementedError', message : 'role object management is not consolidated to a single UI view'};
                 case 'job_template':
                     url += `templates/job_template/${obj.id}`;
                     break;
                 case 'workflow_job_template':
                     url += `templates/workflow_job_template/${obj.id}`;
                     break;
                 case 'workflow_job':
                     url += `workflows/${obj.id}`;
                     break;
                 case 'label':
                     url = null;
                     break;
                 case 'inventory_source':
                     const inventoryId = _.get(obj, 'inventory', '').split('-').reverse()[0];
                     url += `inventories/inventory/${inventoryId}/inventory_sources/edit/${obj.id}`;
                     break;
                 case 'o_auth2_application':
                     url += `applications/${obj.id}`;
                     break;
                 default:
                     url += resource + 's/' + obj.id + '/';
             }

             const name = $filter('sanitize')(obj.name || obj.username);

             if (url) {
                return ` <a href=\"${url}\"> ${name} </a> `;
             }

             return ` <span> ${name} </span> `;
         }
         catch(err){
             $log.debug(err);
             return ' ' + $filter('sanitize')(obj.name || obj.username || '') + ' ';
         }
     };
}

 BuildAnchor.$inject = ['$log', '$filter'];
<|endoftext|>


<commit_before>var tmpFile = require("../tmp-file");
var test = require("buster-test");
var path = require("path");
var when = require("when");
var fs = require("fs");
var _ = require("lodash");

// Error codes, as per FreeBSD's sysexit(3)
// Errors are mapped to sysexit(3) error codes wherever that makes sense
var EX_DATAERR = 65;
var EX_SOFTWARE = 70;

function writeManifest(fileName, manifests) {
    var manifest = _.extend.apply(_, manifests);
    fs.writeFileSync(fileName, JSON.stringify(manifest), "utf-8");
}

function cacheFile(config) {
    return tmpFile(path.join(config.rootPath || "", "buster-cache"));
}

function descriptiveRequire(file) {
    try {
        require(file);
    } catch (e) {
        var relative = file.replace(process.cwd(), ".");
        e.message = "Failed requiring " + relative + ": " + e.message;
        throw e;
    }
}

function readManifest(fileName, callback) {
    fs.readFile(fileName, "utf-8", function (err, contents) {
        try {
            callback(JSON.parse(contents));
        } catch (e) {
            callback({});
        }
    });
}

function processSection(config, section) {
    var d = when.defer();
    config.on("load:" + section, function (resourceSet) {
        readManifest(cacheFile(config), function (manifest) {
            var pd = resourceSet.process(manifest);
            when.chain(pd, d.resolver);
        });
    });
    return d.promise;
}

function manifestsGenerated(manifests) {
    if (!this.processDeferred) { return; }
    this.processDeferred.resolve(manifests);
    delete this.processDeferred;
}

function manifestsFailed(err) {
    if (!this.processDeferred) { return; }
    this.processDeferred.reject(err);
    delete this.processDeferred;
}

var testRun = {
    create: function (config, options, logger, done) {
        return _.extend(Object.create(this), {
            config: config,
            options: options,
            logger: logger,
            callback: done
        });
    },

    configure: function () {
        // if (this.options.captureConsole &&
        //     typeof B.captureConsole === "function") {
        //     B.captureConsole();
        // }
    },

    start: function () {
        this.configure();
        try {
            var hookResolution = this.beforeRunHook();
            var configResolution = this.config.resolve();

            var errorFunc = (function (err) {
                this.done(err);
            }.bind(this));
            configResolution.otherwise(errorFunc);

            when.all([hookResolution, configResolution]).
                then(function (values) {
                    this.runTests(values[1], values[0]);
                }.bind(this));
        } catch (e) {
            this.done(e);
        }
    },

    runTests: function (rs, manifests) {
        if (this.aborted || rs.loadPath.paths().length === 0) {
            return this.done();
        }
        try {
            writeManifest(cacheFile(this.config), manifests);
            var runner = test.autoRun(this.options, {
                start:
                    this.config.runExtensionHook.bind(this.config, "testRun"),
                end: function () {
                    return this.done.apply(this, arguments);
                }.bind(this, null)
            });
            test.testContext.on("create", runner);
            rs.loadPath.paths().map(function (p) {
                return path.join(rs.rootPath, p);
            }).forEach(descriptiveRequire);
        } catch (e) {
            e.code = EX_DATAERR;
            this.done(e);
        }
    },

    done: function (err) {
        if (!this.callback) { return; }
        if (err) {
            err.code = err.code || EX_SOFTWARE;
        }
        this.callback.apply(this, arguments);
        delete this.callback;
    },

    abort: function (err) {
        this.aborted = true;
        delete this.processDeferred;
        this.done(err);
    },

    beforeRunHook: function () {
        this.config.runExtensionHook("beforeRun");
        this.processDeferred = when.defer();
        var sections = ["libs", "sources", "testHelpers", "tests"];
        when.all(sections.map(processSection.bind(null, this.config))).then(
            manifestsGenerated.bind(this),
            manifestsFailed.bind(this)
        );
        return this.processDeferred.promise;
    }
};

module.exports = {
    testRun: testRun,

    create: function (options) {
        return Object.create(this);
    },

    run: function (config, options, done) {
        var run = testRun.create(config, options, this.logger, done);
        run.start();
        return run;
    }
};
<commit_msg>Amendment for fix for issue 376<commit_after>var tmpFile = require("../tmp-file");
var test = require("buster-test");
var path = require("path");
var when = require("when");
var fs = require("fs");
var _ = require("lodash");

// Error codes, as per FreeBSD's sysexit(3)
// Errors are mapped to sysexit(3) error codes wherever that makes sense
var EX_DATAERR = 65;
var EX_SOFTWARE = 70;

function writeManifest(fileName, manifests) {
    var manifest = _.extend.apply(_, manifests);
    fs.writeFileSync(fileName, JSON.stringify(manifest), "utf-8");
}

function cacheFile(config) {
    return tmpFile(path.join(config.rootPath || "", "buster-cache"));
}

function descriptiveRequire(file) {
    try {
        require(file);
    } catch (e) {
        var relative = file.replace(process.cwd(), ".");
        e.message = "Failed requiring " + relative + ": " + e.message;
        throw e;
    }
}

function readManifest(fileName, callback) {
    fs.readFile(fileName, "utf-8", function (err, contents) {
        try {
            callback(JSON.parse(contents));
        } catch (e) {
            callback({});
        }
    });
}

function processSection(config, section) {
    var d = when.defer();
    config.on("load:" + section, function (resourceSet) {
        readManifest(cacheFile(config), function (manifest) {
            var pd = resourceSet.process(manifest);
            when.chain(pd, d.resolver);
        });
    });
    return d.promise;
}

function manifestsGenerated(manifests) {
    if (!this.processDeferred) { return; }
    this.processDeferred.resolve(manifests);
    delete this.processDeferred;
}

function manifestsFailed(err) {
    if (!this.processDeferred) { return; }
    this.processDeferred.reject(err);
    delete this.processDeferred;
}

var testRun = {
    create: function (config, options, logger, done) {
        return _.extend(Object.create(this), {
            config: config,
            options: options,
            logger: logger,
            callback: done
        });
    },

    configure: function () {
        // if (this.options.captureConsole &&
        //     typeof B.captureConsole === "function") {
        //     B.captureConsole();
        // }
    },

    start: function () {
        this.configure();
        try {
            var hookResolution = this.beforeRunHook();
            var configResolution = this.config.resolve();

            var errorFunc = (function (err) {
                this.done(err);
            }.bind(this));
            configResolution.otherwise(errorFunc);

            when.all([hookResolution, configResolution]).
                then(function (values) {
                    this.runTests(values[1], values[0]);
                }.bind(this));
        } catch (e) {
            this.done(e);
        }
    },

    runTests: function (rs, manifests) {
        if (this.aborted) { return; }
        if (rs.loadPath.paths().length === 0) { return this.done(); }
        try {
            writeManifest(cacheFile(this.config), manifests);
            var runner = test.autoRun(this.options, {
                start:
                    this.config.runExtensionHook.bind(this.config, "testRun"),
                end: function () {
                    return this.done.apply(this, arguments);
                }.bind(this, null)
            });
            test.testContext.on("create", runner);
            rs.loadPath.paths().map(function (p) {
                return path.join(rs.rootPath, p);
            }).forEach(descriptiveRequire);
        } catch (e) {
            e.code = EX_DATAERR;
            this.done(e);
        }
    },

    done: function (err) {
        if (!this.callback) { return; }
        if (err) {
            err.code = err.code || EX_SOFTWARE;
        }
        this.callback.apply(this, arguments);
        delete this.callback;
    },

    abort: function (err) {
        this.aborted = true;
        delete this.processDeferred;
        this.done(err);
    },

    beforeRunHook: function () {
        this.config.runExtensionHook("beforeRun");
        this.processDeferred = when.defer();
        var sections = ["libs", "sources", "testHelpers", "tests"];
        when.all(sections.map(processSection.bind(null, this.config))).then(
            manifestsGenerated.bind(this),
            manifestsFailed.bind(this)
        );
        return this.processDeferred.promise;
    }
};

module.exports = {
    testRun: testRun,

    create: function (options) {
        return Object.create(this);
    },

    run: function (config, options, done) {
        var run = testRun.create(config, options, this.logger, done);
        run.start();
        return run;
    }
};
<|endoftext|>


<commit_before>var session = require('./session');
var rest = require('./rest');
var useragent = require('./useragent');
var livedb = require('livedb');

if (!require('semver').gte(process.versions.node, '0.10.0')) {
  throw new Error('ShareJS requires node 0.10 or above.');
}

/** This encapsulates the sharejs server state & exposes a few useful methods.
 *
 * @constructor
 */
var ShareInstance = function(options) {
  this.options = options;

  this.preValidate = options.preValidate;
  this.validate = options.validate;

  if (options.backend) {
    this.backend = options.backend;
  } else if (options.db) {
    this.backend = livedb.client(options.db);
  } else {
    throw Error("Both options.backend and options.db are missing. Can't function without a database!");
  }

  if (!this.backend.bulkSubscribe)
    throw Error("You're using an old version of livedb. Please update livedb or downgrade ShareJS");

  // Map from event name (or '') to a list of middleware.
  this.extensions = {'':[]};
  this.docFilters = [];
  this.opFilters = [];
};

/** A client has connected through the specified stream. Listen for messages.
 * Returns the useragent associated with the connected session.
 *
 * The optional second argument (req) is an initial request which is passed
 * through to any connect() middleware. This is useful for inspecting cookies
 * or an express session or whatever on the request object in your middleware.
 *
 * (The useragent is available through all middleware)
 */
ShareInstance.prototype.listen = function(stream, req) {
  return session(this, stream, req).agent;
};

// Create and return REST middleware to access the documents
ShareInstance.prototype.rest = function() {
  return rest(this);
};

/** Add middleware to an action. The action is optional (if not specified, the
 * middleware fires on every action).
 */
ShareInstance.prototype.use = function(action, middleware) {
  if (typeof action !== 'string') {
    middleware = action;
    action = '';
  }

  if (action === 'getOps') {
    throw new Error("The 'getOps' middleware action has been renamed to 'get ops'. Update your code.");
  }

  var extensions = this.extensions[action];
  if (!extensions) extensions = this.extensions[action] = [];

  extensions.push(middleware);
};

/** Add a function to filter all data going to the current client */
ShareInstance.prototype.filter = function(fn) {
  this.docFilters.push(fn);
};

ShareInstance.prototype.filterOps = function(fn) {
  this.opFilters.push(fn);
};

ShareInstance.prototype.createAgent = function(stream) {
  return useragent(this, stream);
};

// Return truthy if the instance has registered middleware. Used for bulkSubscribe.
ShareInstance.prototype._hasMiddleware = function(action) {
  return this.extensions[action];
};

/**
 * Passes request through the extensions stack
 *
 * Extensions may modify the request object. After all middlewares have been
 * invoked we call `callback` with `null` and the modified request.
 * If one of the extensions resturns an error the callback is called with that
 * error.
 */
ShareInstance.prototype._trigger = function(request, callback) {
  // Copying the triggers we'll fire so they don't get edited while we iterate.
  var middlewares = (this.extensions[request.action] || []).concat(this.extensions['']);

  var next = function() {
    if (!middlewares.length)
      return callback ? callback(null, request) : undefined;

    var middleware = middlewares.shift();
    middleware(request, function(err) {
      if (err) return callback ? callback(err) : undefined;

      next();
    });
  };

  next();
};

exports.createClient = function(options) {
  return new ShareInstance(options);
};

<commit_msg>livedb/redisclient initialization<commit_after>var session = require('./session');
var rest = require('./rest');
var useragent = require('./useragent');
var livedb = require('livedb');

if (!require('semver').gte(process.versions.node, '0.10.0')) {
  throw new Error('ShareJS requires node 0.10 or above.');
}

/** This encapsulates the sharejs server state & exposes a few useful methods.
 *
 * @constructor
 */
var ShareInstance = function(options) {
  this.options = options;

  this.preValidate = options.preValidate;
  this.validate = options.validate;

  if (options.backend) {
    this.backend = options.backend;
  } else if (options.db) {
    // if we have .db, .redis1, .redis2 then create a redisclient for livedb
    if (options.redis1 && options.redis2) options.driver = livedb.redisDriver(options.db, options.redis1, options.redis2);

    this.backend = livedb.client(options);
  } else {
    throw Error("Both options.backend and options.db are missing. Can't function without a database!");
  }

  if (!this.backend.bulkSubscribe)
    throw Error("You're using an old version of livedb. Please update livedb or downgrade ShareJS");

  // Map from event name (or '') to a list of middleware.
  this.extensions = {'':[]};
  this.docFilters = [];
  this.opFilters = [];
};

/** A client has connected through the specified stream. Listen for messages.
 * Returns the useragent associated with the connected session.
 *
 * The optional second argument (req) is an initial request which is passed
 * through to any connect() middleware. This is useful for inspecting cookies
 * or an express session or whatever on the request object in your middleware.
 *
 * (The useragent is available through all middleware)
 */
ShareInstance.prototype.listen = function(stream, req) {
  return session(this, stream, req).agent;
};

// Create and return REST middleware to access the documents
ShareInstance.prototype.rest = function() {
  return rest(this);
};

/** Add middleware to an action. The action is optional (if not specified, the
 * middleware fires on every action).
 */
ShareInstance.prototype.use = function(action, middleware) {
  if (typeof action !== 'string') {
    middleware = action;
    action = '';
  }

  if (action === 'getOps') {
    throw new Error("The 'getOps' middleware action has been renamed to 'get ops'. Update your code.");
  }

  var extensions = this.extensions[action];
  if (!extensions) extensions = this.extensions[action] = [];

  extensions.push(middleware);
};

/** Add a function to filter all data going to the current client */
ShareInstance.prototype.filter = function(fn) {
  this.docFilters.push(fn);
};

ShareInstance.prototype.filterOps = function(fn) {
  this.opFilters.push(fn);
};

ShareInstance.prototype.createAgent = function(stream) {
  return useragent(this, stream);
};

// Return truthy if the instance has registered middleware. Used for bulkSubscribe.
ShareInstance.prototype._hasMiddleware = function(action) {
  return this.extensions[action];
};

/**
 * Passes request through the extensions stack
 *
 * Extensions may modify the request object. After all middlewares have been
 * invoked we call `callback` with `null` and the modified request.
 * If one of the extensions resturns an error the callback is called with that
 * error.
 */
ShareInstance.prototype._trigger = function(request, callback) {
  // Copying the triggers we'll fire so they don't get edited while we iterate.
  var middlewares = (this.extensions[request.action] || []).concat(this.extensions['']);

  var next = function() {
    if (!middlewares.length)
      return callback ? callback(null, request) : undefined;

    var middleware = middlewares.shift();
    middleware(request, function(err) {
      if (err) return callback ? callback(err) : undefined;

      next();
    });
  };

  next();
};

exports.createClient = function(options) {
  return new ShareInstance(options);
};

<|endoftext|>


<commit_before>import React from 'react';
import _ from 'lodash';
import Webiny from 'webiny';

class DownloadLink extends Webiny.Ui.Component {

    constructor(props) {
        super(props);

        this.dialog = null;
        this.state = {
            showDialog: false
        };

        this.bindMethods('getDialog');
    }

    componentWillReceiveProps(props) {
        super.componentWillReceiveProps(props);
        if (this.dialog) {
            this.getDialog();
        }
    }

    getDialog() {
        const result = this.props.download(this.downloader.download, this.props.data || null);
        // TODO: add check if the returned result is actually a Dialog component
        // At this point we do not want to import Modal component to perform the check so we assume it is a Dialog if it is not null
        if (result) {
            this.dialog = result;
        }
    }
}

DownloadLink.defaultProps = {
    download: null,
    method: 'GET',
    renderer() {
        const {Downloader, Link, ...props} = this.props;
        const downloader = <Downloader ref={downloader => this.downloader = downloader}/>;
        props.onClick = () => {
            if (_.isString(this.props.download)) {
                this.downloader.download(this.props.method, this.props.download);
            } else {
                this.getDialog();
                this.setState({showDialog: true});
            }
        };
        delete props['download'];

        let dialog = null;
        if (this.dialog) {
            dialog = React.cloneElement(this.dialog, {
                onHidden: () => {
                    this.dialog = null;
                    this.setState({showDialog: false});
                },
                onComponentDidMount: dialog => {
                    if (this.state.showDialog && !dialog.isShown()) {
                        dialog.show();
                    }
                }
            });
        }

        return (
            <Link {..._.omit(props, ['renderer'])}>
                {this.props.children}
                {downloader}
                {dialog}
            </Link>
        );
    }
};

export default Webiny.createComponent(DownloadLink, {modules: ['Downloader', 'Link']});<commit_msg>Download link can accept data to be sent withing a POST/GET request.<commit_after>import React from 'react';
import _ from 'lodash';
import Webiny from 'webiny';

class DownloadLink extends Webiny.Ui.Component {

    constructor(props) {
        super(props);

        this.dialog = null;
        this.state = {
            showDialog: false
        };

        this.bindMethods('getDialog');
    }

    componentWillReceiveProps(props) {
        super.componentWillReceiveProps(props);
        if (this.dialog) {
            this.getDialog();
        }
    }

    getDialog() {
        const result = this.props.download(this.downloader.download, this.props.data || null);
        // TODO: add check if the returned result is actually a Dialog component
        // At this point we do not want to import Modal component to perform the check so we assume it is a Dialog if it is not null
        if (result) {
            this.dialog = result;
        }
    }
}

DownloadLink.defaultProps = {
    download: null,
    method: 'GET',
    data: null,
    renderer() {
        const {Downloader, Link, ...props} = this.props;
        const downloader = <Downloader ref={downloader => this.downloader = downloader}/>;
        props.onClick = () => {
            if (_.isString(this.props.download)) {
                this.downloader.download(this.props.method, this.props.download, this.props.data);
            } else {
                this.getDialog();
                this.setState({showDialog: true});
            }
        };
        delete props['download'];

        let dialog = null;
        if (this.dialog) {
            dialog = React.cloneElement(this.dialog, {
                onHidden: () => {
                    this.dialog = null;
                    this.setState({showDialog: false});
                },
                onComponentDidMount: dialog => {
                    if (this.state.showDialog && !dialog.isShown()) {
                        dialog.show();
                    }
                }
            });
        }

        return (
            <Link {..._.omit(props, ['renderer'])}>
                {this.props.children}
                {downloader}
                {dialog}
            </Link>
        );
    }
};

export default Webiny.createComponent(DownloadLink, {modules: ['Downloader', 'Link']});<|endoftext|>