@remix-run/router 1.3.0 → 1.3.1-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/router.cjs.js +111 -102
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.js +111 -102
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +111 -102
- package/dist/router.umd.js.map +1 -1
- package/dist/router.umd.min.js +2 -2
- package/dist/router.umd.min.js.map +1 -1
- package/dist/utils.d.ts +2 -2
- package/history.ts +4 -8
- package/package.json +1 -1
- package/router.ts +121 -134
- package/utils.ts +14 -3
package/dist/router.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.3.0
|
|
2
|
+
* @remix-run/router v1.3.1-pre.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -451,10 +451,7 @@
|
|
|
451
451
|
});
|
|
452
452
|
}
|
|
453
453
|
} else {
|
|
454
|
-
warning$1(false,
|
|
455
|
-
// and link to it here so people can understand better what is going on
|
|
456
|
-
// and how to avoid it.
|
|
457
|
-
"You are trying to block a POP navigation to a location that was not " + "created by @remix-run/router. The block will fail silently in " + "production, but in general you should do all navigation with the " + "router (instead of using window.history.pushState directly) " + "to avoid this situation.");
|
|
454
|
+
warning$1(false, "You are trying to perform a POP navigation to a location that was not " + "created by @remix-run/router. This will fail silently in production. " + "You should navigate via the router to avoid this situation (instead of " + "using window.history.pushState/window.location.hash).");
|
|
458
455
|
}
|
|
459
456
|
}
|
|
460
457
|
|
|
@@ -1252,6 +1249,12 @@
|
|
|
1252
1249
|
[key]: this.trackPromise(key, value)
|
|
1253
1250
|
});
|
|
1254
1251
|
}, {});
|
|
1252
|
+
|
|
1253
|
+
if (this.done) {
|
|
1254
|
+
// All incoming values were resolved
|
|
1255
|
+
this.unlistenAbortSignal();
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1255
1258
|
this.init = responseInit;
|
|
1256
1259
|
}
|
|
1257
1260
|
|
|
@@ -1439,11 +1442,11 @@
|
|
|
1439
1442
|
}
|
|
1440
1443
|
/**
|
|
1441
1444
|
* Check if the given error is an ErrorResponse generated from a 4xx/5xx
|
|
1442
|
-
* Response
|
|
1445
|
+
* Response thrown from an action/loader
|
|
1443
1446
|
*/
|
|
1444
1447
|
|
|
1445
|
-
function isRouteErrorResponse(
|
|
1446
|
-
return
|
|
1448
|
+
function isRouteErrorResponse(error) {
|
|
1449
|
+
return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
|
|
1447
1450
|
}
|
|
1448
1451
|
|
|
1449
1452
|
//#region Types and Constants
|
|
@@ -1907,10 +1910,12 @@
|
|
|
1907
1910
|
}
|
|
1908
1911
|
});
|
|
1909
1912
|
return;
|
|
1910
|
-
} // Short circuit if it's only a hash change
|
|
1913
|
+
} // Short circuit if it's only a hash change and not a mutation submission
|
|
1914
|
+
// For example, on /page#hash and submit a <Form method="post"> which will
|
|
1915
|
+
// default to a navigation to /page
|
|
1911
1916
|
|
|
1912
1917
|
|
|
1913
|
-
if (isHashChangeOnly(state.location, location)) {
|
|
1918
|
+
if (isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
|
|
1914
1919
|
completeNavigation(location, {
|
|
1915
1920
|
matches
|
|
1916
1921
|
});
|
|
@@ -2124,9 +2129,8 @@
|
|
|
2124
2129
|
|
|
2125
2130
|
|
|
2126
2131
|
if (!isUninterruptedRevalidation) {
|
|
2127
|
-
revalidatingFetchers.forEach(
|
|
2128
|
-
let
|
|
2129
|
-
let fetcher = state.fetchers.get(key);
|
|
2132
|
+
revalidatingFetchers.forEach(rf => {
|
|
2133
|
+
let fetcher = state.fetchers.get(rf.key);
|
|
2130
2134
|
let revalidatingFetcher = {
|
|
2131
2135
|
state: "loading",
|
|
2132
2136
|
data: fetcher && fetcher.data,
|
|
@@ -2136,7 +2140,7 @@
|
|
|
2136
2140
|
formData: undefined,
|
|
2137
2141
|
" _hasFetcherDoneAnything ": true
|
|
2138
2142
|
};
|
|
2139
|
-
state.fetchers.set(key, revalidatingFetcher);
|
|
2143
|
+
state.fetchers.set(rf.key, revalidatingFetcher);
|
|
2140
2144
|
});
|
|
2141
2145
|
let actionData = pendingActionData || state.actionData;
|
|
2142
2146
|
updateState(_extends({
|
|
@@ -2151,10 +2155,7 @@
|
|
|
2151
2155
|
}
|
|
2152
2156
|
|
|
2153
2157
|
pendingNavigationLoadId = ++incrementingLoadId;
|
|
2154
|
-
revalidatingFetchers.forEach(
|
|
2155
|
-
let [key] = _ref3;
|
|
2156
|
-
return fetchControllers.set(key, pendingNavigationController);
|
|
2157
|
-
});
|
|
2158
|
+
revalidatingFetchers.forEach(rf => fetchControllers.set(rf.key, pendingNavigationController));
|
|
2158
2159
|
let {
|
|
2159
2160
|
results,
|
|
2160
2161
|
loaderResults,
|
|
@@ -2170,10 +2171,7 @@
|
|
|
2170
2171
|
// reassigned to new controllers for the next navigation
|
|
2171
2172
|
|
|
2172
2173
|
|
|
2173
|
-
revalidatingFetchers.forEach(
|
|
2174
|
-
let [key] = _ref4;
|
|
2175
|
-
return fetchControllers.delete(key);
|
|
2176
|
-
}); // If any loaders returned a redirect Response, start a new REPLACE navigation
|
|
2174
|
+
revalidatingFetchers.forEach(rf => fetchControllers.delete(rf.key)); // If any loaders returned a redirect Response, start a new REPLACE navigation
|
|
2177
2175
|
|
|
2178
2176
|
let redirect = findRedirect(results);
|
|
2179
2177
|
|
|
@@ -2237,6 +2235,7 @@
|
|
|
2237
2235
|
submission
|
|
2238
2236
|
} = normalizeNavigateOptions(href, opts, true);
|
|
2239
2237
|
let match = getTargetMatch(matches, path);
|
|
2238
|
+
pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
2240
2239
|
|
|
2241
2240
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
2242
2241
|
handleFetcherAction(key, routeId, path, match, matches, submission);
|
|
@@ -2245,7 +2244,12 @@
|
|
|
2245
2244
|
// revalidations
|
|
2246
2245
|
|
|
2247
2246
|
|
|
2248
|
-
fetchLoadMatches.set(key,
|
|
2247
|
+
fetchLoadMatches.set(key, {
|
|
2248
|
+
routeId,
|
|
2249
|
+
path,
|
|
2250
|
+
match,
|
|
2251
|
+
matches
|
|
2252
|
+
});
|
|
2249
2253
|
handleFetcherLoader(key, routeId, path, match, matches, submission);
|
|
2250
2254
|
} // Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
2251
2255
|
// errors, and revalidation
|
|
@@ -2351,11 +2355,8 @@
|
|
|
2351
2355
|
// current fetcher which we want to keep in it's current loading state which
|
|
2352
2356
|
// contains it's action submission info + action data
|
|
2353
2357
|
|
|
2354
|
-
revalidatingFetchers.filter(
|
|
2355
|
-
let
|
|
2356
|
-
return staleKey !== key;
|
|
2357
|
-
}).forEach(_ref6 => {
|
|
2358
|
-
let [staleKey] = _ref6;
|
|
2358
|
+
revalidatingFetchers.filter(rf => rf.key !== key).forEach(rf => {
|
|
2359
|
+
let staleKey = rf.key;
|
|
2359
2360
|
let existingFetcher = state.fetchers.get(staleKey);
|
|
2360
2361
|
let revalidatingFetcher = {
|
|
2361
2362
|
state: "loading",
|
|
@@ -2384,10 +2385,7 @@
|
|
|
2384
2385
|
|
|
2385
2386
|
fetchReloadIds.delete(key);
|
|
2386
2387
|
fetchControllers.delete(key);
|
|
2387
|
-
revalidatingFetchers.forEach(
|
|
2388
|
-
let [staleKey] = _ref7;
|
|
2389
|
-
return fetchControllers.delete(staleKey);
|
|
2390
|
-
});
|
|
2388
|
+
revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));
|
|
2391
2389
|
let redirect = findRedirect(results);
|
|
2392
2390
|
|
|
2393
2391
|
if (redirect) {
|
|
@@ -2627,16 +2625,10 @@
|
|
|
2627
2625
|
// Call all navigation loaders and revalidating fetcher loaders in parallel,
|
|
2628
2626
|
// then slice off the results into separate arrays so we can handle them
|
|
2629
2627
|
// accordingly
|
|
2630
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(
|
|
2631
|
-
let [, href, match, fetchMatches] = _ref8;
|
|
2632
|
-
return callLoaderOrAction("loader", createClientSideRequest(init.history, href, request.signal), match, fetchMatches, router.basename);
|
|
2633
|
-
})]);
|
|
2628
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename))]);
|
|
2634
2629
|
let loaderResults = results.slice(0, matchesToLoad.length);
|
|
2635
2630
|
let fetcherResults = results.slice(matchesToLoad.length);
|
|
2636
|
-
await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, request.signal, false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(
|
|
2637
|
-
let [,, match] = _ref9;
|
|
2638
|
-
return match;
|
|
2639
|
-
}), fetcherResults, request.signal, true)]);
|
|
2631
|
+
await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, request.signal, false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, request.signal, true)]);
|
|
2640
2632
|
return {
|
|
2641
2633
|
results,
|
|
2642
2634
|
loaderResults,
|
|
@@ -2775,12 +2767,12 @@
|
|
|
2775
2767
|
});
|
|
2776
2768
|
}
|
|
2777
2769
|
|
|
2778
|
-
function shouldBlockNavigation(
|
|
2770
|
+
function shouldBlockNavigation(_ref2) {
|
|
2779
2771
|
let {
|
|
2780
2772
|
currentLocation,
|
|
2781
2773
|
nextLocation,
|
|
2782
2774
|
historyAction
|
|
2783
|
-
} =
|
|
2775
|
+
} = _ref2;
|
|
2784
2776
|
|
|
2785
2777
|
if (activeBlocker == null) {
|
|
2786
2778
|
return;
|
|
@@ -3374,24 +3366,15 @@
|
|
|
3374
3366
|
|
|
3375
3367
|
|
|
3376
3368
|
let parsedPath = parsePath(path);
|
|
3369
|
+
let searchParams = convertFormDataToSearchParams(opts.formData); // Since fetcher GET submissions only run a single loader (as opposed to
|
|
3370
|
+
// navigation GET submissions which run all loaders), we need to preserve
|
|
3371
|
+
// any incoming ?index params
|
|
3377
3372
|
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
// navigation GET submissions which run all loaders), we need to preserve
|
|
3381
|
-
// any incoming ?index params
|
|
3382
|
-
|
|
3383
|
-
if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) {
|
|
3384
|
-
searchParams.append("index", "");
|
|
3385
|
-
}
|
|
3386
|
-
|
|
3387
|
-
parsedPath.search = "?" + searchParams;
|
|
3388
|
-
} catch (e) {
|
|
3389
|
-
return {
|
|
3390
|
-
path,
|
|
3391
|
-
error: getInternalRouterError(400)
|
|
3392
|
-
};
|
|
3373
|
+
if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) {
|
|
3374
|
+
searchParams.append("index", "");
|
|
3393
3375
|
}
|
|
3394
3376
|
|
|
3377
|
+
parsedPath.search = "?" + searchParams;
|
|
3395
3378
|
return {
|
|
3396
3379
|
path: createPath(parsedPath),
|
|
3397
3380
|
submission
|
|
@@ -3415,25 +3398,73 @@
|
|
|
3415
3398
|
}
|
|
3416
3399
|
|
|
3417
3400
|
function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
|
|
3418
|
-
let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;
|
|
3401
|
+
let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;
|
|
3402
|
+
let currentUrl = history.createURL(state.location);
|
|
3403
|
+
let nextUrl = history.createURL(location);
|
|
3404
|
+
let defaultShouldRevalidate = // Forced revalidation due to submission, useRevalidate, or X-Remix-Revalidate
|
|
3405
|
+
isRevalidationRequired || // Clicked the same link, resubmitted a GET form
|
|
3406
|
+
currentUrl.toString() === nextUrl.toString() || // Search params affect all loaders
|
|
3407
|
+
currentUrl.search !== nextUrl.search; // Pick navigation matches that are net-new or qualify for revalidation
|
|
3419
3408
|
|
|
3420
3409
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3421
3410
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3422
|
-
let navigationMatches = boundaryMatches.filter((match, index) =>
|
|
3423
|
-
|
|
3411
|
+
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
3412
|
+
if (match.route.loader == null) {
|
|
3413
|
+
return false;
|
|
3414
|
+
} // Always call the loader on new route instances and pending defer cancellations
|
|
3424
3415
|
|
|
3425
|
-
let revalidatingFetchers = [];
|
|
3426
|
-
fetchLoadMatches && fetchLoadMatches.forEach((_ref11, key) => {
|
|
3427
|
-
let [href, match, fetchMatches] = _ref11;
|
|
3428
3416
|
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3417
|
+
if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {
|
|
3418
|
+
return true;
|
|
3419
|
+
} // This is the default implementation for when we revalidate. If the route
|
|
3420
|
+
// provides it's own implementation, then we give them full control but
|
|
3421
|
+
// provide this value so they can leverage it if needed after they check
|
|
3422
|
+
// their own specific use cases
|
|
3423
|
+
|
|
3424
|
+
|
|
3425
|
+
let currentRouteMatch = state.matches[index];
|
|
3426
|
+
let nextRouteMatch = match;
|
|
3427
|
+
return shouldRevalidateLoader(match, _extends({
|
|
3428
|
+
currentUrl,
|
|
3429
|
+
currentParams: currentRouteMatch.params,
|
|
3430
|
+
nextUrl,
|
|
3431
|
+
nextParams: nextRouteMatch.params
|
|
3432
|
+
}, submission, {
|
|
3433
|
+
actionResult,
|
|
3434
|
+
defaultShouldRevalidate: defaultShouldRevalidate || isNewRouteInstance(currentRouteMatch, nextRouteMatch)
|
|
3435
|
+
}));
|
|
3436
|
+
}); // Pick fetcher.loads that need to be revalidated
|
|
3437
|
+
|
|
3438
|
+
let revalidatingFetchers = [];
|
|
3439
|
+
fetchLoadMatches && fetchLoadMatches.forEach((f, key) => {
|
|
3440
|
+
if (!matches.some(m => m.route.id === f.routeId)) {
|
|
3441
|
+
// This fetcher is not going to be present in the subsequent render so
|
|
3442
|
+
// there's no need to revalidate it
|
|
3443
|
+
return;
|
|
3444
|
+
} else if (cancelledFetcherLoads.includes(key)) {
|
|
3445
|
+
// This fetcher was cancelled from a prior action submission - force reload
|
|
3446
|
+
revalidatingFetchers.push(_extends({
|
|
3447
|
+
key
|
|
3448
|
+
}, f));
|
|
3449
|
+
} else {
|
|
3450
|
+
// Revalidating fetchers are decoupled from the route matches since they
|
|
3451
|
+
// hit a static href, so they _always_ check shouldRevalidate and the
|
|
3452
|
+
// default is strictly if a revalidation is explicitly required (action
|
|
3453
|
+
// submissions, useRevalidator, X-Remix-Revalidate).
|
|
3454
|
+
let shouldRevalidate = shouldRevalidateLoader(f.match, _extends({
|
|
3455
|
+
currentUrl,
|
|
3456
|
+
currentParams: state.matches[state.matches.length - 1].params,
|
|
3457
|
+
nextUrl,
|
|
3458
|
+
nextParams: matches[matches.length - 1].params
|
|
3459
|
+
}, submission, {
|
|
3460
|
+
actionResult,
|
|
3461
|
+
defaultShouldRevalidate
|
|
3462
|
+
}));
|
|
3434
3463
|
|
|
3435
3464
|
if (shouldRevalidate) {
|
|
3436
|
-
revalidatingFetchers.push(
|
|
3465
|
+
revalidatingFetchers.push(_extends({
|
|
3466
|
+
key
|
|
3467
|
+
}, f));
|
|
3437
3468
|
}
|
|
3438
3469
|
}
|
|
3439
3470
|
});
|
|
@@ -3456,43 +3487,20 @@
|
|
|
3456
3487
|
return (// param change for this match, /users/123 -> /users/456
|
|
3457
3488
|
currentMatch.pathname !== match.pathname || // splat param changed, which is not present in match.path
|
|
3458
3489
|
// e.g. /files/images/avatar.jpg -> files/finances.xls
|
|
3459
|
-
currentPath && currentPath.endsWith("*") && currentMatch.params["*"] !== match.params["*"]
|
|
3490
|
+
currentPath != null && currentPath.endsWith("*") && currentMatch.params["*"] !== match.params["*"]
|
|
3460
3491
|
);
|
|
3461
3492
|
}
|
|
3462
3493
|
|
|
3463
|
-
function shouldRevalidateLoader(
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
let nextUrl = history.createURL(location);
|
|
3467
|
-
let nextParams = match.params; // This is the default implementation as to when we revalidate. If the route
|
|
3468
|
-
// provides it's own implementation, then we give them full control but
|
|
3469
|
-
// provide this value so they can leverage it if needed after they check
|
|
3470
|
-
// their own specific use cases
|
|
3471
|
-
// Note that fetchers always provide the same current/next locations so the
|
|
3472
|
-
// URL-based checks here don't apply to fetcher shouldRevalidate calls
|
|
3473
|
-
|
|
3474
|
-
let defaultShouldRevalidate = isNewRouteInstance(currentMatch, match) || // Clicked the same link, resubmitted a GET form
|
|
3475
|
-
currentUrl.toString() === nextUrl.toString() || // Search params affect all loaders
|
|
3476
|
-
currentUrl.search !== nextUrl.search || // Forced revalidation due to submission, useRevalidate, or X-Remix-Revalidate
|
|
3477
|
-
isRevalidationRequired;
|
|
3478
|
-
|
|
3479
|
-
if (match.route.shouldRevalidate) {
|
|
3480
|
-
let routeChoice = match.route.shouldRevalidate(_extends({
|
|
3481
|
-
currentUrl,
|
|
3482
|
-
currentParams,
|
|
3483
|
-
nextUrl,
|
|
3484
|
-
nextParams
|
|
3485
|
-
}, submission, {
|
|
3486
|
-
actionResult,
|
|
3487
|
-
defaultShouldRevalidate
|
|
3488
|
-
}));
|
|
3494
|
+
function shouldRevalidateLoader(loaderMatch, arg) {
|
|
3495
|
+
if (loaderMatch.route.shouldRevalidate) {
|
|
3496
|
+
let routeChoice = loaderMatch.route.shouldRevalidate(arg);
|
|
3489
3497
|
|
|
3490
3498
|
if (typeof routeChoice === "boolean") {
|
|
3491
3499
|
return routeChoice;
|
|
3492
3500
|
}
|
|
3493
3501
|
}
|
|
3494
3502
|
|
|
3495
|
-
return defaultShouldRevalidate;
|
|
3503
|
+
return arg.defaultShouldRevalidate;
|
|
3496
3504
|
}
|
|
3497
3505
|
|
|
3498
3506
|
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
@@ -3667,8 +3675,8 @@
|
|
|
3667
3675
|
let searchParams = new URLSearchParams();
|
|
3668
3676
|
|
|
3669
3677
|
for (let [key, value] of formData.entries()) {
|
|
3670
|
-
|
|
3671
|
-
searchParams.append(key, value);
|
|
3678
|
+
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#converting-an-entry-list-to-a-list-of-name-value-pairs
|
|
3679
|
+
searchParams.append(key, value instanceof File ? value.name : value);
|
|
3672
3680
|
}
|
|
3673
3681
|
|
|
3674
3682
|
return searchParams;
|
|
@@ -3759,7 +3767,10 @@
|
|
|
3759
3767
|
} = processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds); // Process results from our revalidating fetchers
|
|
3760
3768
|
|
|
3761
3769
|
for (let index = 0; index < revalidatingFetchers.length; index++) {
|
|
3762
|
-
let
|
|
3770
|
+
let {
|
|
3771
|
+
key,
|
|
3772
|
+
match
|
|
3773
|
+
} = revalidatingFetchers[index];
|
|
3763
3774
|
invariant(fetcherResults !== undefined && fetcherResults[index] !== undefined, "Did not find corresponding fetcher result");
|
|
3764
3775
|
let result = fetcherResults[index]; // Process fetcher non-redirect errors
|
|
3765
3776
|
|
|
@@ -3865,8 +3876,6 @@
|
|
|
3865
3876
|
errorMessage = "You made a " + method + " request to \"" + pathname + "\" but " + ("did not provide a `loader` for route \"" + routeId + "\", ") + "so there is no way to handle the request.";
|
|
3866
3877
|
} else if (type === "defer-action") {
|
|
3867
3878
|
errorMessage = "defer() is not supported in actions";
|
|
3868
|
-
} else {
|
|
3869
|
-
errorMessage = "Cannot submit binary form data using GET";
|
|
3870
3879
|
}
|
|
3871
3880
|
} else if (status === 403) {
|
|
3872
3881
|
statusText = "Forbidden";
|