(function(global) { var define, requireModule, require, requirejs; (function() { var _isArray; if (!Array.isArray) { _isArray = function (x) { return Object.prototype.toString.call(x) === "[object Array]"; }; } else { _isArray = Array.isArray; } var registry = {}, seen = {}; var FAILED = false; var uuid = 0; function tryFinally(tryable, finalizer) { try { return tryable(); } finally { finalizer(); } } function Module(name, deps, callback, exports) { var defaultDeps = ['require', 'exports', 'module']; this.id = uuid++; this.name = name; this.deps = !deps.length && callback.length ? defaultDeps : deps; this.exports = exports || { }; this.callback = callback; this.state = undefined; } define = function(name, deps, callback) { if (!_isArray(deps)) { callback = deps; deps = []; } registry[name] = new Module(name, deps, callback); }; define.amd = {}; function reify(mod, name, seen) { var deps = mod.deps; var length = deps.length; var reified = new Array(length); var dep; // TODO: new Module // TODO: seen refactor var module = { }; for (var i = 0, l = length; i < l; i++) { dep = deps[i]; if (dep === 'exports') { module.exports = reified[i] = seen; } else if (dep === 'require') { reified[i] = require; } else if (dep === 'module') { mod.exports = seen; module = reified[i] = mod; } else { reified[i] = require(resolve(dep, name)); } } return { deps: reified, module: module }; } requirejs = require = requireModule = function(name) { var mod = registry[name]; if (!mod) { throw new Error('Could not find module ' + name); } if (mod.state !== FAILED && seen.hasOwnProperty(name)) { return seen[name]; } var reified; var module; var loaded = false; seen[name] = { }; // placeholder for run-time cycles tryFinally(function() { reified = reify(mod, name, seen[name]); module = mod.callback.apply(this, reified.deps); loaded = true; }, function() { if (!loaded) { mod.state = FAILED; } }); if (module === undefined && reified.module.exports) { return (seen[name] = reified.module.exports); } else { return (seen[name] = module); } }; function resolve(child, name) { if (child.charAt(0) !== '.') { return child; } var parts = child.split('/'); var nameParts = name.split('/'); var parentBase = nameParts.slice(0, -1); for (var i = 0, l = parts.length; i < l; i++) { var part = parts[i]; if (part === '..') { parentBase.pop(); } else if (part === '.') { continue; } else { parentBase.push(part); } } return parentBase.join('/'); } requirejs.entries = requirejs._eak_seen = registry; requirejs.clear = function(){ requirejs.entries = requirejs._eak_seen = registry = {}; seen = state = {}; }; })(); define("route-recognizer", ["route-recognizer/dsl","exports"], function(__dependency1__, __exports__) { "use strict"; var map = __dependency1__["default"]; var specials = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\' ]; var escapeRegex = new RegExp('(\\' + specials.join('|\\') + ')', 'g'); function isArray(test) { return Object.prototype.toString.call(test) === "[object Array]"; } // A Segment represents a segment in the original route description. // Each Segment type provides an `eachChar` and `regex` method. // // The `eachChar` method invokes the callback with one or more character // specifications. A character specification consumes one or more input // characters. // // The `regex` method returns a regex fragment for the segment. If the // segment is a dynamic of star segment, the regex fragment also includes // a capture. // // A character specification contains: // // * `validChars`: a String with a list of all valid characters, or // * `invalidChars`: a String with a list of all invalid characters // * `repeat`: true if the character specification can repeat function StaticSegment(string) { this.string = string; } StaticSegment.prototype = { eachChar: function(callback) { var string = this.string, ch; for (var i=0, l=string.length; i<l; i++) { ch = string.charAt(i); callback({ validChars: ch }); } }, regex: function() { return this.string.replace(escapeRegex, '\\$1'); }, generate: function() { return this.string; } }; function DynamicSegment(name) { this.name = name; } DynamicSegment.prototype = { eachChar: function(callback) { callback({ invalidChars: "/", repeat: true }); }, regex: function() { return "([^/]+)"; }, generate: function(params) { return params[this.name]; } }; function StarSegment(name) { this.name = name; } StarSegment.prototype = { eachChar: function(callback) { callback({ invalidChars: "", repeat: true }); }, regex: function() { return "(.+)"; }, generate: function(params) { return params[this.name]; } }; function EpsilonSegment() {} EpsilonSegment.prototype = { eachChar: function() {}, regex: function() { return ""; }, generate: function() { return ""; } }; function parse(route, names, types) { // normalize route as not starting with a "/". Recognition will // also normalize. if (route.charAt(0) === "/") { route = route.substr(1); } var segments = route.split("/"), results = []; for (var i=0, l=segments.length; i<l; i++) { var segment = segments[i], match; if (match = segment.match(/^:([^\/]+)$/)) { results.push(new DynamicSegment(match[1])); names.push(match[1]); types.dynamics++; } else if (match = segment.match(/^\*([^\/]+)$/)) { results.push(new StarSegment(match[1])); names.push(match[1]); types.stars++; } else if(segment === "") { results.push(new EpsilonSegment()); } else { results.push(new StaticSegment(segment)); types.statics++; } } return results; } // A State has a character specification and (`charSpec`) and a list of possible // subsequent states (`nextStates`). // // If a State is an accepting state, it will also have several additional // properties: // // * `regex`: A regular expression that is used to extract parameters from paths // that reached this accepting state. // * `handlers`: Information on how to convert the list of captures into calls // to registered handlers with the specified parameters // * `types`: How many static, dynamic or star segments in this route. Used to // decide which route to use if multiple registered routes match a path. // // Currently, State is implemented naively by looping over `nextStates` and // comparing a character specification against a character. A more efficient // implementation would use a hash of keys pointing at one or more next states. function State(charSpec) { this.charSpec = charSpec; this.nextStates = []; } State.prototype = { get: function(charSpec) { var nextStates = this.nextStates; for (var i=0, l=nextStates.length; i<l; i++) { var child = nextStates[i]; var isEqual = child.charSpec.validChars === charSpec.validChars; isEqual = isEqual && child.charSpec.invalidChars === charSpec.invalidChars; if (isEqual) { return child; } } }, put: function(charSpec) { var state; // If the character specification already exists in a child of the current // state, just return that state. if (state = this.get(charSpec)) { return state; } // Make a new state for the character spec state = new State(charSpec); // Insert the new state as a child of the current state this.nextStates.push(state); // If this character specification repeats, insert the new state as a child // of itself. Note that this will not trigger an infinite loop because each // transition during recognition consumes a character. if (charSpec.repeat) { state.nextStates.push(state); } // Return the new state return state; }, // Find a list of child states matching the next character match: function(ch) { // DEBUG "Processing `" + ch + "`:" var nextStates = this.nextStates, child, charSpec, chars; // DEBUG " " + debugState(this) var returned = []; for (var i=0, l=nextStates.length; i<l; i++) { child = nextStates[i]; charSpec = child.charSpec; if (typeof (chars = charSpec.validChars) !== 'undefined') { if (chars.indexOf(ch) !== -1) { returned.push(child); } } else if (typeof (chars = charSpec.invalidChars) !== 'undefined') { if (chars.indexOf(ch) === -1) { returned.push(child); } } } return returned; } /** IF DEBUG , debug: function() { var charSpec = this.charSpec, debug = "[", chars = charSpec.validChars || charSpec.invalidChars; if (charSpec.invalidChars) { debug += "^"; } debug += chars; debug += "]"; if (charSpec.repeat) { debug += "+"; } return debug; } END IF **/ }; /** IF DEBUG function debug(log) { console.log(log); } function debugState(state) { return state.nextStates.map(function(n) { if (n.nextStates.length === 0) { return "( " + n.debug() + " [accepting] )"; } return "( " + n.debug() + " <then> " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )"; }).join(", ") } END IF **/ // This is a somewhat naive strategy, but should work in a lot of cases // A better strategy would properly resolve /posts/:id/new and /posts/edit/:id. // // This strategy generally prefers more static and less dynamic matching. // Specifically, it // // * prefers fewer stars to more, then // * prefers using stars for less of the match to more, then // * prefers fewer dynamic segments to more, then // * prefers more static segments to more function sortSolutions(states) { return states.sort(function(a, b) { if (a.types.stars !== b.types.stars) { return a.types.stars - b.types.stars; } if (a.types.stars) { if (a.types.statics !== b.types.statics) { return b.types.statics - a.types.statics; } if (a.types.dynamics !== b.types.dynamics) { return b.types.dynamics - a.types.dynamics; } } if (a.types.dynamics !== b.types.dynamics) { return a.types.dynamics - b.types.dynamics; } if (a.types.statics !== b.types.statics) { return b.types.statics - a.types.statics; } return 0; }); } function recognizeChar(states, ch) { var nextStates = []; for (var i=0, l=states.length; i<l; i++) { var state = states[i]; nextStates = nextStates.concat(state.match(ch)); } return nextStates; } var oCreate = Object.create || function(proto) { function F() {} F.prototype = proto; return new F(); }; function RecognizeResults(queryParams) { this.queryParams = queryParams || {}; } RecognizeResults.prototype = oCreate({ splice: Array.prototype.splice, slice: Array.prototype.slice, push: Array.prototype.push, length: 0, queryParams: null }); function findHandler(state, path, queryParams) { var handlers = state.handlers, regex = state.regex; var captures = path.match(regex), currentCapture = 1; var result = new RecognizeResults(queryParams); for (var i=0, l=handlers.length; i<l; i++) { var handler = handlers[i], names = handler.names, params = {}; for (var j=0, m=names.length; j<m; j++) { params[names[j]] = captures[currentCapture++]; } result.push({ handler: handler.handler, params: params, isDynamic: !!names.length }); } return result; } function addSegment(currentState, segment) { segment.eachChar(function(ch) { var state; currentState = currentState.put(ch); }); return currentState; } // The main interface var RouteRecognizer = function() { this.rootState = new State(); this.names = {}; }; RouteRecognizer.prototype = { add: function(routes, options) { var currentState = this.rootState, regex = "^", types = { statics: 0, dynamics: 0, stars: 0 }, handlers = [], allSegments = [], name; var isEmpty = true; for (var i=0, l=routes.length; i<l; i++) { var route = routes[i], names = []; var segments = parse(route.path, names, types); allSegments = allSegments.concat(segments); for (var j=0, m=segments.length; j<m; j++) { var segment = segments[j]; if (segment instanceof EpsilonSegment) { continue; } isEmpty = false; // Add a "/" for the new segment currentState = currentState.put({ validChars: "/" }); regex += "/"; // Add a representation of the segment to the NFA and regex currentState = addSegment(currentState, segment); regex += segment.regex(); } var handler = { handler: route.handler, names: names }; handlers.push(handler); } if (isEmpty) { currentState = currentState.put({ validChars: "/" }); regex += "/"; } currentState.handlers = handlers; currentState.regex = new RegExp(regex + "$"); currentState.types = types; if (name = options && options.as) { this.names[name] = { segments: allSegments, handlers: handlers }; } }, handlersFor: function(name) { var route = this.names[name], result = []; if (!route) { throw new Error("There is no route named " + name); } for (var i=0, l=route.handlers.length; i<l; i++) { result.push(route.handlers[i]); } return result; }, hasRoute: function(name) { return !!this.names[name]; }, generate: function(name, params) { var route = this.names[name], output = ""; if (!route) { throw new Error("There is no route named " + name); } var segments = route.segments; for (var i=0, l=segments.length; i<l; i++) { var segment = segments[i]; if (segment instanceof EpsilonSegment) { continue; } output += "/"; output += segment.generate(params); } if (output.charAt(0) !== '/') { output = '/' + output; } if (params && params.queryParams) { output += this.generateQueryString(params.queryParams, route.handlers); } return output; }, generateQueryString: function(params, handlers) { var pairs = []; var keys = []; for(var key in params) { if (params.hasOwnProperty(key)) { keys.push(key); } } keys.sort(); for (var i = 0, len = keys.length; i < len; i++) { key = keys[i]; var value = params[key]; if (value == null) { continue; } var pair = encodeURIComponent(key); if (isArray(value)) { for (var j = 0, l = value.length; j < l; j++) { var arrayPair = key + '[]' + '=' + encodeURIComponent(value[j]); pairs.push(arrayPair); } } else { pair += "=" + encodeURIComponent(value); pairs.push(pair); } } if (pairs.length === 0) { return ''; } return "?" + pairs.join("&"); }, parseQueryString: function(queryString) { var pairs = queryString.split("&"), queryParams = {}; for(var i=0; i < pairs.length; i++) { var pair = pairs[i].split('='), key = decodeURIComponent(pair[0]), keyLength = key.length, isArray = false, value; if (pair.length === 1) { value = 'true'; } else { //Handle arrays if (keyLength > 2 && key.slice(keyLength -2) === '[]') { isArray = true; key = key.slice(0, keyLength - 2); if(!queryParams[key]) { queryParams[key] = []; } } value = pair[1] ? decodeURIComponent(pair[1]) : ''; } if (isArray) { queryParams[key].push(value); } else { queryParams[key] = value; } } return queryParams; }, recognize: function(path) { var states = [ this.rootState ], pathLen, i, l, queryStart, queryParams = {}, isSlashDropped = false; path = decodeURI(path); queryStart = path.indexOf('?'); if (queryStart !== -1) { var queryString = path.substr(queryStart + 1, path.length); path = path.substr(0, queryStart); queryParams = this.parseQueryString(queryString); } // DEBUG GROUP path if (path.charAt(0) !== "/") { path = "/" + path; } pathLen = path.length; if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { path = path.substr(0, pathLen - 1); isSlashDropped = true; } for (i=0, l=path.length; i<l; i++) { states = recognizeChar(states, path.charAt(i)); if (!states.length) { break; } } // END DEBUG GROUP var solutions = []; for (i=0, l=states.length; i<l; i++) { if (states[i].handlers) { solutions.push(states[i]); } } states = sortSolutions(solutions); var state = solutions[0]; if (state && state.handlers) { // if a trailing slash was dropped and a star segment is the last segment // specified, put the trailing slash back if (isSlashDropped && state.regex.source.slice(-5) === "(.+)$") { path = path + "/"; } return findHandler(state, path, queryParams); } } }; RouteRecognizer.prototype.map = map; __exports__["default"] = RouteRecognizer; }); define("route-recognizer/dsl", ["exports"], function(__exports__) { "use strict"; function Target(path, matcher, delegate) { this.path = path; this.matcher = matcher; this.delegate = delegate; } Target.prototype = { to: function(target, callback) { var delegate = this.delegate; if (delegate && delegate.willAddRoute) { target = delegate.willAddRoute(this.matcher.target, target); } this.matcher.add(this.path, target); if (callback) { if (callback.length === 0) { throw new Error("You must have an argument in the function passed to `to`"); } this.matcher.addChild(this.path, target, callback, this.delegate); } return this; } }; function Matcher(target) { this.routes = {}; this.children = {}; this.target = target; } Matcher.prototype = { add: function(path, handler) { this.routes[path] = handler; }, addChild: function(path, target, callback, delegate) { var matcher = new Matcher(target); this.children[path] = matcher; var match = generateMatch(path, matcher, delegate); if (delegate && delegate.contextEntered) { delegate.contextEntered(target, match); } callback(match); } }; function generateMatch(startingPath, matcher, delegate) { return function(path, nestedCallback) { var fullPath = startingPath + path; if (nestedCallback) { nestedCallback(generateMatch(fullPath, matcher, delegate)); } else { return new Target(startingPath + path, matcher, delegate); } }; } function addRoute(routeArray, path, handler) { var len = 0; for (var i=0, l=routeArray.length; i<l; i++) { len += routeArray[i].path.length; } path = path.substr(len); var route = { path: path, handler: handler }; routeArray.push(route); } function eachRoute(baseRoute, matcher, callback, binding) { var routes = matcher.routes; for (var path in routes) { if (routes.hasOwnProperty(path)) { var routeArray = baseRoute.slice(); addRoute(routeArray, path, routes[path]); if (matcher.children[path]) { eachRoute(routeArray, matcher.children[path], callback, binding); } else { callback.call(binding, routeArray); } } } } __exports__["default"] = function(callback, addRouteCallback) { var matcher = new Matcher(); callback(generateMatch("", matcher, this.delegate)); eachRoute([], matcher, function(route) { if (addRouteCallback) { addRouteCallback(this, route); } else { this.add(route); } }, this); } }); global.RouteRecognizer = require("route-recognizer")["default"]; })(window);