react-crossroads
Version:
Client side router for web applications built with React and utilizing the Flux architecture. The backing routing engine is CrossroadsJs.
203 lines (173 loc) • 6.58 kB
JavaScript
var BLOCKED, EventEmitter, LocationStore, Logger, RouterConstants, UNBLOCKED, _, _isExpectedEvent,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__slice = [].slice;
RouterConstants = require('../constants/RouterConstants');
Logger = require('../utils/logger');
EventEmitter = require('events').EventEmitter;
_ = require('lodash');
BLOCKED = true;
UNBLOCKED = false;
_isExpectedEvent = function(action, path) {
if (action.actionType === RouterConstants.LOCATION_GOBACK) {
return true;
} else {
return path === action.path;
}
};
LocationStore = (function(_super) {
__extends(LocationStore, _super);
function LocationStore(dispatcher, context) {
this.context = context;
this._locationChanged = __bind(this._locationChanged, this);
this.handler = __bind(this.handler, this);
this.dispatchToken = dispatcher.register(this.handler);
this._become(UNBLOCKED);
this._currentLocationChanged = this._locationChangedDefault;
this._queue = [];
this._changing = false;
this._blockedIds = {};
}
LocationStore.prototype.isBlocked = function() {
return _.any(_.values(this._blockedIds));
};
LocationStore.prototype.isIdBlocked = function(id) {
return this._blockedIds[id] || false;
};
LocationStore.prototype.getCurrentPath = function() {
return this._location.getCurrentPath();
};
LocationStore.prototype.pathToHref = function(path) {
return this._location.pathToHref(path);
};
LocationStore.prototype.handler = function(payload) {
var action;
if (payload.source !== RouterConstants.ROUTER_ACTION) {
return;
}
action = payload.action;
this._queue.push(action);
if (!this._changing) {
return this._processQueue();
}
};
LocationStore.prototype._blockedHandler = function(action) {
switch (action.actionType) {
case RouterConstants.LOCATION_BLOCK:
Logger.development.warn('Location store is already blocked');
break;
case RouterConstants.LOCATION_UNBLOCK:
if (this._blockedIds[action.blockId] != null) {
delete this._blockedIds[action.blockId];
}
if (!this.isBlocked()) {
this._become(UNBLOCKED);
}
this._emitChange();
break;
case RouterConstants.LOCATION_CHANGE:
case RouterConstants.LOCATION_REPLACE:
case RouterConstants.LOCATION_GOBACK:
Logger.development.warn("Location store is blocked: " + (JSON.stringify(action)));
}
return true;
};
LocationStore.prototype._unblockedHandler = function(action) {
switch (action.actionType) {
case RouterConstants.LOCATION_BLOCK:
if (this._blockedIds[action.blockId]) {
console.error("Router has already been blocked for id `" + action.blockId + "`");
}
this._blockedIds[action.blockId] = true;
this._become(BLOCKED);
this._emitChange();
return true;
case RouterConstants.LOCATION_UNBLOCK:
Logger.development.warn('Location store is already unblocked');
return true;
case RouterConstants.LOCATION_CHANGE:
if (action.fromLocationEvent) {
this._emitChange();
return true;
} else {
this._changeLocation((function(_this) {
return function() {
return _this._location.push(action.path);
};
})(this));
return false;
}
break;
case RouterConstants.LOCATION_REPLACE:
this._changeLocation((function(_this) {
return function() {
return _this._location.replace(action.path);
};
})(this));
return false;
case RouterConstants.LOCATION_GOBACK:
this._changeLocation((function(_this) {
return function() {
return _this._location.pop();
};
})(this));
return false;
case RouterConstants.LOCATION_ATTEMPT:
throw new Error('Location store is not blocked!');
}
};
LocationStore.prototype._processQueue = function() {
var action, queueTail, _ref;
_ref = this._queue, action = _ref[0], queueTail = 2 <= _ref.length ? __slice.call(_ref, 1) : [];
if (action && this._currentHandler(action)) {
this._queue = queueTail;
return this._processQueue();
}
};
LocationStore.prototype._changeLocation = function(func) {
this._changing = true;
this._currentLocationChanged = this._locationChangedExpected;
return func();
};
LocationStore.prototype._become = function(blocked) {
return this._currentHandler = blocked ? this._blockedHandler : this._unblockedHandler;
};
LocationStore.prototype._emitChange = function() {
return this.emit(RouterConstants.CHANGE_EVENT);
};
LocationStore.prototype._locationChanged = function() {
return this._currentLocationChanged(this._location.getCurrentPath());
};
LocationStore.prototype._locationChangedExpected = function(path) {
if (!_isExpectedEvent(this._queue[0], path)) {
return this._locationChangedDefault(path);
} else {
Logger.debug.log("Location changed by react-crossroads [path: " + path + "]");
this._changing = false;
this._emitChange();
this._queue.shift();
this._currentLocationChanged = this._locationChangedDefault;
return this._processQueue();
}
};
LocationStore.prototype._locationChangedDefault = function(path) {
Logger.debug.log("Location changed by user [path: " + path + "]");
return this.context.actions.updateLocation(path);
};
LocationStore.prototype.setup = function(location, initialPath) {
if (this._location != null) {
Location.development.warn('Location has already been setup');
}
this._location = location;
return this._location.setup(this._locationChanged, initialPath);
};
LocationStore.prototype.addChangeListener = function(listener) {
return this.on(RouterConstants.CHANGE_EVENT, listener);
};
LocationStore.prototype.removeChangeListener = function(listener) {
return this.removeListener(RouterConstants.CHANGE_EVENT, listener);
};
return LocationStore;
})(EventEmitter);
module.exports = LocationStore;