UNPKG

tabby

Version:

create tabbed web apps with progressive enhancement

126 lines (109 loc) 3.66 kB
var quotemeta = require('quotemeta'); var qs = require('querystring'); var url = require('url'); var through = require('through'); var EventEmitter = require('events').EventEmitter; module.exports = Match; function Match () { if (!(this instanceof Match)) return new Match; this._routes = []; this._groups = []; this._regexp = { test: function () { return false } }; } Match.prototype.match = function (href) { var m = this._regexp.exec(href); if (!m) return undefined; var vars = {}; var route; for (var i = 0, j = 1; i < this._groups.length; i++) { var g = this._groups[i]; if (m[j] !== undefined) { route = g.route; for (var k = 0; k < g.groups.length; k++) { vars[g.groups[k]] = m[j + k + 1]; } break; } j += 1 + g.groups.length; } if (!route) return undefined; var ext = m[m.length - 1]; var params = qs.parse((url.parse(href).search || '').replace(/^\?/, '')); Object.keys(vars).forEach(function (key) { params[key] = vars[key]; }); return { route: route, vars: vars, ext: ext, params: params }; }; Match.prototype.test = function (u) { return this._regexp.test(u); }; Match.prototype._scan = function () { var self = this; self._groups = []; var parts = self._routes.map(function (r) { var groups = []; var pattern = ('(' + quotemeta(r.pattern) + ')') .replace(/\\:([\w-]+)/g, function (_, x) { groups.push(x); return '([\\w-]+)'; }) ; self._groups.push({ route: r, groups: groups }); return pattern; }); self._regexp = RegExp( '^(?:' + parts.join('|') + ')' + '(\.(?:json|html))?(?:[/?#]|$)' ); self.source = self._regexp.source; }; Match.prototype.add = function (pattern, route) { if (pattern && typeof pattern === 'object') { route = pattern; } else route.pattern = pattern; route._events = new EventEmitter; var regex = RegExp( ('(' + quotemeta(pattern) + ')').replace(/\\:([\w-]+)/g, '([\\w-]+)') ); route._events.regex = regex; route._events.test = function (s) { return regex.test(s) }; route.data = (function (dataFn) { if (!dataFn) return null; return function (params) { var st = dataFn.call(route, params, function (err, res) { if (err) return st.emit('error', err); st.queue(res); st.queue(null); }); if (!st) st = through(); return st; }; })(route.data); var parts = route.pattern.split('/'); (function (routes) { for (var i = 0; i < routes.length; i++) { var rparts = routes[i].pattern.split('/').slice(1); for (var j = 0; j < parts.length; j++) { var a = /:[\w-]+/.test(parts[j]); var b = /:[\w-]+/.test(rparts[j]); if (!a && b) { return routes.splice(i, 0, route); } if (!a && !b && (!rparts[j] || parts[j].length < rparts[j].length)) { return routes.splice(i, 0, route); } if (a && rparts[j] === undefined) { return routes.splice(i, 0, route); } } } if (i === routes.length) routes.push(route); })(this._routes); this._scan(); return route._events; };