1 | import { parsePath, parseQueryString } from './path-utils';
|
2 | const isAbsolute = (pathname) => {
|
3 | return pathname.charAt(0) === '/';
|
4 | };
|
5 | export const createKey = (keyLength) => {
|
6 | return Math.random().toString(36).substr(2, keyLength);
|
7 | };
|
8 |
|
9 | const spliceOne = (list, index) => {
|
10 | for (let i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) {
|
11 | list[i] = list[k];
|
12 | }
|
13 | list.pop();
|
14 | };
|
15 |
|
16 | export const resolvePathname = (to, from = '') => {
|
17 | let fromParts = from && from.split('/') || [];
|
18 | let hasTrailingSlash;
|
19 | let up = 0;
|
20 | const toParts = to && to.split('/') || [];
|
21 | const isToAbs = to && isAbsolute(to);
|
22 | const isFromAbs = from && isAbsolute(from);
|
23 | const mustEndAbs = isToAbs || isFromAbs;
|
24 | if (to && isAbsolute(to)) {
|
25 |
|
26 | fromParts = toParts;
|
27 | }
|
28 | else if (toParts.length) {
|
29 |
|
30 | fromParts.pop();
|
31 | fromParts = fromParts.concat(toParts);
|
32 | }
|
33 | if (!fromParts.length) {
|
34 | return '/';
|
35 | }
|
36 | if (fromParts.length) {
|
37 | const last = fromParts[fromParts.length - 1];
|
38 | hasTrailingSlash = (last === '.' || last === '..' || last === '');
|
39 | }
|
40 | else {
|
41 | hasTrailingSlash = false;
|
42 | }
|
43 | for (let i = fromParts.length; i >= 0; i--) {
|
44 | const part = fromParts[i];
|
45 | if (part === '.') {
|
46 | spliceOne(fromParts, i);
|
47 | }
|
48 | else if (part === '..') {
|
49 | spliceOne(fromParts, i);
|
50 | up++;
|
51 | }
|
52 | else if (up) {
|
53 | spliceOne(fromParts, i);
|
54 | up--;
|
55 | }
|
56 | }
|
57 | if (!mustEndAbs) {
|
58 | for (; up--; up) {
|
59 | fromParts.unshift('..');
|
60 | }
|
61 | }
|
62 | if (mustEndAbs && fromParts[0] !== '' && (!fromParts[0] || !isAbsolute(fromParts[0]))) {
|
63 | fromParts.unshift('');
|
64 | }
|
65 | let result = fromParts.join('/');
|
66 | if (hasTrailingSlash && result.substr(-1) !== '/') {
|
67 | result += '/';
|
68 | }
|
69 | return result;
|
70 | };
|
71 | export const valueEqual = (a, b) => {
|
72 | if (a === b) {
|
73 | return true;
|
74 | }
|
75 | if (a == null || b == null) {
|
76 | return false;
|
77 | }
|
78 | if (Array.isArray(a)) {
|
79 | return Array.isArray(b) && a.length === b.length && a.every((item, index) => {
|
80 | return valueEqual(item, b[index]);
|
81 | });
|
82 | }
|
83 | const aType = typeof a;
|
84 | const bType = typeof b;
|
85 | if (aType !== bType) {
|
86 | return false;
|
87 | }
|
88 | if (aType === 'object') {
|
89 | const aValue = a.valueOf();
|
90 | const bValue = b.valueOf();
|
91 | if (aValue !== a || bValue !== b) {
|
92 | return valueEqual(aValue, bValue);
|
93 | }
|
94 | const aKeys = Object.keys(a);
|
95 | const bKeys = Object.keys(b);
|
96 | if (aKeys.length !== bKeys.length) {
|
97 | return false;
|
98 | }
|
99 | return aKeys.every((key) => {
|
100 | return valueEqual(a[key], b[key]);
|
101 | });
|
102 | }
|
103 | return false;
|
104 | };
|
105 | export const locationsAreEqual = (a, b) => {
|
106 | return a.pathname === b.pathname &&
|
107 | a.search === b.search &&
|
108 | a.hash === b.hash &&
|
109 | a.key === b.key &&
|
110 | valueEqual(a.state, b.state);
|
111 | };
|
112 | export const createLocation = (path, state, key, currentLocation) => {
|
113 | let location;
|
114 | if (typeof path === 'string') {
|
115 |
|
116 | location = parsePath(path);
|
117 | if (state !== undefined) {
|
118 | location.state = state;
|
119 | }
|
120 | }
|
121 | else {
|
122 |
|
123 | location = Object.assign({ pathname: '' }, path);
|
124 | if (location.search && location.search.charAt(0) !== '?') {
|
125 | location.search = '?' + location.search;
|
126 | }
|
127 | if (location.hash && location.hash.charAt(0) !== '#') {
|
128 | location.hash = '#' + location.hash;
|
129 | }
|
130 | if (state !== undefined && location.state === undefined) {
|
131 | location.state = state;
|
132 | }
|
133 | }
|
134 | try {
|
135 | location.pathname = decodeURI(location.pathname);
|
136 | }
|
137 | catch (e) {
|
138 | if (e instanceof URIError) {
|
139 | throw new URIError('Pathname "' + location.pathname + '" could not be decoded. ' +
|
140 | 'This is likely caused by an invalid percent-encoding.');
|
141 | }
|
142 | else {
|
143 | throw e;
|
144 | }
|
145 | }
|
146 | location.key = key;
|
147 | if (currentLocation) {
|
148 |
|
149 | if (!location.pathname) {
|
150 | location.pathname = currentLocation.pathname;
|
151 | }
|
152 | else if (location.pathname.charAt(0) !== '/') {
|
153 | location.pathname = resolvePathname(location.pathname, currentLocation.pathname);
|
154 | }
|
155 | }
|
156 | else {
|
157 |
|
158 | if (!location.pathname) {
|
159 | location.pathname = '/';
|
160 | }
|
161 | }
|
162 | location.query = parseQueryString(location.search || '');
|
163 | return location;
|
164 | };
|