Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Метод генерации урла ns.router.generateUrl #154

Merged
merged 6 commits into from
Sep 12, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 187 additions & 56 deletions src/ns.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ ns.router = function(url) {

var r = route.regexp.exec(url);
if (r) {
var tokens = route.tokens;
var defaults = route.defaults;
var rparams = route.params;
var params = {};

// Вытаскиваем параметры из основной части урла. Имена параметров берем из массива tokens.
var l = tokens.length;
// Вытаскиваем параметры из основной части урла.
var l = rparams.length;
var rparam;
for (var k = 0; k < l; k++) {
params[ tokens[k] ] = r[ k + 1 ] || defaults[ tokens[k] ];
rparam = rparams[k];
params[rparam.name] = r[k + 1] || rparam.default_value;
}

// Смотрим, есть ли дополнительные get-параметры, вида ?param1=value1&param2=value2...
Expand Down Expand Up @@ -82,15 +83,6 @@ ns.router = function(url) {

};

/**
* Generate url.
* @param {string} url Relative url.
* @return {String} Valid url that takes into consideration baseDir.
*/
ns.router.url = function(url) {
return ns.router.baseDir + url;
};

/**
* Inititialize ns.router, compiles defined routes.
*/
Expand All @@ -102,76 +94,215 @@ ns.router.init = function() {
_routes.rewriteUrl = routes.rewriteUrl || {};
_routes.rewriteParams = routes.rewriteParams || {};

// FIXME вообще конечно лучше бы route был массивом, потому что нам важен порядок рутов... пока не трогаем )
var rawRoutes = routes.route || {};

var compiledRoutes = [];
var compiledRoutesHash = {};
for (var route in rawRoutes) {
var page = rawRoutes[route];
var compiled = ns.router.compile(route);
compiled.page = rawRoutes[route];
compiled.page = page;
compiledRoutes.push(compiled);
compiledRoutesHash[page] = compiledRoutesHash[page] || [];
compiledRoutesHash[page].push(compiled);
}
_routes.route = compiledRoutes;
_routes.routeHash = compiledRoutesHash;

ns.router._routes = _routes;

// Типы нужны при генерации урла.
ns.router._regexps = {};
for (var id in ns.router.regexps) {
ns.router._regexps[id] = new RegExp( ns.router.regexps[id] );
}
};

/**
* Compile route.
* @param {String} route
* @return {{ regexp: RegExp, tokens: Array.<string> }}
*/
ns.router.compile = function(route) {
// Отрезаем последний слэш, он ниже добавится как необязательный.
var regexp = route.replace(/\/$/, '');

var tokens = [];
var defaults = {};

// Заменяем {name} на кусок регэкспа соответствующего типу токена name.
// Матч на слеш нужен, чтобы сделать слеш опциональным.
regexp = regexp.replace(/(\/?){(.*?)}/g, function(_, slash, token) {
var tokenParts = token.split(':');
slash = slash || '';

var type = tokenParts[1] || 'id';
var rx_part = ns.router.regexps[type];
if (!rx_part) {
throw new Error("[ns.router] Can't find regexp for '" + type +"'!");
* Generate url.
* @param {string} url Relative url.
* @return {String} Valid url that takes into consideration baseDir.
*/
ns.router.url = function(url) {
return (ns.router.baseDir + url) || '/';
};

ns.router.generateUrl = function(id, params) {
var url;
var routes = ns.router._routes.routeHash[id];
params = params || {};

if (!routes || !routes.length) {
throw new Error("[ns.router] Could not find route with id '" + id + "'!");
}

for (var i = 0; i < routes.length; i++) {
url = ns.router._generateUrl(routes[i], params);
if (url) {
break;
}
}

var tokenName = tokenParts[0];
var equalSignIndex = tokenName.indexOf('=');
if (url === null) {
throw new Error("[ns.router] Could not generate url for layout id '" + id + "'!");
}

return ns.router.url(url);
};

if (equalSignIndex > 0) {
var tokenDefault = tokenName.substring(equalSignIndex + 1);
tokenName = tokenName.substring(0, equalSignIndex);
/**
* @param {Object} def Url definition.
* @param {Object} params Url generation params.
* @return {?string} Generated url.
*/
ns.router._generateUrl = function(def, params) {
var url;
var part;
var pvalue;
var result = [];
var query = no.extend({}, params);
var rewrites = ns.router._routes.rewriteUrl;

for (var i = 0; i < def.parts.length; i++) {
part = def.parts[i];
if (!part.name) {
// Добавляем статический кусок урла как есть.
result.push(part.default_value);
} else {
pvalue = params[part.name] || part.default_value;

tokens.push(tokenName);
defaults[tokenName] = tokenDefault;
if (slash) {
return '(?:' + slash + '(' + rx_part + '))?';
// Обязательный параметр должен быть указан.
if (!part.is_optional && !pvalue) {
return null;
}
else {
return '(' + rx_part + ')?';

// Опциональный параметр не должен попасть в урл, если он не указан явно в params.
if (part.is_optional && !(part.name in params)) {
continue;
}

} else {
// Запоминаем имя токена, оно нужно при парсинге урлов.
tokens.push(tokenName);
return slash + '(' + rx_part + ')';
// Проверка типа.
if (!ns.router._regexps[part.type].test(pvalue)) {
return null;
}

result.push(pvalue);
delete query[part.name];
}
});
// Добавляем "якоря" ^ и $;
// Плюс матчим необязательный query-string в конце урла, вида ?param1=value1&param2=value2...
}

url = result.join('/');
url = (url) ? ('/' + url) : '';

// Разворачиваем rewrite правила, чтобы получить красивый урл до rewrite-ов.
var rewrote = true;
while (rewrote) {
rewrote = false;
for (var srcUrl in rewrites) {
if (url === rewrites[srcUrl]) {
url = srcUrl;
rewrote = true;
}
}
}

// Дописываем query string.
var queryString = $.param(query);
return (queryString) ? (url + '?' + queryString) : url;
};

/**
* Compile route.
* @param {String} route
* @return {{ regexp: RegExp, tokens: Array.<string> }}
*/
ns.router.compile = function(route) {
// Удаляем слеши в начале и в конце урла.
route = route
.replace(/^\//, '')
.replace(/\/$/, '');

var parts = route.split('/');
var params = parts.map(ns.router._parseParam);
var pregexps = params.map(ns.router._generateParamRegexp);
var regexp = pregexps.join('');

// Добавляем "якоря" ^ и $;
// Плюс матчим необязательный query-string в конце урла, вида ?param1=value1&param2=value2...
regexp = '^' + regexp + '\/?(?:\\?(.*))?$';

return {
regexp: new RegExp(regexp),
tokens: tokens,
defaults: defaults
params: params.filter(function(p) { return !!p.name; }), // оставляем только настоящие параметры, а не статические части урла
parts: params // для генерации урла нужны все параметры
};
};

ns.router._parseParam = function(param) {
var param_extract;
var type_parts;
var default_parts;
var param_type;
var param_default;
var param_is_optional;

// Параметр (указывается в фигурных скобках)
param_extract = /{([^}]+)}/.exec(param);
if (param_extract) {
// Самый сложный вариант: {name=default:type}
param = param_extract[1];

// parameter type (defaults to id)
type_parts = param.split(':');
param_type = type_parts[1] || 'id';

// parameter default value and if parameter is optional
param = type_parts[0];
default_parts = param.split('=');
param_default = default_parts[1];
param_is_optional = (default_parts.length > 1);

// section parsed
param = default_parts[0];
return {
name: param,
type: param_type,
default_value: param_default,
is_optional: param_is_optional
};
} else {
// статический кусок урла
return {
default_value: param
};
}
};

ns.router._generateParamRegexp = function(p) {
var re;
var regexps = ns.router.regexps;

// static text
if (p.default_value && !p.name) {
return '/' + p.default_value;
}

// validate parameter type is known (if specified)
if (p.type && !(p.type in regexps)) {
throw new Error("[ns.router] Could not find regexp for '" + p.type + "'!");
}

re = regexps[p.type];
re = '/(' + re + ')';

if (p.is_optional) {
re = '(?:' + re + ')?';
}

return re;
};

/**
* Базовая часть урла, относительно которой строятся урлы. Без слэша на конце.
* @type {String}
Expand Down
2 changes: 2 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
<script src="spec/ns.page.js"></script>
<script src="spec/ns.request.js"></script>
<script src="spec/ns.router.js"></script>
<script src="spec/ns.router2.js"></script>
<script src="spec/ns.router.generateUrl.js"></script>
<script src="spec/ns.updater.js"></script>
<script src="spec/ns.view.js"></script>
<script src="spec/ns.view.bind-events.js"></script>
Expand Down
Loading