UNPKG

the-tramp

Version:

Chaplin.js on Node.js. Use it for performance boosts, SEO, re-usability.

184 lines (151 loc) 5.73 kB
_ = require 'underscore' Backbone = require 'backbone' Chaplin = require 'chaplin' Controller = Chaplin.Controller module.exports = class RouteGenerator # Taken from Backbone.Router. escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g # Create a route for a URL pattern and a controller action # e.g. new Route '/users/:id', 'users', 'show', { some: 'options' } constructor: (@pattern, @controller, @action, options) -> # Disallow regexp routes. if _.isRegExp @pattern throw new Error 'Route: RegExps are not supported. Use strings with :names and `constraints` option of route' # Clone options. @options = if options then _.clone(options) else {} # Store the name on the route if given @name = @options.name if @options.name? # Don’t allow ambiguity with controller#action. if @name and @name.indexOf('#') isnt -1 throw new Error 'Route: "#" cannot be used in name' # Set default route name. @name ?= @controller + '#' + @action # Initialize list of :params which the route will use. @paramNames = [] # Check if the action is a reserved name if _.has Controller.prototype, @action throw new Error 'Route: You should not use existing controller ' + 'properties as action names' @createRegExp() # You’re frozen when your heart’s not open. #Object.freeze? this # Tests if route params are equal to criteria. matches: (criteria) -> if typeof criteria is 'string' criteria is @name else for name in ['name', 'action', 'controller'] property = criteria[name] return false if property and property isnt this[name] true # Generates route URL from params. reverse: (params) -> url = @pattern if _.isArray params # Ensure we have enough parameters. return false if params.length < @paramNames.length index = 0 url = url.replace /[:*][^\/\?]+/g, (match) -> result = params[index] index += 1 result else # From a params hash; we need to be able to return # the actual URL this route represents # Iterate and attempt to replace params in pattern for name in @paramNames value = params[name] return false if value is undefined url = url.replace ///[:*]#{name}///g, value # If the url tests out good; return the url; else, false. if @test url then url else false # Creates the actual regular expression that Backbone.History#loadUrl # uses to determine if the current url is a match. createRegExp: -> pattern = @pattern # Escape magic characters. .replace(escapeRegExp, '\\$&') # Replace named parameters, collecting their names. .replace(/(?::|\*)(\w+)/g, @addParamName) # Create the actual regular expression, match until the end of the URL or # the begin of query string. @regExp = ///^#{pattern}(?=\?|$)/// addParamName: (match, paramName) => # Save parameter name. @paramNames.push paramName # Replace with a character class. if match.charAt(0) is ':' # Regexp for :foo. '([^\/\?]+)' else # Regexp for *foo. '(.*?)' # Test if the route matches to a path (called by Backbone.History#loadUrl). test: (path) -> # Test the main RegExp. matched = @regExp.test path return false unless matched # Apply the parameter constraints. constraints = @options.constraints if constraints params = @extractParams path for own name, constraint of constraints return false unless constraint.test(params[name]) return true # The handler called by Backbone.History when the route matches. # It is also called by Router#route which might pass options. handler: (path, options) => options = if options then _.clone(options) else {} # If no query string was passed, use the current. query = options.query ? @getCurrentQuery() # Build params hash. params = @buildParams path, query # Construct a route object to forward to the match event. route = {path, @action, @controller, @name, query} # Returns the query string for the current document. getCurrentQuery: '' # Create a proper Rails-like params hash, not an array like Backbone. buildParams: (path, query) -> _.extend {}, # Add params from query string. @extractQueryParams(query), # Add named params from pattern matches. @extractParams(path), # Add additional params from options as they might # overwrite params extracted from URL. @options.params # Extract named parameters from the URL path. extractParams: (path) -> params = {} # Apply the regular expression. matches = @regExp.exec path # Fill the hash using the paramNames and the matches. for match, index in matches.slice(1) paramName = if @paramNames.length then @paramNames[index] else index params[paramName] = match params # Extract parameters from the query string. extractQueryParams: (query) -> params = {} return params unless query pairs = query.split '&' for pair in pairs continue unless pair.length [field, value] = pair.split '=' continue unless field.length field = decodeURIComponent field value = decodeURIComponent value current = params[field] if current # Handle multiple params with same name: # Aggregate them in an array. if current.push # Add the existing array. current.push value else # Create a new array. params[field] = [current, value] else params[field] = value params