You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
docuri/index.js

133 lines
3.0 KiB
JavaScript

/*
* DocURI: Rich document ids for CouchDB.
*
* Copyright (c) 2014 null2 GmbH Berlin
* Licensed under the MIT license.
*/
// type/id/subtype/index/version
var docuri = module.exports = exports = {};
// Cached regular expressions for matching named param parts and splatted parts
// of route strings.
// http://backbonejs.org/docs/backbone.html#section-158
var optionalParam = /\((.*?)\)/g;
var namedParam = /(\(\?)?:\w+/g;
var splatParam = /\*\w+/g;
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
var paramKeys = /[*:]\w+/g;
// Convert a route string into a regular expression,
// with named regular expressions for named arguments.
// http://backbonejs.org/docs/backbone.html#section-165
function routeToRegExp(src) {
var keys = [], match;
while ( ( match = paramKeys.exec( src ) ) !== null )
{
keys.push( match[0] );
}
var route = src.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional) {
return optional ? match : '([^/?]+)';
})
.replace(splatParam, '([^?]*?)');
keys = keys.reduce(function(memo, key) {
var value = '\\' + key;
memo[key] = new RegExp(value + '(\\/|\\)|\\(|$)');
return memo;
}, {});
return {
src: src,
exp: new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'),
keys: keys
}
}
// Given a route and a DocURI return an object of extracted parameters.
// Unmatched DocURIs will be treated as false.
// http://backbonejs.org/docs/backbone.html#section-166
function extractParameters(route, fragment) {
var params = route.exp.exec(fragment);
if (!params) {
return false;
}
params = params.slice(1);
return Object.keys(route.keys).reduce(function(memo, key, i) {
var param = params[i];
if (param) {
if (key[0] === '*') {
param = param.split('/').map(decodeURIComponent);
} else {
param = decodeURIComponent(param);
}
memo[key.substr(1)] = param;
}
return memo;
}, {});
}
// Insert named parameters from object.
function insertParameters(route, obj) {
var str = route.src;
Object.keys(route.keys).forEach(function(key) {
var k = key.substr(1);
var value = (obj[k] !== undefined) ? obj[k] : '';
if (Array.isArray(value)) {
value = value.map(encodeURIComponent).join('/');
} else {
value = encodeURIComponent(value);
}
str = str.replace(route.keys[key], value + '$1');
});
// massage optional parameter
return str
.replace(/\(\/\)/g, '')
.replace(/[)(]/g, '');
}
docuri.route = function(route) {
route = routeToRegExp(route);
return function(source, target) {
source = source || {};
if (target) {
source = extractParameters(route, source);
Object.keys(target).forEach(function(key) {
source[key] = target[key];
});
}
if (typeof source === 'object') {
return insertParameters(route, source);
}
if (typeof source === 'string') {
return extractParameters(route, source);
}
};
};