Last active
December 10, 2015 07:28
-
-
Save amasad/4401728 to your computer and use it in GitHub Desktop.
A quick and dirty yet powerful JS router.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// requires underscore.js for `_.clone`, although come to think about it could be done without it. | |
// The router module. | |
var router = (function () { | |
// Get the location array and filter out empty strings. | |
var path = window.location.pathname.split('/').filter(Boolean) | |
// The param arg stack. | |
, arg_stack = []; | |
// Main match function. Which is called by all api's to match a certain path. | |
// with params and regular expression. | |
var _match = function (path, param, regHash, args) { | |
var parts = param.split('/') | |
, passed = true; | |
// For every part of our path to match the window path. | |
for (var i = 0, p; p = parts[i]; i++) { | |
// A part is optional if its starts with a paran. | |
var isOptional = !!p.match(/^\(.+\)$/) | |
// The current path to match against. | |
, cur = path.shift(); | |
// Pull up the actual path to match. | |
p = p.match(/\(?([^\)]+)\)?/)[1]; | |
// If its a param. | |
if (cur && p.charAt(0) === ':') { | |
var argName = p.substring(1); | |
if (regHash[argName] && !cur.match(regHash[argName])) { | |
if (isOptional) { | |
path.unshift(cur); | |
continue; | |
} else { | |
passed = false; | |
break; | |
} | |
} | |
args[argName] = cur; | |
continue; | |
} | |
// Its not a param then its must match the current path. | |
if (p !== cur) { | |
// If path part to match is optional then we unshift the current | |
// location path we're matching against. | |
if (isOptional) { | |
if (cur) path.unshift(cur); | |
} else { | |
// fail. | |
passed = false; | |
break; | |
} | |
} | |
} | |
return passed ? path : null; | |
}; | |
// Wraps every api call of our module and abstracts the common | |
// argument juggling. | |
var api = function (call) { | |
return function (param, regHash, fns) { | |
if (typeof regHash === 'function' || Array.isArray(regHash)) { | |
fns = regHash; | |
regHash = {}; | |
} | |
var fn; | |
if (Array.isArray(fns)) { | |
fn = function () { | |
var args = arguments; | |
fns.forEach(function (f) { | |
f.apply(null, args); | |
}); | |
}; | |
} else { | |
fn = fns; | |
} | |
return call(param, regHash, fn); | |
}; | |
}; | |
// Just match part of the location path. | |
// Takes care of creating a scope (context for other calls). | |
var scope = function (param, regHash, fn) { | |
var args = arg_stack[arg_stack.length - 1] || {}; | |
arg_stack.push(args); | |
var passed = _match(_.clone(path), param, regHash, args) | |
, _path = path; | |
if (passed) { | |
path = passed; | |
fn(); | |
} | |
// Back up after we finished executing our context function. | |
path = _path; | |
arg_stack.pop(); | |
}; | |
// This is a terminal match. Check that all path parts is succesfully | |
// matched. | |
var match = function (param, regHash, fn) { | |
var args = _.clone(arg_stack[arg_stack.length - 1]) | |
, passed = _match(_.clone(path), param, regHash, args); | |
if (passed && !passed.length) { | |
fn(args); | |
} | |
}; | |
// Just like the scope function but don't have to do post exec | |
// stuff. | |
var part = function (param, regHash, fn) { | |
var args = _.clone(arg_stack[arg_stack.length - 1]) | |
, passed = _match(_.clone(path), param, regHash, args); | |
if (passed) { | |
fn(args); | |
} | |
}; | |
return { | |
scope: api(scope) | |
, match: api(match) | |
, part: api(part) | |
}; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// path: '/en/sections/123abc/1' | |
var scope = router.scope, match = router.match, part = router.part; | |
scope('(:locale)', {locale: /^[a-z]{2}$/g}, function (args) { | |
scope('sections', function () { | |
part(':section_id', function (args) { | |
console.log('init something awesome to do with section %s', args.section_id); | |
}); | |
scope(':section_id', function () { | |
match(':section_no', {section_no: /[0-9]+/}, function (args) { | |
console.log('some action to do with section #%d', args.section_no); | |
}); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment