Skip to content

Instantly share code, notes, and snippets.

@midned
Last active December 13, 2015 20:28
Show Gist options
  • Save midned/4969899 to your computer and use it in GitHub Desktop.
Save midned/4969899 to your computer and use it in GitHub Desktop.
Handle the routes in Javascript using HTML5 pushState (uses Zepto.js and https://github.com/balupton/History.js)
# Create the router that will handle routes to 'site.com/app'
router = new Routy.Router 'app'
# also you can:
# router = new Routy.Router 'app', 'a.routy-link'
# to only listen clicks to links with the "routy-link" class
# or:
# router = new Routy.Router 'app', null, '#container'
# to listen clicks for every link inside the element with the "container" id
# this will handle 'app/' and 'app/home'
# and log 'Home'
router.register '/, home', ->
console.log 'Home'
# this will handle 'app/users' and 'app/users/list'
# and log 'List of users'
router.register 'users, users/list', ->
console.log 'List of users'
# TODO: add support to wildcards in the routes
router.register 'users/:id', (id)->
console.log 'Viewing user #', id
window.Routy or = {}
History = window.History;
# The router, used to manage and store the actions
class Routy.Router
# List of registered actions
actions: []
# Generate the router to a specific routing context
# @state_changers_selector: String containing the selector of the elements that will trigger the pushState event
# default: 'a' (all links)
# context_selector: String containing the selector of the container of the elements that will trigger the pushState event
# default: 'document' (will search inside the document)
constructor: (context, @state_changers_selector, context_selector)->
# Let's clean the context variable
context or = ''
if context != ''
if context[0] == '/'
context = context.substr 1
if context.substr(-1) == '/'
context = context.substr 0, context.length-1
if context == ''
context = '/'
@context = context
@state_changers_selector or = 'a'
context_selector or = document
@context_selector = $ context_selector
console.log 'Initialized'
if History.enabled
@attach()
# Apply the current context to the specified url
apply_context: (url)->
if url != ''
if url[0] != '/'
url = @context + '/' + url
else
url = @context + url
if url.substr(-1) != '/'
url = url + '/'
else
url = @context + '/'
url
# Listen for clicks in the elements that will trigger the pushState event
attach: ->
# 'cause "this" will be replaced with the clicked element
router = @
@context_selector.on 'click', @state_changers_selector, (e)->
e.preventDefault()
router.go @href, @title
# Create an anonymous function to call the router.run method so we can
# pass the router as "this" variable
History.Adapter.bind window, 'statechange', ->
router.run.apply router
# Redirect (using pushState) to a specific page
go: (url, title, data)->
History.pushState data or {}, title or document.title, url
# Register a new action
register: (route, callback)->
@actions.push new Routy.Action route, callback, @
run: ->
uri = window.location.pathname
if uri != ''
if uri[0] == '/'
uri = uri.substr 1
if uri.substr(-1) != '/'
uri = uri + '/'
for action in @actions
for route in action.route
if @matches route, uri
return action.call()
# Checks if the route matches with the current uri
# TODO: return an object with the result of the comparation and the arguments to pass to the action
matches: (route, uri)->
route == uri
# Class representing an action
class Routy.Action
# Routes this action with handle
route: []
# The callback to execute
callback: null
# Callback to execute before the action
before_callback: null
# Callback to execute after the action
after_callback: null
# Condition to execute the action
condition: null
# Create a new action
constructor: (routes, @callback, @router)->
# so you can call it like: new Routy.Action(['/', 'home'], callback)
# or: new Routy.Action('/, home', callback);
routes = routes.split ', ' if typeof routes == 'string'
arr = []
for route in routes
route = @router.apply_context route
arr.push route
@route = arr
console.log @route
# Call the action passing arguments to it
call: (args)->
# for now we can call the action
result = true
# call the condition
if @condition
result = @condition.apply @, args
# if it returned false when can't call the action
false if ! result
# if we defined a callback to execute before the action
if @before_callback
# execute it passing the same arguments as the action
contents = @before_callback.apply @, args
# and if it returned some contents use it as
# the main action (then we can use "before_callback" to use route filters)
return contents if contents
# call the action callback and fetch the contents of it
contents = @callback.apply @, args
# if we defined some callback to execute after the main one
if @after_callback
# call it passing the returned content
@after_callback.apply @, [contents]
# Set a callback to execute before the action
before: (@before_callback)->
@
# Set a callback to execute after the action
after: (@after_callback)->
@
# Set a required condition to execute the action
when: (@condition)->
@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment