From ae84eb3975d1c4adc1873d452cc5e1ca93e79484 Mon Sep 17 00:00:00 2001 From: Paul Ryan Date: Fri, 26 Apr 2013 15:04:16 -0600 Subject: [PATCH 1/5] Added wildcard * to the matcher for searchParams. Added a test to ensure this gets parsed from exec. --- .gitignore | 1 + src/urlMatcherFactory.js | 34 ++++++++++++++++++++++++++++++---- test/urlMatcherFactorySpec.js | 7 +++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index b713cef55..cdcce561b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ node_modules # webstorm files .idea +*.iml diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js index 48e0b84a5..8ee9d5964 100644 --- a/src/urlMatcherFactory.js +++ b/src/urlMatcherFactory.js @@ -62,7 +62,7 @@ function UrlMatcher(pattern) { params = this.params = []; function addParameter(id) { - if (!/^\w+$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'"); + if (!/^(\w+|\*)$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'"); if (names[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'"); names[id] = true; params.push(id); @@ -72,6 +72,16 @@ function UrlMatcher(pattern) { return string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&"); } + this.parseUrlVars = function(varset) { + var vars = {}, hash; + var hashes = varset.substring(1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + vars[hash[0]] = decodeURIComponent(hash[1]); + } + return vars; + }; + this.source = pattern; // Split into static segments separated by path parameter placeholders. @@ -96,8 +106,15 @@ function UrlMatcher(pattern) { segment = segment.substring(0, i); this.sourcePath = pattern.substring(0, last+i); - // Allow parameters to be separated by '?' as well as '&' to make concat() easier - forEach(search.substring(1).split(/[&?]/), addParameter); + var searchString = search.substring(1); + if(searchString === '*'){ + this.params.push('*'); + compiled += '(\\?.*)?'; + } else { + // Allow parameters to be separated by '?' as well as '&' to make concat() easier + forEach(searchString.split(/[&?]/), addParameter); + } + } else { this.sourcePath = pattern; this.sourceSearch = ''; @@ -162,7 +179,16 @@ UrlMatcher.prototype.exec = function (path, searchParams) { values = {}, i; for (i=0; i Date: Mon, 29 Apr 2013 17:30:22 -0600 Subject: [PATCH 2/5] Added the wildcard params to the toParams. Forced locals check to fail when to.params contains an asteristk. Prepped for transition triggering on new resolve. --- src/state.js | 21 +++++++++++++++------ src/urlMatcherFactory.js | 16 ++-------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/state.js b/src/state.js index aa75e7024..2141a2fea 100644 --- a/src/state.js +++ b/src/state.js @@ -178,16 +178,21 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { // But clear 'transition', as we still want to cancel any other pending transitions. // TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves, // because we might accidentally abort a legitimate transition initiated from code? - if (to === from && locals === from.locals) { + if (to === from && locals === from.locals && to.params.indexOf('*') < 0) { $state.transition = null; return $q.when($state.current); } - // Normalize/filter parameters before we pass them to event handlers etc. var normalizedToParams = {}; forEach(to.params, function (name) { - var value = toParams[name]; - normalizedToParams[name] = (value != null) ? String(value) : null; + if(name === '*'){ + for(var key in toParams){ + normalizedToParams[key] = toParams[key]; + } + } else { + var value = toParams[name]; + normalizedToParams[name] = (value != null) ? String(value) : null; + } }); toParams = normalizedToParams; @@ -282,11 +287,15 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { // we also need $stateParams to be available for any $injector calls we make during the // dependency resolution process. var $stateParams; - if (paramsAreFiltered) $stateParams = params; + if (paramsAreFiltered && params['*'] === undefined) $stateParams = params; else { $stateParams = {}; forEach(state.params, function (name) { - $stateParams[name] = params[name]; + if(name === '*'){ + // TODO ??? needed for transition but what... + } else { + $stateParams[name] = params[name]; + } }); } var locals = { $stateParams: $stateParams }; diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js index 8ee9d5964..109bd7c0e 100644 --- a/src/urlMatcherFactory.js +++ b/src/urlMatcherFactory.js @@ -72,16 +72,6 @@ function UrlMatcher(pattern) { return string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&"); } - this.parseUrlVars = function(varset) { - var vars = {}, hash; - var hashes = varset.substring(1).split('&'); - for (var i = 0; i < hashes.length; i++) { - hash = hashes[i].split('='); - vars[hash[0]] = decodeURIComponent(hash[1]); - } - return vars; - }; - this.source = pattern; // Split into static segments separated by path parameter placeholders. @@ -109,7 +99,6 @@ function UrlMatcher(pattern) { var searchString = search.substring(1); if(searchString === '*'){ this.params.push('*'); - compiled += '(\\?.*)?'; } else { // Allow parameters to be separated by '?' as well as '&' to make concat() easier forEach(searchString.split(/[&?]/), addParameter); @@ -181,9 +170,8 @@ UrlMatcher.prototype.exec = function (path, searchParams) { for (i=0; i Date: Tue, 30 Apr 2013 10:12:22 -0600 Subject: [PATCH 3/5] Unrolled uneeded checks for wildcard and added the wildcard check to equalForKeys (forces a refresh for all) --- src/state.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/state.js b/src/state.js index 2141a2fea..7070358cd 100644 --- a/src/state.js +++ b/src/state.js @@ -178,7 +178,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { // But clear 'transition', as we still want to cancel any other pending transitions. // TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves, // because we might accidentally abort a legitimate transition initiated from code? - if (to === from && locals === from.locals && to.params.indexOf('*') < 0) { + if (to === from && locals === from.locals) { $state.transition = null; return $q.when($state.current); } @@ -287,12 +287,14 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { // we also need $stateParams to be available for any $injector calls we make during the // dependency resolution process. var $stateParams; - if (paramsAreFiltered && params['*'] === undefined) $stateParams = params; + if (paramsAreFiltered) $stateParams = params; else { $stateParams = {}; forEach(state.params, function (name) { if(name === '*'){ - // TODO ??? needed for transition but what... + for(var key in params){ + $stateParams[key] = params[key]; + } } else { $stateParams[name] = params[name]; } @@ -355,6 +357,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { } function equalForKeys(a, b, keys) { + if(keys.indexOf('*') > 0 && a != b){ + return false; + } for (var i=0; i Date: Wed, 1 May 2013 13:56:56 -0600 Subject: [PATCH 4/5] Reload wildcard flag handler (default is true). --- src/state.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/state.js b/src/state.js index 7070358cd..b2bed1a88 100644 --- a/src/state.js +++ b/src/state.js @@ -166,10 +166,12 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { var toPath = to.path, from = $state.$current, fromParams = $state.params, fromPath = from.path; + var reloadWild = ($state.current.reloadWild === undefined || $state.current.reloadWild); + // Starting from the root of the path, keep all levels that haven't changed var keep, state, locals = root.locals, toLocals = []; for (keep = 0, state = toPath[keep]; - state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams); + state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams, reloadWild); keep++, state = toPath[keep]) { locals = toLocals[keep] = state.locals; } @@ -186,7 +188,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { var normalizedToParams = {}; forEach(to.params, function (name) { if(name === '*'){ - for(var key in toParams){ + for(var key in toParams) { normalizedToParams[key] = toParams[key]; } } else { @@ -356,8 +358,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { }); } - function equalForKeys(a, b, keys) { - if(keys.indexOf('*') > 0 && a != b){ + function equalForKeys(a, b, keys, reloadWild) { + if(keys.indexOf('*') > 0 && a != b && reloadWild){ return false; } for (var i=0; i Date: Thu, 2 May 2013 13:51:16 -0600 Subject: [PATCH 5/5] Fixed test for current wildcard implementation. --- test/urlMatcherFactorySpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/urlMatcherFactorySpec.js b/test/urlMatcherFactorySpec.js index d638fd462..654b0a1d3 100644 --- a/test/urlMatcherFactorySpec.js +++ b/test/urlMatcherFactorySpec.js @@ -30,7 +30,7 @@ describe("UrlMatcher", function () { it(".exec() captures parameter values from wildcard", function () { expect( new UrlMatcher('/users/:id/details/{type}/{repeat:[0-9]+}?*') - .exec('/users/123/details//0?match=&match2=wildcard', {})) + .exec('/users/123/details//0', {match: '', match2: 'wildcard'})) .toEqual({ id:'123', type:'', repeat:'0', match: '', match2: 'wildcard'}); });