mixdown-router
Version:
Router for mixdown.js
163 lines (128 loc) • 5.21 kB
JavaScript
var _ = require('lodash');
var url = require('url');
var querystring = require('querystring');
var BasePlugin = require('mixdown-app').Plugin;
if (typeof (global) === 'undefined') {
var global = typeof (window) === 'undefined' ? {} : window;
}
module.exports = BasePlugin.extend({
_namespace_default: 'route_generator',
init: function (options) {
this._super(options);
this._options.cache_buster = this._options.cache_buster || Math.ceil(Math.random() * 10000);
},
// this allow the manifest to be set if the plugin is used outside of mixdown router.
// mixdown-router extends this class and overrides this method.
manifest: function (route_table) {
if (route_table) {
this.__route_table__ = route_table;
_.each(this.__route_table__, function (routeObject) {
_.each(routeObject.params, function (param) {
if (_.isEmpty(param.regex)) {
param.regex = /(.*)/;
} else if (!(param.regex instanceof RegExp)) {
param.regex = new RegExp(param.regex);
}
});
});
}
return this.__route_table__ || {};
},
/**
* Gets a url object for the given route and parameters. This is where you put your custom route generation.
* http://nodejs.org/api/url.html#url_url
**/
url: function (route, params) {
params = params || {};
var uri = url.parse('');
var routeObject = this.manifest()[route];
if (!routeObject) {
return 'Route Not Found - ' + route;
}
// if these are set on the route, then attach them. This will allow injection of FQ urls.
// this will allow polyfill of window object when in a browser.
var location = global.location || {};
uri.protocol = routeObject.protocol || location.protocol;
uri.hostname = routeObject.hostname || location.hostname;
uri.port = routeObject.hasOwnProperty('port') ? routeObject.port : location.port;
uri.method = routeObject.method || null;
uri.auth = routeObject.auth || null;
// map the param fields to querystring as defined in route.
var queryParams = {};
var restParams = {};
// split params into rest/query
_.each(routeObject.params, function (param, key) {
if (param.kind === 'rest') {
restParams[key] = param;
} else if (param.kind === 'query') {
queryParams[key] = param;
}
});
uri.query = uri.query || {};
if (!_.isEmpty(queryParams)) {
_.each(queryParams, function (param, key) {
if (params.hasOwnProperty(key) || param['default']) {
// replace capturing group with value
var qval = '';
if (typeof (params[key]) !== 'undefined' && params[key] !== null) {
qval = params[key].toString();
} else if (typeof (params['default']) !== 'undefined' && params['default'] !== null) {
return params['default'].toString();
}
if (qval != '') {
uri.query[key] = param.regex.source.replace(/\(.*\)/, qval);
}
}
});
}
// build the pathname.
uri.pathname = '';
// get url segments
// drop first element in array, since a leading slash creates an empty segment
var urlSegments = routeObject.path.split('/');
urlSegments = urlSegments.length > 0 ? urlSegments.slice(1) : urlSegments;
// replace named params with corresponding values and generate uri
_.each(urlSegments, function (segment, i) {
// is this a REST segment?
if (/^\??:/.test(segment)) {
// replace with param value if available.
var pName = segment.replace(/^\??:/, '');
var pConfig = restParams[pName];
if (pConfig && pConfig.kind === 'rest') {
// this is a rest param. replace the capturing group with our value.
var pval = params[pName] ? params[pName] : pConfig['default'];
if (pval) {
var rx = (pConfig.regex instanceof RegExp) ? pConfig.regex.source : pConfig.regex;
uri.pathname += '/' + encodeURIComponent(rx.replace(/\(.*\)/, pval)).replace(/(%2f)/gi, '/');
} else if (segment[0] === '?') {
uri.pathname += '/';
}
}
} else {
// just append
uri.pathname += '/' + segment;
}
});
// do we need to add a cache buster to this?
if (routeObject.cache_buster) {
var cb_key = typeof (routeObject.cache_buster) == 'string' ? routeObject.cache_buster : 'v';
uri.query[cb_key] = this._options.cache_buster;
}
uri.search = _.keys(uri.query).length ? '?' + querystring.stringify(uri.query) : null;
uri.path = uri.pathname + (uri.search || '');
uri.host = uri.hostname ? (uri.hostname + (uri.port && uri.port != 80 ? ':' + uri.port : '')) : null;
return uri;
},
/**
* Gets a url as a string for the given route and parameters. This calls url() and stringifies the route.
*
**/
format: function (route, params) {
var u = this.url(route, params);
var routeObject = this.manifest()[route];
if (routeObject && (routeObject.browser === true || routeObject.browser === 'true')) {
return u.path;
}
return url.format(u);
}
});