UNPKG

@behance/router

Version:

A lightweight JavaScript library is built on top of route-recognizer and rsvp.js to provide an API for handling routes

110 lines (92 loc) 3.88 kB
import { ResolvedHandlerInfo } from './handler-info'; import { forEach, promiseLabel, callHook } from './utils'; import Promise from 'rsvp/promise'; function TransitionState(other) { this.handlerInfos = []; this.queryParams = {}; this.params = {}; } TransitionState.prototype = { handlerInfos: null, queryParams: null, params: null, promiseLabel: function(label) { var targetName = ''; forEach(this.handlerInfos, function(handlerInfo) { if (targetName !== '') { targetName += '.'; } targetName += handlerInfo.name; }); return promiseLabel("'" + targetName + "': " + label); }, resolve: function(shouldContinue, payload) { var self = this; // First, calculate params for this state. This is useful // information to provide to the various route hooks. var params = this.params; forEach(this.handlerInfos, function(handlerInfo) { params[handlerInfo.name] = handlerInfo.params || {}; }); payload = payload || {}; payload.resolveIndex = 0; var currentState = this; var wasAborted = false; // The prelude RSVP.resolve() asyncs us into the promise land. return Promise.resolve(null, this.promiseLabel("Start transition")) .then(resolveOneHandlerInfo, null, this.promiseLabel('Resolve handler'))['catch'](handleError, this.promiseLabel('Handle error')); function innerShouldContinue() { return Promise.resolve(shouldContinue(), currentState.promiseLabel("Check if should continue"))['catch'](function(reason) { // We distinguish between errors that occurred // during resolution (e.g. beforeModel/model/afterModel), // and aborts due to a rejecting promise from shouldContinue(). wasAborted = true; return Promise.reject(reason); }, currentState.promiseLabel("Handle abort")); } function handleError(error) { // This is the only possible // reject value of TransitionState#resolve var handlerInfos = currentState.handlerInfos; var errorHandlerIndex = payload.resolveIndex >= handlerInfos.length ? handlerInfos.length - 1 : payload.resolveIndex; return Promise.reject({ error: error, handlerWithError: currentState.handlerInfos[errorHandlerIndex].handler, wasAborted: wasAborted, state: currentState }); } function proceed(resolvedHandlerInfo) { var wasAlreadyResolved = currentState.handlerInfos[payload.resolveIndex].isResolved; // Swap the previously unresolved handlerInfo with // the resolved handlerInfo currentState.handlerInfos[payload.resolveIndex++] = resolvedHandlerInfo; if (!wasAlreadyResolved) { // Call the redirect hook. The reason we call it here // vs. afterModel is so that redirects into child // routes don't re-run the model hooks for this // already-resolved route. var handler = resolvedHandlerInfo.handler; callHook(handler, 'redirect', resolvedHandlerInfo.context, payload); } // Proceed after ensuring that the redirect hook // didn't abort this transition by transitioning elsewhere. return innerShouldContinue().then(resolveOneHandlerInfo, null, currentState.promiseLabel('Resolve handler')); } function resolveOneHandlerInfo() { if (payload.resolveIndex === currentState.handlerInfos.length) { // This is is the only possible // fulfill value of TransitionState#resolve return { error: null, state: currentState }; } var handlerInfo = currentState.handlerInfos[payload.resolveIndex]; return handlerInfo.resolve(innerShouldContinue, payload) .then(proceed, null, currentState.promiseLabel('Proceed')); } } }; export default TransitionState;