221 lines · 5.5 KB · Text
RawDownload
### * Based on github.com/visionmedia/page.js * Licensed under the MIT license * Copyright 2012 TJ Holowaychuk <tj@vision-media.ca>### running = falsecurrentState = nullcallbacks = [] @page = (value, fn) ->  if typeof value is 'function'    page '*', value  else if typeof fn is 'function'    route = new Route(value)    callbacks.push route.middleware(fn)  else if typeof value is 'string'    page.show(value, fn)  else    page.start(value)  return page.start = (options = {}) ->  unless running    running = true    addEventListener 'popstate', onpopstate    addEventListener 'click', onclick    page.replace currentPath(), null, null, true  return page.stop = ->  if running    running = false    removeEventListener 'click', onclick    removeEventListener 'popstate', onpopstate  return page.show = (path, state) ->  return if path is currentState?.path  context = new Context(path, state)  previousState = currentState  currentState = context.state  if res = page.dispatch(context)    currentState = previousState    location.assign(res)  else    context.pushState()    updateCanonicalLink()    track()  context page.replace = (path, state, skipDispatch, init) ->  context = new Context(path, state or currentState)  context.init = init  currentState = context.state  result = page.dispatch(context) unless skipDispatch  if result    context = new Context(result)    context.init = init    currentState = context.state    page.dispatch(context)  context.replaceState()  updateCanonicalLink()  track() unless skipDispatch  context page.dispatch = (context) ->  i = 0  next = ->    res = fn(context, next) if fn = callbacks[i++]    return res  return next() page.canGoBack = ->  not Context.isIntialState(currentState) page.canGoForward = ->  not Context.isLastState(currentState) currentPath = ->  location.pathname + location.search + location.hash class Context  @initialPath: currentPath()  @sessionId: Date.now()  @stateId: 0   @isIntialState: (state) ->    state.id == 0   @isLastState: (state) ->    state.id == @stateId - 1   @isInitialPopState: (state) ->    state.path is @initialPath and @stateId is 1   @isSameSession: (state) ->    state.sessionId is @sessionId   constructor: (@path = '/', @state = {}) ->    @pathname = @path.replace /(?:\?([^#]*))?(?:#(.*))?$/, (_, query, hash) =>      @query = query      @hash = hash      ''     @state.id ?= @constructor.stateId++    @state.sessionId ?= @constructor.sessionId    @state.path = @path   pushState: ->    history.pushState @state, '', @path    return   replaceState: ->    try history.replaceState @state, '', @path # NS_ERROR_FAILURE in Firefox    return class Route  constructor: (@path, options = {}) ->    @keys = []    @regexp = pathtoRegexp @path, @keys   middleware: (fn) ->    (context, next) =>      if @match context.pathname, params = []        context.params = params        return fn(context, next)      else        return next()   match: (path, params) ->    return unless matchData = @regexp.exec(path)     for value, i in matchData[1..]      value = decodeURIComponent value if typeof value is 'string'      if key = @keys[i]        params[key.name] = value      else        params.push value    true pathtoRegexp = (path, keys) ->  return path if path instanceof RegExp   path = "(#{path.join '|'})" if path instanceof Array  path = path    .replace /\/\(/g, '(?:/'    .replace /(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, (_, slash = '', format = '', key, capture, optional) ->      keys.push name: key, optional: !!optional      str = if optional then '' else slash      str += '(?:'      str += slash if optional      str += format      str += capture or if format then '([^/.]+?)' else '([^/]+?)'      str += ')'      str += optional if optional      str    .replace /([\/.])/g, '\\$1'    .replace /\*/g, '(.*)'   new RegExp "^#{path}$" onpopstate = (event) ->  return if not event.state or Context.isInitialPopState(event.state)   if Context.isSameSession(event.state)    page.replace(event.state.path, event.state)  else    location.reload()  return onclick = (event) ->  try    return if event.which isnt 1 or event.metaKey or event.ctrlKey or event.shiftKey or event.defaultPrevented  catch    return   link = $.eventTarget(event)  link = link.parentNode while link and link.tagName isnt 'A'   if link and not link.target and isSameOrigin(link.href)    event.preventDefault()    path = link.pathname + link.search + link.hash    path = path.replace /^\/\/+/, '/' # IE11 bug    page.show(path)  return isSameOrigin = (url) ->  url.indexOf("#{location.protocol}//#{location.hostname}") is 0 updateCanonicalLink = ->  @canonicalLink ||= document.head.querySelector('link[rel="canonical"]')  @canonicalLink.setAttribute('href', "https://#{location.host}#{location.pathname}") trackers = [] page.track = (fn) ->  trackers.push(fn)  return track = ->  return unless app.config.env == 'production'   consentGiven = Cookies.get('analyticsConsent')  consentAsked = Cookies.get('analyticsConsentAsked')   if consentGiven == '1'    tracker.call() for tracker in trackers  else if consentGiven == undefined and consentAsked == undefined    # Only ask for consent once per browser session    Cookies.set('analyticsConsentAsked', '1')     new app.views.Notif 'AnalyticsConsent', autoHide: null  return @resetAnalytics = ->  for cookie in document.cookie.split(/;\s?/)    name = cookie.split('=')[0]    if name[0] == '_' && name[1] != '_'      Cookies.expire(name)  return