react-crossroads
Version:
Client side router for web applications built with React and utilizing the Flux architecture. The backing routing engine is CrossroadsJs.
132 lines (106 loc) • 4.25 kB
text/coffeescript
RouterConstants = require '../constants/RouterConstants'
Logger = require '../utils/logger'
{EventEmitter} = require 'events'
_ = require 'lodash'
BLOCKED=true
UNBLOCKED=false
_isExpectedEvent = (action, path) ->
if action.actionType == RouterConstants.LOCATION_GOBACK
# TODO: Figure out something better here, potential for incredibly RARE race condition
true
else
path == action.path
class LocationStore extends EventEmitter
constructor: (dispatcher, @context) ->
@dispatchToken = dispatcher.register(@handler)
@_become UNBLOCKED
@_currentLocationChanged = @_locationChangedDefault
@_queue = []
@_changing = false
@_blockedIds = {}
isBlocked: -> _.any _.values(@_blockedIds)
isIdBlocked: (id) -> @_blockedIds[id] || false
getCurrentPath: -> @_location.getCurrentPath()
pathToHref: (path) -> @_location.pathToHref path
handler: (payload) =>
return unless payload.source == RouterConstants.ROUTER_ACTION
action = payload.action
@_queue.push action
@_processQueue() unless @_changing
_blockedHandler: (action) ->
switch action.actionType
when RouterConstants.LOCATION_BLOCK
Logger.development.warn 'Location store is already blocked'
when RouterConstants.LOCATION_UNBLOCK
delete @_blockedIds[action.blockId] if @_blockedIds[action.blockId]?
@_become UNBLOCKED unless @isBlocked()
@_emitChange()
when RouterConstants.LOCATION_CHANGE, RouterConstants.LOCATION_REPLACE, RouterConstants.LOCATION_GOBACK
Logger.development.warn "Location store is blocked: #{JSON.stringify action}"
true
_unblockedHandler: (action) ->
switch action.actionType
when RouterConstants.LOCATION_BLOCK
if @_blockedIds[action.blockId]
console.error "Router has already been blocked for id `#{action.blockId}`"
@_blockedIds[action.blockId] = true
@_become BLOCKED
@_emitChange()
true
when RouterConstants.LOCATION_UNBLOCK
Logger.development.warn 'Location store is already unblocked'
true
when RouterConstants.LOCATION_CHANGE
if action.fromLocationEvent
@_emitChange()
true
else
@_changeLocation => @_location.push action.path
false
when RouterConstants.LOCATION_REPLACE
@_changeLocation => @_location.replace action.path
false
when RouterConstants.LOCATION_GOBACK
@_changeLocation => @_location.pop()
false
when RouterConstants.LOCATION_ATTEMPT
throw new Error 'Location store is not blocked!'
_processQueue: ->
[action, queueTail...] = @_queue
if action and @_currentHandler(action)
@_queue = queueTail
@_processQueue()
_changeLocation: (func) ->
@_changing = true
@_currentLocationChanged = @_locationChangedExpected
func()
_become: (blocked) ->
@_currentHandler = if blocked then @_blockedHandler else @_unblockedHandler
_emitChange: -> @emit RouterConstants.CHANGE_EVENT
###################### Listening to Location ######################
_locationChanged: =>
@_currentLocationChanged @_location.getCurrentPath()
_locationChangedExpected: (path) ->
if !_isExpectedEvent(@_queue[0], path)
@_locationChangedDefault(path)
else
Logger.debug.log "Location changed by react-crossroads [path: #{path}]"
@_changing = false
@_emitChange()
@_queue.shift()
@_currentLocationChanged = @_locationChangedDefault
@_processQueue()
_locationChangedDefault: (path) ->
# TODO: if blocked goback, but still send transition attempt event
Logger.debug.log "Location changed by user [path: #{path}]"
@context.actions.updateLocation path
setup: (location, initialPath) ->
Location.development.warn 'Location has already been setup' if @_location?
@_location = location
@_location.setup @_locationChanged, initialPath
###################### End Listening to Location ######################
addChangeListener: (listener) ->
@on RouterConstants.CHANGE_EVENT, listener
removeChangeListener: (listener) ->
@removeListener RouterConstants.CHANGE_EVENT, listener
module.exports = LocationStore