mirror of https://github.com/jo/docuri.git
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.
133 lines
3.0 KiB
JavaScript
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] || '';
|
|
|
|
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);
|
|
}
|
|
};
|
|
};
|
|
|