1 | const hasBasename = (path, prefix) => {
|
2 | return (new RegExp('^' + prefix + '(\\/|\\?|#|$)', 'i')).test(path);
|
3 | };
|
4 | const stripBasename = (path, prefix) => {
|
5 | return hasBasename(path, prefix) ? path.substr(prefix.length) : path;
|
6 | };
|
7 | const stripTrailingSlash = (path) => {
|
8 | return path.charAt(path.length - 1) === '/' ? path.slice(0, -1) : path;
|
9 | };
|
10 | const addLeadingSlash = (path) => {
|
11 | return path.charAt(0) === '/' ? path : '/' + path;
|
12 | };
|
13 | const stripLeadingSlash = (path) => {
|
14 | return path.charAt(0) === '/' ? path.substr(1) : path;
|
15 | };
|
16 | const parsePath = (path) => {
|
17 | let pathname = path || '/';
|
18 | let search = '';
|
19 | let hash = '';
|
20 | const hashIndex = pathname.indexOf('#');
|
21 | if (hashIndex !== -1) {
|
22 | hash = pathname.substr(hashIndex);
|
23 | pathname = pathname.substr(0, hashIndex);
|
24 | }
|
25 | const searchIndex = pathname.indexOf('?');
|
26 | if (searchIndex !== -1) {
|
27 | search = pathname.substr(searchIndex);
|
28 | pathname = pathname.substr(0, searchIndex);
|
29 | }
|
30 | return {
|
31 | pathname,
|
32 | search: search === '?' ? '' : search,
|
33 | hash: hash === '#' ? '' : hash,
|
34 | query: {},
|
35 | key: ''
|
36 | };
|
37 | };
|
38 | const createPath = (location) => {
|
39 | const { pathname, search, hash } = location;
|
40 | let path = pathname || '/';
|
41 | if (search && search !== '?') {
|
42 | path += (search.charAt(0) === '?' ? search : `?${search}`);
|
43 | }
|
44 | if (hash && hash !== '#') {
|
45 | path += (hash.charAt(0) === '#' ? hash : `#${hash}`);
|
46 | }
|
47 | return path;
|
48 | };
|
49 | const parseQueryString = (query) => {
|
50 | if (!query) {
|
51 | return {};
|
52 | }
|
53 | return (/^[?#]/.test(query) ? query.slice(1) : query)
|
54 | .split('&')
|
55 | .reduce((params, param) => {
|
56 | let [key, value] = param.split('=');
|
57 | params[key] = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : '';
|
58 | return params;
|
59 | }, {});
|
60 | };
|
61 |
|
62 | const isAbsolute = (pathname) => {
|
63 | return pathname.charAt(0) === '/';
|
64 | };
|
65 | const createKey = (keyLength) => {
|
66 | return Math.random().toString(36).substr(2, keyLength);
|
67 | };
|
68 |
|
69 | const spliceOne = (list, index) => {
|
70 | for (let i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) {
|
71 | list[i] = list[k];
|
72 | }
|
73 | list.pop();
|
74 | };
|
75 |
|
76 | const resolvePathname = (to, from = '') => {
|
77 | let fromParts = from && from.split('/') || [];
|
78 | let hasTrailingSlash;
|
79 | let up = 0;
|
80 | const toParts = to && to.split('/') || [];
|
81 | const isToAbs = to && isAbsolute(to);
|
82 | const isFromAbs = from && isAbsolute(from);
|
83 | const mustEndAbs = isToAbs || isFromAbs;
|
84 | if (to && isAbsolute(to)) {
|
85 |
|
86 | fromParts = toParts;
|
87 | }
|
88 | else if (toParts.length) {
|
89 |
|
90 | fromParts.pop();
|
91 | fromParts = fromParts.concat(toParts);
|
92 | }
|
93 | if (!fromParts.length) {
|
94 | return '/';
|
95 | }
|
96 | if (fromParts.length) {
|
97 | const last = fromParts[fromParts.length - 1];
|
98 | hasTrailingSlash = (last === '.' || last === '..' || last === '');
|
99 | }
|
100 | else {
|
101 | hasTrailingSlash = false;
|
102 | }
|
103 | for (let i = fromParts.length; i >= 0; i--) {
|
104 | const part = fromParts[i];
|
105 | if (part === '.') {
|
106 | spliceOne(fromParts, i);
|
107 | }
|
108 | else if (part === '..') {
|
109 | spliceOne(fromParts, i);
|
110 | up++;
|
111 | }
|
112 | else if (up) {
|
113 | spliceOne(fromParts, i);
|
114 | up--;
|
115 | }
|
116 | }
|
117 | if (!mustEndAbs) {
|
118 | for (; up--; up) {
|
119 | fromParts.unshift('..');
|
120 | }
|
121 | }
|
122 | if (mustEndAbs && fromParts[0] !== '' && (!fromParts[0] || !isAbsolute(fromParts[0]))) {
|
123 | fromParts.unshift('');
|
124 | }
|
125 | let result = fromParts.join('/');
|
126 | if (hasTrailingSlash && result.substr(-1) !== '/') {
|
127 | result += '/';
|
128 | }
|
129 | return result;
|
130 | };
|
131 | const valueEqual = (a, b) => {
|
132 | if (a === b) {
|
133 | return true;
|
134 | }
|
135 | if (a == null || b == null) {
|
136 | return false;
|
137 | }
|
138 | if (Array.isArray(a)) {
|
139 | return Array.isArray(b) && a.length === b.length && a.every((item, index) => {
|
140 | return valueEqual(item, b[index]);
|
141 | });
|
142 | }
|
143 | const aType = typeof a;
|
144 | const bType = typeof b;
|
145 | if (aType !== bType) {
|
146 | return false;
|
147 | }
|
148 | if (aType === 'object') {
|
149 | const aValue = a.valueOf();
|
150 | const bValue = b.valueOf();
|
151 | if (aValue !== a || bValue !== b) {
|
152 | return valueEqual(aValue, bValue);
|
153 | }
|
154 | const aKeys = Object.keys(a);
|
155 | const bKeys = Object.keys(b);
|
156 | if (aKeys.length !== bKeys.length) {
|
157 | return false;
|
158 | }
|
159 | return aKeys.every((key) => {
|
160 | return valueEqual(a[key], b[key]);
|
161 | });
|
162 | }
|
163 | return false;
|
164 | };
|
165 | const locationsAreEqual = (a, b) => {
|
166 | return a.pathname === b.pathname &&
|
167 | a.search === b.search &&
|
168 | a.hash === b.hash &&
|
169 | a.key === b.key &&
|
170 | valueEqual(a.state, b.state);
|
171 | };
|
172 | const createLocation = (path, state, key, currentLocation) => {
|
173 | let location;
|
174 | if (typeof path === 'string') {
|
175 |
|
176 | location = parsePath(path);
|
177 | if (state !== undefined) {
|
178 | location.state = state;
|
179 | }
|
180 | }
|
181 | else {
|
182 |
|
183 | location = Object.assign({ pathname: '' }, path);
|
184 | if (location.search && location.search.charAt(0) !== '?') {
|
185 | location.search = '?' + location.search;
|
186 | }
|
187 | if (location.hash && location.hash.charAt(0) !== '#') {
|
188 | location.hash = '#' + location.hash;
|
189 | }
|
190 | if (state !== undefined && location.state === undefined) {
|
191 | location.state = state;
|
192 | }
|
193 | }
|
194 | try {
|
195 | location.pathname = decodeURI(location.pathname);
|
196 | }
|
197 | catch (e) {
|
198 | if (e instanceof URIError) {
|
199 | throw new URIError('Pathname "' + location.pathname + '" could not be decoded. ' +
|
200 | 'This is likely caused by an invalid percent-encoding.');
|
201 | }
|
202 | else {
|
203 | throw e;
|
204 | }
|
205 | }
|
206 | location.key = key;
|
207 | if (currentLocation) {
|
208 |
|
209 | if (!location.pathname) {
|
210 | location.pathname = currentLocation.pathname;
|
211 | }
|
212 | else if (location.pathname.charAt(0) !== '/') {
|
213 | location.pathname = resolvePathname(location.pathname, currentLocation.pathname);
|
214 | }
|
215 | }
|
216 | else {
|
217 |
|
218 | if (!location.pathname) {
|
219 | location.pathname = '/';
|
220 | }
|
221 | }
|
222 | location.query = parseQueryString(location.search || '');
|
223 | return location;
|
224 | };
|
225 |
|
226 | export { addLeadingSlash as a, stripBasename as b, createLocation as c, createKey as d, createPath as e, stripLeadingSlash as f, hasBasename as h, locationsAreEqual as l, stripTrailingSlash as s, valueEqual as v };
|