lucid-router
Version:
a simple html5-history aware router
327 lines (273 loc) • 9.73 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
exports.addRoutes = addRoutes;
exports.removeRoute = removeRoute;
exports.match = match;
exports.navigate = navigate;
exports.navigatorFor = navigatorFor;
exports.pathFor = pathFor;
exports.navigateToRoute = navigateToRoute;
exports.navigatorForRoute = navigatorForRoute;
exports.register = register;
exports.getLocation = getLocation;
var _urlPattern = require('url-pattern');
var _urlPattern2 = _interopRequireDefault(_urlPattern);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var window = global.window;
var history = global.history;
var hasHistoryApi = window !== undefined && history !== undefined && typeof history.pushState === 'function';
var locationChangeCallbacks = [];
var routes = [];
function addRoutes(newRoutes) {
if (!(newRoutes instanceof Array)) throw typeError(routes, 'lucid-router expects to be passed a routing array as its first parameter');
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = newRoutes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var route = _step.value;
if (route === null || !(route instanceof Object)) throw typeError(routes, 'lucid-router expects each route definition to be an object');
route.path = route.path || null;
route.name = route.name || null;
route.external = typeof route.external === 'function' ? route.external : !!route.external;
try {
route.pattern = new _urlPattern2.default(route.path);
} catch (err) {
throw typeError(route.path, 'lucid-router expects route paths to be a string or regex expression');
}
routes.push(route);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
function removeRoute(name) {
var idx = -1;
for (var i = 0, l = routes.length; i < l; i++) {
if (routes[i].name === name) {
idx = i;
break;
}
}
~idx && routes.splice(idx, 1);
}
function parseQuery(query) {
var queryArgs = {};
if (query) {
query.split('&').filter(function (keyValStr) {
return !!keyValStr;
}).map(function (keyValStr) {
return keyValStr.split('=').map(function (encoded) {
return decodeURIComponent(encoded);
});
}).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2);
var key = _ref2[0];
var val = _ref2[1];
return key && (queryArgs[key] = val);
});
}
return queryArgs;
}
function match(path) {
var _path$split = path.split('#');
var _path$split2 = _slicedToArray(_path$split, 2);
var pathnameAndQuery = _path$split2[0];
var hashAndHashQuery = _path$split2[1];
var _pathnameAndQuery$spl = pathnameAndQuery.split('?');
var _pathnameAndQuery$spl2 = _slicedToArray(_pathnameAndQuery$spl, 2);
var pathname = _pathnameAndQuery$spl2[0];
var search = _pathnameAndQuery$spl2[1];
var _ref3 = hashAndHashQuery ? hashAndHashQuery.split('?') : [];
var _ref4 = _slicedToArray(_ref3, 2);
var hash = _ref4[0];
var hashSearch = _ref4[1];
var queryState = parseQuery([search, hashSearch].join('&'));
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = routes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var route = _step2.value;
var matchState = route.pattern.match(pathname);
if (!matchState) continue;
return {
route: route,
pathname: pathname,
search: search ? '?'.concat(search) : '',
hash: hash ? '#'.concat(hash) : '',
hashSearch: hashSearch ? '?'.concat(hashSearch) : '',
state: _extends({}, queryState, matchState)
};
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return null;
}
function navigate(path, e, replace) {
path = getFullPath(path || '');
if (hasHistoryApi) {
if (typeof path !== 'string' || !path) throw typeError(path, 'lucid-router.navigate expected a non empty string as its first parameter');
var m = match(path);
if (m && notExternal(m)) {
var location = matchAndPathToLocation(m, path);
if (replace) {
history.replaceState(null, '', path);
} else {
history.pushState(null, '', path);
}
if (e && e.preventDefault) {
e.preventDefault();
}
onLocationChange(location);
return;
}
}
if (window) {
if (!e) window.location = path;else {
var target = e.target;
if (!target || target.tagName !== 'A') {
window.location = path;
}
}
}
}
function navigatorFor(path, replace) {
return function (e) {
return navigate(path, e, replace);
};
}
function pathFor(routeName, params) {
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = routes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var route = _step3.value;
if (route.name === routeName) {
return route.pattern.stringify(params);
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
throw new Error('lucid-router.pathFor failed to find a route with the name \'' + routeName + '\'');
}
function navigateToRoute(routeName, params, e) {
navigate(pathFor(routeName, params), e);
}
function navigatorForRoute(routeName, params) {
return function (e) {
return navigateToRoute(routeName, params, e);
};
}
function register(callback) {
if (typeof callback !== 'function') throw typeError(callback, 'lucid-router.register expects to be passed a callback function');
locationChangeCallbacks.push(callback);
return function unregister() {
var idx = locationChangeCallbacks.indexOf(callback);
~idx && locationChangeCallbacks.splice(idx, 1);
};
}
function onLocationChange(location) {
locationChangeCallbacks.forEach(function (cb) {
return cb(location);
});
}
function getFullPath(path) {
if (window) {
var a = window.document.createElement('a');
a.href = path;
if (!a.host) a.href = a.href; /* IE hack */
if (a.hostname === window.location.hostname) {
path = a.pathname + a.search + a.hash;
if (path[0] !== '/') {
/* more IE hacks */
path = '/' + path;
}
} else {
path = a.href;
}
}
return path;
}
function getWindowPathAndQuery() {
var location = window.location;
if (!location) return null;
return location.pathname + location.search + location.hash;
}
function getLocation(path) {
path = path || getWindowPathAndQuery() || '';
var m = match(path);
return matchAndPathToLocation(m, path);
}
function matchAndPathToLocation(m, p) {
return !m ? null : {
path: p,
name: m.route.name,
pathname: m.pathname,
search: m.search,
hash: m.hash,
hashSearch: m.hashSearch,
state: m.state,
route: m.route
};
}
function notExternal(m) {
var external = m.route.external;
if (typeof external === 'function') {
return !external(m);
} else return !external;
}
if (hasHistoryApi && window) {
window.addEventListener('popstate', function (e) {
var path = getWindowPathAndQuery() || '';
var m = match(path);
if (m && notExternal(m)) {
var location = matchAndPathToLocation(m, path);
onLocationChange(location);
}
}, false);
}
function typeError(type, msg) {
return new TypeError(msg + ' but got type `' + (typeof type === 'undefined' ? 'undefined' : _typeof(type)) + '`!');
}