<commit_before>
var EventEmitter = require('events').EventEmitter,
	Router = require('routr'),
	Q = require('q');

class Navigator extends EventEmitter {

	constructor (context, routes, applicationStore) {
		this.router = new Router(routes);
		this.context = context;
		
		this._loading = false;
		this._currentRoute = null;
	}

	navigate (request, type) {

		var route = this.router.getRoute(request.getUrl(), {navigate: {path:request.getUrl()}});
		if (!route) {
			setTimeout( () => {
				this.emit('navigateDone', { status: 404, message: "No Route!" });
			}, 0);
			return;
		}

		this.startRoute(route);
		this.emit('navigateStart', route);

		/* Breathe... */

		route.config.page().done( pageConstructor => {
			if (request.setRoute) {
				request.setRoute(route);
			}
			// instantiate the page we need to fulfill this request.
			var page = new pageConstructor();

			// call page.handleRoute(), and use the resulting code to decide how to 
			// respond. -sra.
			// note that handleRoute can return a handleRouteResult or a Promise of handleRouteResult. using
			// Q() to normalize that and make it always be a Promise of handleRouteResult. -sra.
			var handleRouteValueOrPromise = page.handleRoute ? page.handleRoute(request, this.context.loader) : {code: 200};
			Q(handleRouteValueOrPromise).then(handleRouteResult => {
				// TODO: I think that 3xx/4xx/5xx shouldn't be considered "errors" in navigateDone, but that's
				// how the code is structured right now, and I'm changing too many things at once at the moment. -sra.
				if (handleRouteResult.code && handleRouteResult.code / 100 !== 2) {
					this.emit("navigateDone", {status: handleRouteResult.code, redirectUrl: handleRouteResult.location});
				}
				if (handleRouteResult.page) {
					// TODO: deal with returning a new Page object in handleRouteResult -sra.
				}

				this.finishRoute(route);
				this.emit('navigateDone', null, page);
			}).catch(err => {
				console.error("Error while handling route.", err);
			});
		}, err => {
			console.error("Error resolving page", err);
		});

	}

	getState () {
		return {
			loading: this._loading,
			route: this._currentRoute
		}
	}

	getCurrentRoute () {
		return this._currentRoute;
	}

	getLoading () {
		return this._loading;
	}

	startRoute (route) {
		this._loading = true;
		this._currentRoute = route;
	}

	finishRoute (route) {
		this._loading = false;
	}

}

module.exports = Navigator;<commit_msg>Triton API refactor part N: added forwarding of requests (without changing the URL) to both client and server sides.<commit_after>
var EventEmitter = require('events').EventEmitter,
	Router = require('routr'),
	Q = require('q');

class Navigator extends EventEmitter {

	constructor (context, routes, applicationStore) {
		this.router = new Router(routes);
		this.context = context;
		
		this._loading = false;
		this._currentRoute = null;
	}

	navigate (request, type) {

		var route = this.router.getRoute(request.getUrl(), {navigate: {path:request.getUrl()}});
		if (!route) {
			setTimeout( () => {
				this.emit('navigateDone', { status: 404, message: "No Route!" });
			}, 0);
			return;
		}

		this.startRoute(route);
		this.emit('navigateStart', route);

		/* Breathe... */

		route.config.page().done( pageConstructor => {
			if (request.setRoute) {
				request.setRoute(route);
			}
			this.handlePage(pageConstructor, request, this.context.loader);
		}, err => {
			console.error("Error resolving page", err);
		});

	}

	handlePage(pageConstructor, request, loader) {
		// instantiate the page we need to fulfill this request.
		var page = new pageConstructor();

		// call page.handleRoute(), and use the resulting code to decide how to 
		// respond. -sra.
		// note that handleRoute can return a handleRouteResult or a Promise of handleRouteResult. using
		// Q() to normalize that and make it always be a Promise of handleRouteResult. -sra.
		var handleRouteValueOrPromise = page.handleRoute ? page.handleRoute(request, loader) : {code: 200};
		Q(handleRouteValueOrPromise).then(handleRouteResult => {
			// TODO: I think that 3xx/4xx/5xx shouldn't be considered "errors" in navigateDone, but that's
			// how the code is structured right now, and I'm changing too many things at once at the moment. -sra.
			if (handleRouteResult.code && handleRouteResult.code / 100 !== 2) {
				this.emit("navigateDone", {status: handleRouteResult.code, redirectUrl: handleRouteResult.location});
			}
			if (handleRouteResult.page) {
				// in this case, we should forward to a new page *without* changing the URL. Since we are already
				// in an async callback, we should schedule a new handlePage with the new page constructor and return
				// from this call.
				setTimeout(() => this.handlePage(handleRouteResult.page, request, loader), 0);
				return;
			}

			this.finishRoute();
			this.emit('navigateDone', null, page);
		}).catch(err => {
			console.error("Error while handling route.", err);
		});

	}

	getState () {
		return {
			loading: this._loading,
			route: this._currentRoute
		}
	}

	getCurrentRoute () {
		return this._currentRoute;
	}

	getLoading () {
		return this._loading;
	}

	startRoute (route) {
		this._loading = true;
		this._currentRoute = route;
	}

	finishRoute () {
		this._loading = false;
	}

}

module.exports = Navigator;
{'fraction_non_alphanumeric': 0.129688972667295, 'fraction_numerical': 0.005466540999057493, 'mean_word_length': 3.546700942587832, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4520846', 'n_tokens_mistral': 1853, 'n_tokens_neox': 1596, 'n_words': 471}