StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
StackGenVis/frontend/node_modules/en-route/lib/index.js

579 lines
12 KiB

4 years ago
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('router');
var utils = require('./utils');
var Route = require('./route');
var Layer = require('./layer');
/**
* Initialize a new `Router` with the given `options`.
*
* @name Router
* @param {Object} options
* @return {Router} which is an callable function
* @api public
*/
var proto = module.exports = function proto(options) {
options = options || {};
function router(file, next) {
router.handle(file, next);
}
// mixin Router class functions
router.__proto__ = proto;
router.params = {};
router._params = [];
router.caseSensitive = options.caseSensitive;
router.mergeParams = options.mergeParams;
router.strict = options.strict;
router.stack = [];
router.methods = utils.arrayify(options.methods);
decorate(router, router.methods);
return router;
};
/**
* Map the given param placeholder `name`(s) to the given callback.
*
* Parameter mapping is used to provide pre-conditions to routes
* which use normalized placeholders. For example a `:user_id` parameter
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
*
* **Example**
*
* ```js
* app.param('user_id', function(file, next, id) {
* User.find(id, function(err, user) {
* if (err) {
* return next(err);
* } else if (!user) {
* return next(new Error('failed to load user'));
* }
* file.user = user;
* next();
* });
* });
* ```
*
* @param {String} `name`
* @param {Function} `fn`
* @return {Router} `Object` for chaining
* @api public
*/
proto.param = function(name, fn) {
// param logic
if (typeof name === 'function') {
this._params.push(name);
return;
}
// apply param functions
var params = this._params;
var len = params.length;
var ret;
if (name[0] === ':') {
name = name.substr(1);
}
for (var i = 0; i < len; ++i) {
if ((ret = params[i](name, fn))) {
fn = ret;
}
}
// ensure we end up with a middleware function
if (typeof fn !== 'function') {
throw new Error('invalid param() call for ' + name + ', got ' + fn);
}
(this.params[name] = this.params[name] || []).push(fn);
return this;
};
/**
* Dispatch a file into the router.
*
* @api private
*/
proto.handle = function(file, done) {
var self = this;
file.options = file.options || {};
debug('dispatching %s', file.path);
var idx = 0;
var removed = '';
var slashAdded = false;
var paramcalled = {};
// middleware and routes
var stack = self.stack;
// manage inter-router variables
var parentParams = file.options.params;
var parentPath = file.options.basePath || '';
done = restore(done, file.options, 'basePath', 'next', 'params');
// setup next layer
proto.defineOption(file, 'next', next);
// setup basic req values
proto.defineOption(file, 'basePath', parentPath);
proto.defineOption(file, 'originalPath', file.options.originalPath || file.path);
next();
function next(err) {
var layerError = err === 'route'
? null
: err;
var layer = stack[idx++];
if (slashAdded) {
file.path = file.path.substr(1);
slashAdded = false;
}
if (removed.length !== 0) {
file.options.basePath = parentPath;
file.path = removed + file.path;
removed = '';
}
if (!layer) {
return done(layerError);
}
self.matchLayer(layer, file, function(err, path) {
if (err || path === undefined) {
return next(layerError || err);
}
// route object and not middleware
var route = layer.route;
// if final route, then we support options
if (route) {
// we don't run any routes with error first
if (layerError) {
return next(layerError);
}
var method = file.options.method;
var hasMethod = route.handlesMethod(method);
if (!hasMethod) {
return next();
}
// we can now dispatch to the route
file.options.route = route;
}
// Capture one-time layer values
file.options.params = self.mergeParams
? mergeParams(layer.params, parentParams)
: layer.params;
var layerPath = layer.path;
// this should be done for the layer
self.processParams(layer, paramcalled, file, function(err) {
if (err) {
return next(layerError || err);
}
if (route) {
return layer.handleFile(file, next);
}
trimPrefix(layer, layerError, layerPath, path);
});
});
}
function trimPrefix(layer, layerError, layerPath, path) {
// Trim off the part of the path that matches the route
// middleware (.use stuff) needs to have the path stripped
if (layerPath.length !== 0) {
debug('trim prefix (%s) from path %s', layerPath, file.path);
removed = layerPath;
file.path = file.path.substr(removed.length);
// Ensure leading slash
if (file.path[0] !== '/') {
file.path = '/' + file.path;
slashAdded = true;
}
var rlen = removed.length;
var removedPath = removed[rlen - 1] === '/'
? removed.substring(0, rlen - 1)
: removed;
// Setup base path (no trailing slash)
file.options.basePath = parentPath + removedPath;
}
debug('%s %s : %s', layer.name, layerPath, file.options.originalPath);
if (layerError) {
layer.handleError(layerError, file, next);
} else {
layer.handleFile(file, next);
}
}
};
/**
* Match file to a layer.
*
* @api private
*/
proto.matchLayer = function matchLayer(layer, file, done) {
var error = null;
var path;
try {
path = file.path;
if (!layer.match(path)) {
path = undefined;
}
} catch (err) {
error = err;
}
done(error, path);
};
/**
* Process any parameters for the layer.
*
* @api private
*/
proto.processParams = function(layer, called, file, done) {
var params = this.params;
// captured parameters from the layer, keys and values
var keys = layer.keys;
// fast track
if (!keys || keys.length === 0) {
return done();
}
var i = 0;
var name;
var paramIndex = 0;
var key;
var paramVal;
var paramCallbacks;
var paramCalled;
// process params in order
// param callbacks can be async
function param(err) {
if (err) {
return done(err);
}
if (i >= keys.length) {
return done();
}
paramIndex = 0;
key = keys[i++];
if (!key) {
return done();
}
name = key.name;
paramVal = file.options.params[name];
paramCallbacks = params[name];
paramCalled = called[name];
if (paramVal === undefined || !paramCallbacks) {
return param();
}
// param previously called with same value or error occurred
if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
// restore value
file.options.params[name] = paramCalled.value;
// next param
return param(paramCalled.error);
}
called[name] = paramCalled = {
error: null,
match: paramVal,
value: paramVal
};
paramCallback();
}
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
// store updated value
paramCalled.value = file.options.params[key.name];
if (err) {
// store error
paramCalled.error = err;
param(err);
return;
}
if (!fn) return param();
try {
fn(file, paramCallback, paramVal, key.name);
} catch (e) {
paramCallback(e);
}
}
param();
};
/**
* Use the given middleware function, with optional path, defaulting to `/`.
*
* The other difference is that _route_ path is stripped and not visible
* to the handler function. The main effect of this feature is that mounted
* handlers can operate without any code changes regardless of the `prefix`
* pathname.
*
* **Example**
*
* ```js
* var router = new Router();
*
* router.use(function(file, next) {
* false.should.be.true;
* next();
* });
* ```
*
* @param {Function} `fn`
* @api public
*/
proto.use = function use(fn) {
var offset = 0;
var path = '/';
// default path to '/', disambiguate router.use([fn])
if (typeof fn !== 'function') {
var arg = fn;
while (Array.isArray(arg) && arg.length !== 0) {
arg = arg[0];
}
// first arg is the path
if (typeof arg !== 'function') {
offset = 1;
path = fn;
}
}
var callbacks = utils.flatten([].slice.call(arguments, offset));
var len = callbacks.length, i = 0;
if (len === 0) {
throw new TypeError('expected middleware functions to be defined');
}
while (len--) {
var cb = callbacks[i++];
if (typeof cb !== 'function') {
throw new TypeError('expected callback to be a function, but received: ' + utils.typeOf(cb));
}
// add the middleware
debug('use %s %s', path, cb.name || '<unknown>');
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: false,
end: false
}, cb);
layer.route = undefined;
this.stack.push(layer);
}
return this;
};
/**
* Create a new Route for the given path. Each route contains a
* separate middleware stack.
*
* See the Route api documentation for details on adding handlers
* and middleware to routes.
*
* @param {String} `path`
* @return {Object} `Route` for chaining
* @api public
*/
proto.route = function(path) {
var route = new Route(path, this.methods);
var opts = { sensitive: this.caseSensitive, strict: this.strict, end: true };
var layer = new Layer(path, opts, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
};
/**
* Add additional methods to the current router instance.
*
* ```
* var router = new Router();
* router.method('post');
* router.post('.hbs', function(file, next) {
* next();
* });
* ```
*
* @param {String|Array} `methods` New methods to add to the router.
* @return {Object} the router to enable chaining
* @api public
*/
proto.method = function(methods) {
methods = utils.arrayify(methods);
this.methods = this.methods.concat(methods);
decorate(this, methods);
return this;
};
/**
* Define a non-enumerable option on the `file` object.
*/
Object.defineProperty(proto, 'defineOption', {
enumerable: false,
value: function(file, key, value) {
file.options = file.options || {};
Object.defineProperty(file.options, key, {
enumerable: false,
configurable: true,
set: function(val) {
value = val;
},
get: function() {
return value;
}
});
}
});
/**
* Decorate the `router` with the given methods to provide middleware
* for the router.
*
* @param {Object} `router` Router instance to add methods to.
* @param {Array} `methods` Methods to add to the `router`
* @api private
*/
function decorate(router, methods) {
var arr = methods.concat('all');
var len = arr.length, i = 0;
while (len--) {
var method = arr[i++];
router[method] = function(path) {
var route = this.route(path);
route[method].apply(route, [].slice.call(arguments, 1));
return this;
};
}
}
// merge params with parent params
function mergeParams(params, parent) {
if (typeof parent !== 'object' || !parent) {
return params;
}
// make copy of parent for base
var obj = utils.extend({}, parent);
// simple non-numeric merging
if (!(0 in params) || !(0 in parent)) {
return utils.extend(obj, params);
}
var i = 0;
var o = 0;
// determine numeric gaps
while (i === o || o in parent) {
if (i in params) i++;
if (o in parent) o++;
}
// offset numeric indices in params before merge
for (i--; i >= 0; i--) {
params[i + o] = params[i];
// create holes for the merge when necessary
if (i < o) {
delete params[i];
}
}
return utils.extend(parent, params);
}
// restore obj props after function
function restore(fn, obj) {
var len = arguments.length - 2;
var props = new Array(len);
var vals = new Array(len);
for (var i = 0; i < props.length; i++) {
props[i] = arguments[i + 2];
vals[i] = obj[props[i]];
}
return function(/* err */) {
// restore vals
for (var j = 0; j < props.length; j++) {
obj[props[j]] = vals[j];
}
return fn.apply(this, arguments);
};
}