Skip to content

Commit 082455b

Browse files
committed
Simplify path encoding/decoding
1 parent 851ba01 commit 082455b

File tree

6 files changed

+70
-47
lines changed

6 files changed

+70
-47
lines changed

modules/locations/HashLocation.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
1+
var invariant = require('react/lib/invariant');
2+
var canUseDOM = require('react/lib/ExecutionEnvironment').canUseDOM;
13
var LocationActions = require('../actions/LocationActions');
2-
var getWindowPath = require('../utils/getWindowPath');
4+
var Path = require('../utils/Path');
35

6+
/**
7+
* Returns the current URL path from `window.location.hash`, including query string
8+
*/
49
function getHashPath() {
5-
return window.location.hash.substr(1);
10+
invariant(
11+
canUseDOM,
12+
'getHashPath needs a DOM'
13+
);
14+
15+
return Path.decode(
16+
window.location.hash.substr(1)
17+
);
618
}
719

820
var _actionType;
@@ -69,12 +81,12 @@ var HashLocation = {
6981

7082
push: function (path) {
7183
_actionType = LocationActions.PUSH;
72-
window.location.hash = path;
84+
window.location.hash = Path.encode(path);
7385
},
7486

7587
replace: function (path) {
7688
_actionType = LocationActions.REPLACE;
77-
window.location.replace(getWindowPath() + '#' + path);
89+
window.location.replace(window.location.pathname + '#' + Path.encode(path));
7890
},
7991

8092
pop: function () {

modules/locations/HistoryLocation.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1+
var invariant = require('react/lib/invariant');
2+
var canUseDOM = require('react/lib/ExecutionEnvironment').canUseDOM;
13
var LocationActions = require('../actions/LocationActions');
2-
var getWindowPath = require('../utils/getWindowPath');
4+
var Path = require('../utils/Path');
5+
6+
/**
7+
* Returns the current URL path from `window.location`, including query string
8+
*/
9+
function getWindowPath() {
10+
invariant(
11+
canUseDOM,
12+
'getWindowPath needs a DOM'
13+
);
14+
15+
return Path.decode(
16+
window.location.pathname + window.location.search
17+
);
18+
}
319

420
var _changeListeners = [];
521

@@ -41,12 +57,12 @@ var HistoryLocation = {
4157
},
4258

4359
push: function (path) {
44-
window.history.pushState({ path: path }, '', path);
60+
window.history.pushState({ path: path }, '', Path.encode(path));
4561
notifyChange(LocationActions.PUSH);
4662
},
4763

4864
replace: function (path) {
49-
window.history.replaceState({ path: path }, '', path);
65+
window.history.replaceState({ path: path }, '', Path.encode(path));
5066
notifyChange(LocationActions.REPLACE);
5167
},
5268

modules/locations/RefreshLocation.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
var getWindowPath = require('../utils/getWindowPath');
1+
var HistoryLocation = require('./HistoryLocation');
2+
var Path = require('../utils/Path');
23

34
/**
45
* A Location that uses full page refreshes. This is used as
@@ -8,18 +9,18 @@ var getWindowPath = require('../utils/getWindowPath');
89
var RefreshLocation = {
910

1011
push: function (path) {
11-
window.location = path;
12+
window.location = Path.encode(path);
1213
},
1314

1415
replace: function (path) {
15-
window.location.replace(path);
16+
window.location.replace(Path.encode(path));
1617
},
1718

1819
pop: function () {
1920
window.history.back();
2021
},
2122

22-
getCurrentPath: getWindowPath,
23+
getCurrentPath: HistoryLocation.getCurrentPath,
2324

2425
toString: function () {
2526
return '<RefreshLocation>';

modules/utils/Path.js

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@ var invariant = require('react/lib/invariant');
22
var merge = require('qs/lib/utils').merge;
33
var qs = require('qs');
44

5-
function encodeURL(url) {
6-
return encodeURIComponent(url).replace(/%20/g, '+');
5+
function decodePathSegment(string) {
6+
return decodeURIComponent(string.replace(/\+/g, ' '));
77
}
88

9-
function decodeURL(url) {
10-
return decodeURIComponent(url.replace(/\+/g, ' '));
11-
}
12-
13-
function encodeURLPath(path) {
14-
return String(path).split('/').map(encodeURL).join('/');
9+
function encodePathSegment(string) {
10+
return encodeURIComponent(string).replace(/%20/g, '+');
1511
}
1612

1713
var paramCompileMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*.()\[\]\\+|{}^$]/g;
@@ -47,6 +43,20 @@ function compilePattern(pattern) {
4743

4844
var Path = {
4945

46+
/**
47+
* Safely decodes special characters in the given URL path.
48+
*/
49+
decode: function decodePath(path) {
50+
return String(path).split('/').map(decodePathSegment).join('/');
51+
},
52+
53+
/**
54+
* Safely encodes special characters in the given URL path.
55+
*/
56+
encode: function encodePath(path) {
57+
return String(path).split('/').map(encodePathSegment).join('/');
58+
},
59+
5060
/**
5161
* Returns an array of the names of all parameters in the given pattern.
5262
*/
@@ -61,7 +71,7 @@ var Path = {
6171
*/
6272
extractParams: function (pattern, path) {
6373
var object = compilePattern(pattern);
64-
var match = decodeURL(path).match(object.matcher);
74+
var match = path.match(object.matcher);
6575

6676
if (!match)
6777
return null;
@@ -94,10 +104,10 @@ var Path = {
94104
'Missing "' + paramName + '" parameter for path "' + pattern + '"'
95105
);
96106
} else {
97-
paramName = paramName.slice(0, -1)
98-
if (params[paramName] == null) {
99-
return '';
100-
}
107+
paramName = paramName.slice(0, -1);
108+
109+
if (params[paramName] == null)
110+
return '';
101111
}
102112

103113
var segment;
@@ -112,7 +122,7 @@ var Path = {
112122
segment = params[paramName];
113123
}
114124

115-
return encodeURLPath(segment);
125+
return segment;
116126
}).replace(paramInjectTrailingSlashMatcher, '/');
117127
},
118128

modules/utils/__tests__/Path-test.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ describe('Path.extractParams', function () {
9393

9494
describe('and the path matches', function () {
9595
it('returns an empty object', function () {
96-
expect(Path.extractParams(pattern, 'one%2C+two')).toEqual({});
96+
expect(Path.extractParams(pattern, 'one, two')).toEqual({});
9797
});
9898
});
9999

100100
describe('and the path does not match', function () {
101101
it('returns null', function () {
102-
expect(Path.extractParams(pattern, 'one+two')).toBe(null);
102+
expect(Path.extractParams(pattern, 'one two')).toBe(null);
103103
});
104104
});
105105
});
@@ -109,7 +109,7 @@ describe('Path.extractParams', function () {
109109

110110
describe('and the path matches', function () {
111111
it('returns an object with the params', function () {
112-
expect(Path.extractParams(pattern, '/comments/abc/edit+now')).toEqual({ id: 'abc' });
112+
expect(Path.extractParams(pattern, '/comments/abc/edit now')).toEqual({ id: 'abc' });
113113
});
114114
});
115115

@@ -198,7 +198,7 @@ describe('Path.injectParams', function () {
198198
var pattern = 'comments/:id?/edit';
199199

200200
it('returns the correct path when param is supplied', function () {
201-
expect(Path.injectParams(pattern, {id:'123'})).toEqual('comments/123/edit');
201+
expect(Path.injectParams(pattern, { id:'123' })).toEqual('comments/123/edit');
202202
});
203203

204204
it('returns the correct path when param is not supplied', function () {
@@ -210,7 +210,7 @@ describe('Path.injectParams', function () {
210210
var pattern = 'comments/:id?/?edit';
211211

212212
it('returns the correct path when param is supplied', function () {
213-
expect(Path.injectParams(pattern, {id:'123'})).toEqual('comments/123/edit');
213+
expect(Path.injectParams(pattern, { id:'123' })).toEqual('comments/123/edit');
214214
});
215215

216216
it('returns the correct path when param is not supplied', function () {
@@ -230,7 +230,7 @@ describe('Path.injectParams', function () {
230230

231231
describe('and some params have special URL encoding', function () {
232232
it('returns the correct path', function () {
233-
expect(Path.injectParams(pattern, { id: 'one, two' })).toEqual('comments/one%2C+two/edit');
233+
expect(Path.injectParams(pattern, { id: 'one, two' })).toEqual('comments/one, two/edit');
234234
});
235235
});
236236

modules/utils/getWindowPath.js

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)