UNPKG

zeta

Version:

Write node web app in an angular and unexpress way

267 lines (214 loc) 7.12 kB
/*! * Zeta * Copyright(c) 2014-2015 Xinyu Zhang beviszhang1993@gmail.com * MIT Licensed */ //Dependencies //Methods var Methods = ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE', 'CONNECT']; //Routing hashtable var Table = {}; //Build the table for (var i = 0; i < Methods.length; i++) { Table[Methods[i]] = { staticRoutes: {}, dynamicRoutes: {} }; } var Routes = Table; //Parse a route and put it into the routing table function Parser(pathname, handler) { //Narrow it down with the method table var method = this.method; var table = Routes[method]; //Check if returning function was passed if (typeof handler !== 'function') { throw 'No handler function was passed'; } //Remove the first and last slash frm the expression if present pathname = trimPathname(pathname); //Split the path into paths to start indexing var paths = splitPath(pathname); //Size, this will be used as an index later on var size = paths.length; //Check if its a static route var params = pathname2ParamsArray(paths); //Its static, add to static routes and finish if (params.length === 0) { table.staticRoutes[pathname] = { handler: handler }; table = table.staticRoutes[pathname]; //recurse into } //Else, its a dynamic route, recurse into dynamicRoutes else { table = table.dynamicRoutes; //Check if a routing table for the given size filter already exists, if not create if (typeof table[size] === 'undefined') { table[size] = { params: [], bases: {} }; } //Recurse into table filtered by size table = table[size]; //Add route to dynamic table table for (var i = 0; i < size; i++) { var path = paths[i]; var param_index = indexOfParam(i, params); //Its an parameter if (param_index > -1) { var param = params[param_index]; var length = table.params.push({ param: param.name }); //Recurse into table created table = table.params[length - 1]; //Check if a regexp was passed if (typeof param.regexp !== 'undefined') { table.regexp = new RegExp(param.regexp); } //Add routing scheme table.routes = { params: [], bases: {} }; //Check if this is the last iteration, if so, add the handler if (i >= size - 1) { table = table.routes; //recurse into table.handler = handler; } //Else recurse into next table we will be working in else { table = table.routes; } } //Its a base path else { //Create a new table, if not exists if (typeof table.bases[path] === 'undefined') { table.bases[path] = { params: [], bases: {} }; } //Recurse into routing table table = table.bases[path]; //Check if this is the last iteration, if so, add the handler if (i >= size - 1) { table.handler = handler; } } } } //Pointer to final table //The final table contains all metadata of the route this.table = table; return this; } //Helper function to trim the pathnames first and last slash //@return string (trimed pathname) function trimPathname(pathname) { if (pathname.charAt(0) === '/') { pathname = pathname.substring(1); } if (pathname.charAt(pathname.length - 1) === '/') { pathname = pathname.substring(pathname.length); } return pathname; } //Helper function to construct an array with parameters of a pathname //@return [] (parameters found) function pathname2ParamsArray(paths) { var params = []; for (var p = 0; p < paths.length; p++) { var path = paths[p]; if (path.charAt(0) === ':') { var push_data = { pos: parseInt(p) }; //Remove `:` from path string path = path.substring(1); //Check if an regexp was passed if (path.indexOf('(') > -1 && path.lastIndexOf(')') > -1) { //Start, end var start = path.indexOf('('); var end = path.lastIndexOf(')'); //Extract regexp string from path var regexp = path.substring(start + 1, end); //Remove regexp from path identifier path = path.substring(0, start); push_data.regexp = regexp; } //Append parameter identifier push_data.name = path; params.push(push_data); } } return params; } //Helper function to retrieve parameters inside an given array of paths //@return integer (index in the array) function indexOfParam(pos, parameters) { for (var i = 0; i < parameters.length; i++) { if (parameters[i].pos == pos) { return i; } } return -1; } //Helper function to properly parse a url, this replaces require('url').parse(path).pathname function urlParse(url) { var query = url.indexOf('?'); if (query !== -1) { url = url.substring(0, query); } return url; } //Helper function to split the path into smaller base paths //A simple pathname.split('/') wont work, since regexp can contain slashes //@input string //@return array function splitPath(path) { var paths = []; var buffer = ''; var i = 0; //Modes var inParameter = false, inRegexp = false; while (i < path.length) { //Check if a parameter is starting if (path[i - 1] == '/' && path[i] == ':') { inParameter = true; } //Check if they passed a regexp //This initialize the regexp buffer if (inParameter === true && inRegexp === false && path[i] == '(') { inRegexp = true; } //Deactivate else if (inRegexp === true && path[i] == ')' && path[i + 1] == '/') { inRegexp = false; } //Clear buffer or append if (path[i] == '/' && inRegexp === false) { //Clear buffer paths.push(buffer); buffer = ''; inParameter = false; //default } else { buffer = buffer + path[i]; } i++; //next character } //Push any remaining buffers if (buffer) { paths.push(buffer); } return paths; } //Exports Parser.trimPathname = trimPathname; Parser.Table = Table; Parser.url = urlParse; module.exports = Parser;