////////////////////////////////////////////////////////////////////////// // // // This is a generated file. You can view the original // // source in your browser if your browser supports source maps. // // Source maps are supported by all recent versions of Chrome, Safari, // // and Firefox, and by Internet Explorer 11. // // // ////////////////////////////////////////////////////////////////////////// (function () { /* Imports */ var Meteor = Package.meteor.Meteor; var global = Package.meteor.global; var meteorEnv = Package.meteor.meteorEnv; var _ = Package.underscore._; var EJSON = Package.ejson.EJSON; var Iron = Package['iron:core'].Iron; var Url = Package['clinical:router-url'].Url; /* Package-scope variables */ var Handler, MiddlewareStack; (function(){ ///////////////////////////////////////////////////////////////////////////////////////////////////////// // // // packages/clinical_router-middleware-stack/lib/handler.js // // // ///////////////////////////////////////////////////////////////////////////////////////////////////////// // var Url = Iron.Url; Handler = function (path, fn, options) { if (_.isFunction(path)) { options = options || fn || {}; fn = path; path = '/'; // probably need a better approach here to differentiate between // Router.use(function () {}) and Router.use(MyAdminApp). In the first // case we don't want to count it as a viable server handler when we're // on the client and need to decide whether to go to the server. in the // latter case, we DO want to go to the server, potentially. this.middleware = true; if (typeof options.mount === 'undefined') options.mount = true; } // if fn is a function then typeof fn => 'function' // but note we can't use _.isObject here because that will return true if the // fn is a function OR an object. if (typeof fn === 'object') { options = fn; fn = options.action || 'action'; } options = options || {}; this.options = options; this.mount = options.mount; this.method = (options.method && options.method.toLowerCase()) || false; // should the handler be on the 'client', 'server' or 'both'? // XXX can't we default this to undefined in which case it's run in all // environments? this.where = options.where || 'client'; // if we're mounting at path '/foo' then this handler should also handle // '/foo/bar' and '/foo/bar/baz' if (this.mount) options.end = false; // set the name if (options.name) this.name = options.name; else if (typeof path === 'string' && path.charAt(0) !== '/') this.name = path; else if (typeof path === 'string' && path !== '/') this.name = path.split('/').slice(1).join('.'); // if the path is explicitly set on the options (e.g. legacy router support) // then use that // otherwise use the path argument which could also be a name path = options.path || path; if (typeof path === 'string' && path.charAt(0) !== '/') path = '/' + path; this.path = path; this.compiledUrl = new Url(path, options); if (_.isString(fn)) { this.handle = function handle () { // try to find a method on the current thisArg which might be a Controller // for example. var func = this[fn]; if (typeof func !== 'function') throw new Error("No method named " + JSON.stringify(fn) + " found on handler."); return func.apply(this, arguments); }; } else if (_.isFunction(fn)) { // or just a regular old function this.handle = fn; } }; /** * Returns true if the path matches the handler's compiled url, method * and environment (e.g. client/server). If no options.method or options.where * is provided, then only the path will be used to test. */ Handler.prototype.test = function (path, options) { options = options || {}; var isUrlMatch = this.compiledUrl.test(path); var isMethodMatch = true; var isEnvMatch = true; if (this.method && options.method) isMethodMatch = this.method == options.method.toLowerCase(); if (options.where) isEnvMatch = this.where == options.where; return isUrlMatch && isMethodMatch && isEnvMatch; }; Handler.prototype.params = function (path) { return this.compiledUrl.params(path); }; Handler.prototype.resolve = function (params, options) { return this.compiledUrl.resolve(params, options); }; /** * Returns a new cloned Handler. * XXX problem is here because we're not storing the original path. */ Handler.prototype.clone = function () { var clone = new Handler(this.path, this.handle, this.options); // in case the original function had a name clone.name = this.name; return clone; }; ///////////////////////////////////////////////////////////////////////////////////////////////////////// }).call(this); (function(){ ///////////////////////////////////////////////////////////////////////////////////////////////////////// // // // packages/clinical_router-middleware-stack/lib/middleware_stack.js // // // ///////////////////////////////////////////////////////////////////////////////////////////////////////// // var Url = Iron.Url; var assert = Iron.utils.assert; var defaultValue = Iron.utils.defaultValue; /** * Connect inspired middleware stack that works on the client and the server. * * You can add handlers to the stack for various paths. Those handlers can run * on the client or server. Then you can dispatch into the stack with a * given path by calling the dispatch method. This goes down the stack looking * for matching handlers given the url and environment (client/server). If we're * on the client and we should make a trip to the server, the onServerDispatch * callback is called. * * The middleware stack supports the Connect API. But it also allows you to * specify a context so we can have one context object (like a Controller) that * is a consistent context for each handler function called on a dispatch. * */ MiddlewareStack = function () { this._stack = []; this.length = 0; }; MiddlewareStack.prototype._create = function (path, fn, options) { var handler = new Handler(path, fn, options); var name = handler.name; if (name) { if (_.has(this._stack, name)) { throw new Error("Handler with name '" + name + "' already exists."); } this._stack[name] = handler; } return handler; }; MiddlewareStack.prototype.findByName = function (name) { return this._stack[name]; }; /** * Push a new handler onto the stack. */ MiddlewareStack.prototype.push = function (path, fn, options) { var handler = this._create(path, fn, options); this._stack.push(handler); this.length++; return handler; }; MiddlewareStack.prototype.append = function (/* fn1, fn2, [f3, f4]... */) { var self = this; var args = _.toArray(arguments); var options = {}; if (typeof args[args.length-1] === 'object') options = args.pop(); _.each(args, function (fnOrArray) { if (typeof fnOrArray === 'undefined') return; else if (typeof fnOrArray === 'function') self.push(fnOrArray, options); else if (_.isArray(fnOrArray)) self.append.apply(self, fnOrArray.concat([options])); else throw new Error("Can only append functions or arrays to the MiddlewareStack"); }); return this; }; /** * Insert a handler at a specific index in the stack. * * The index behavior is the same as Array.prototype.splice. If the index is * greater than the stack length the handler will be appended at the end of the * stack. If the index is negative, the item will be inserted "index" elements * from the end. */ MiddlewareStack.prototype.insertAt = function (index, path, fn, options) { var handler = this._create(path, fn, options); this._stack.splice(index, 0, handler); this.length = this._stack.length; return this; }; /** * Insert a handler before another named handler. */ MiddlewareStack.prototype.insertBefore = function (name, path, fn, options) { var beforeHandler; var index; if (!(beforeHandler = this._stack[name])) throw new Error("Couldn't find a handler named '" + name + "' on the path stack"); index = _.indexOf(this._stack, beforeHandler); this.insertAt(index, path, fn, options); return this; }; /** * Insert a handler after another named handler. * */ MiddlewareStack.prototype.insertAfter = function (name, path, fn, options) { var handler; var index; if (!(handler = this._stack[name])) throw new Error("Couldn't find a handler named '" + name + "' on the path stack"); index = _.indexOf(this._stack, handler); this.insertAt(index + 1, path, fn, options); return this; }; /** * Return a new MiddlewareStack comprised of this stack joined with other * stacks. Note the new stack will not have named handlers anymore. Only the * handlers are cloned but not the name=>handler mapping. */ MiddlewareStack.prototype.concat = function (/* stack1, stack2, */) { var ret = new MiddlewareStack; var concat = Array.prototype.concat; var clonedThisStack = EJSON.clone(this._stack); var clonedOtherStacks = _.map(_.toArray(arguments), function (s) { return EJSON.clone(s._stack); }); ret._stack = concat.apply(clonedThisStack, clonedOtherStacks); ret.length = ret._stack.length; return ret; }; /** * Dispatch into the middleware stack, allowing the handlers to control the * iteration by calling this.next(); */ MiddlewareStack.prototype.dispatch = function dispatch (url, context, done) { var self = this; var originalUrl = url; assert(typeof url === 'string', "Requires url"); assert(typeof context === 'object', "Requires context object"); url = Url.normalize(url || '/'); defaultValue(context, 'request', {}); defaultValue(context, 'response', {}); defaultValue(context, 'originalUrl', url); //defaultValue(context, 'location', Url.parse(originalUrl)); defaultValue(context, '_method', context.method); defaultValue(context, '_handlersForEnv', {client: false, server: false}); defaultValue(context, '_handled', false); defaultValue(context, 'isHandled', function () { return context._handled; }); defaultValue(context, 'willBeHandledOnClient', function () { return context._handlersForEnv.client; }); defaultValue(context, 'willBeHandledOnServer', function () { return context._handlersForEnv.server; }); var wrappedDone = function () { if (done) { try { done.apply(this, arguments); } catch (err) { // if we catch an error at this point in the stack we don't want it // handled in the next() iterator below. So we'll mark the error to tell // the next iterator to ignore it. err._punt = true; // now rethrow it! throw err; } } }; var index = 0; var next = Meteor.bindEnvironment(function boundNext (err) { var handler = self._stack[index++]; // reset the url context.url = context.request.url = context.originalUrl; if (!handler) return wrappedDone.call(context, err); if (!handler.test(url, {method: context._method})) return next(err); // okay if we've gotten this far the handler matches our url but we still // don't know if this is a client or server handler. Let's track that. // XXX couldn't the environment be something else like cordova? var where = Meteor.isClient ? 'client' : 'server'; // track that we have a handler for the given environment so long as it's // not middleware created like this Router.use(function () {}). We'll assume // that if the handler is of that form we don't want to make a trip to // the client or the server for it. if (!handler.middleware) context._handlersForEnv[handler.where] = true; // but if we're not actually on that env, skip to the next handler. if (handler.where !== where) return next(err); // get the parameters for this url from the handler's compiled path // XXX removing for now //var params = handler.params(context.location.href); //context.request.params = defaultValue(context, 'params', {}); //_.extend(context.params, params); // so we can call this.next() // XXX this breaks with things like request.body which require that the // iterator be saved for the given function call. context.next = next; if (handler.mount) { var mountpath = Url.normalize(handler.compiledUrl.pathname); var newUrl = url.substr(mountpath.length, url.length); newUrl = Url.normalize(newUrl); context.url = context.request.url = newUrl; } try { // // The connect api says a handler signature (arity) can look like any of: // // 1) function (req, res, next) // 2) function (err, req, res, next) // 3) function (err) var arity = handler.handle.length var req = context.request; var res = context.response; // function (err, req, res, next) if (err && arity === 4) return handler.handle.call(context, err, req, res, next); // function (req, res, next) if (!err && arity < 4) return handler.handle.call(context, req, res, next); // default is function (err) so punt the error down the stack // until we either find a handler who likes to deal with errors or we call // out return next(err); } catch (err) { if (err._punt) // ignore this error and throw it down the stack throw err; else // see if the next handler wants to deal with the error next(err); } finally { // we'll put this at the end because some middleware // might want to decide what to do based on whether we've // been handled "yet". If we set this to true before the handler // is called, there's no way for the handler to say, if we haven't been // handled yet go to the server, for example. context._handled = true; context.next = null; } }); next(); context.next = null; return context; }; Iron = Iron || {}; Iron.MiddlewareStack = MiddlewareStack; ///////////////////////////////////////////////////////////////////////////////////////////////////////// }).call(this); /* Exports */ Package._define("clinical:router-middleware-stack", { MiddlewareStack: MiddlewareStack, Handler: Handler }); })();