UNPKG

kdf

Version:

KD: a non-document focused UI Framework for web applications.

178 lines (133 loc) 5.32 kB
KDObject = require './object.coffee' KDNotificationView = require './../components/notifications/notificationview.coffee' module.exports = class KDRouter extends KDObject {history} = window listenerKey = 'ಠ_ಠ' @registerStaticEmitter() createObjectRef =(obj)-> return unless obj?.bongo_? and obj.getId? constructorName : obj.bongo_?.constructorName id : obj.getId() revive =(objRef, callback)-> unless objRef?.constructorName? and objRef.id? then callback null else KD.remote.cacheable objRef.constructorName, objRef.id, callback constructor:(routes)-> super() @tree = {} # this is the tree for quick lookups @routes = {} # this is the flat namespace containing all routes @visitedRoutes = [] @addRoutes routes if routes KD.utils.defer => KDRouter.emit 'RouterIsReady', this listen:-> # this handles the case that the url is an "old-style" hash fragment hack. if location.hash.length hashFragment = location.hash.substr 1 @userRoute = hashFragment @utils.defer => @handleRoute hashFragment, shouldPushState : yes replaceState : yes @startListening() popState:(event)-> revive event.state, (err, state)=> return KD.showError err if err @handleRoute "#{location.pathname}#{location.search}", shouldPushState : no state : state clear:(route = '/', replaceState = yes)-> delete @userRoute # TODO: i hope deleting the userRoute here doesn't break anything... C.T. @handleRoute route, {replaceState} back:-> if @visitedRoutes.length <= 1 then @clear() else history.back() startListening:-> return no if @isListening # make this action idempotent @isListening = yes # we need to add a listener to the window's popstate event: window.addEventListener 'popstate', @bound "popState" return yes stopListening:-> return no unless @isListening # make this action idempotent @isListening = no # we need to remove the listener from the window's popstate event: window.removeEventListener 'popstate', @bound "popState" return yes @handleNotFound =(route)-> console.trace() log "The route #{ Encoder.XSSEncode route } was not found!" getCurrentPath:-> @currentPath handleNotFound:(route)-> message = if /<|>/.test route then "Invalid route!" else"404 Not found! #{ Encoder.XSSEncode route }" delete @userRoute @clear() log "The route #{route} was not found!" new KDNotificationView title: message routeWithoutEdgeAtIndex =(route, i)-> "/#{route.slice(0, i).concat(route.slice i + 1).join '/'}" addRoute:(route, listener)-> @routes[route] = listener node = @tree route = route.split '/' route.shift() # first edge is garbage like '' or '#!' for edge, i in route last = edge.length - 1 if '?' is edge.charAt last # then this is an "optional edge". # recursively alias this route without this optional edge: @addRoute routeWithoutEdgeAtIndex(route, i), listener edge = edge.substr 0, last # get rid of the "?" from the route if /^:/.test edge node[':'] or= name: edge.substr 1 node = node[':'] else node[edge] or= {} node = node[edge] node[listenerKey] or= [] node[listenerKey].push listener unless listener in node[listenerKey] addRoutes:(routes)-> @addRoute route, listener for own route, listener of routes handleRoute:(userRoute, options={})-> userRoute = userRoute.slice 1 if (userRoute.indexOf '!') is 0 @visitedRoutes.push userRoute if @visitedRoutes.last isnt userRoute [frags, query...] = (userRoute ? @getDefaultRoute?() ? '/').split '?' query = @utils.parseQuery query.join '&' {shouldPushState, replaceState, state, suppressListeners} = options shouldPushState ?= yes objRef = createObjectRef state node = @tree params = {} frags = frags.split '/' frags.shift() # first edge is garbage like '' or '#!' frags = frags.filter Boolean path = "/#{frags.join '/'}" qs = @utils.stringifyQuery query path += "?#{qs}" if qs.length notFound = no for edge in frags if node[edge] node = node[edge] else param = node[':'] if param? params[param.name] = edge node = param else notFound = yes if not suppressListeners and shouldPushState and not replaceState and path is @currentPath @emit 'AlreadyHere', path, { params, frags } return @handleNotFound frags.join '/' if notFound @currentPath = path if shouldPushState method = if replaceState then 'replaceState' else 'pushState' history[method] objRef, path, path routeInfo = {params, query} @emit 'RouteInfoHandled', {params, query, path} unless suppressListeners listeners = node[listenerKey] if listeners?.length listener.call this, routeInfo, state, path for listener in listeners return this handleQuery:(query)-> query = @utils.stringifyQuery query unless 'string' is typeof query return unless query.length nextRoute = "#{@currentPath}?#{query}" @handleRoute nextRoute