avenue.common.js

'use strict';

/**
 * Iterates over each entry of an object.
 *
 * @memberof For
 * @param {object} obj Object to iterate.
 * @param {function} fn Function to use (`fn(key: *, val: *, index: number, obj: object) => void`).
 * @example
 * const a = {a: 1, b: 2};
 *
 * forEachEntry(a, (key, val, index) => {
 *     a[key] = val * index;
 * })
 * // a = {a: 0, b: 2}
 */
const forEachEntry = (obj, fn) => {
    for (const [key, val] of Object.entries(obj)) {
        fn(val, key, obj);
    }
};

var Delimiters;
(function (Delimiters) {
    Delimiters["KEBAB"] = "-";
    Delimiters["SNAKE"] = "_";
})(Delimiters || (Delimiters = {}));

/**
 * Returns hash without init-character.
 *
 * @private
 * @returns current location hash, without the hash.
 */
const getLocationHash = () => location.hash.replace("#", "");

/**
 * Splits path by slashes and trims.
 *
 * @private
 * @param path Path string.
 * @returns trimmed path string array.
 */
const splitPath = (path) => path.split("/").filter(item => item.length);

/**
 * Checks if the pathPart is a path variable.
 *
 * @private
 * @param pathPart path string.
 * @returns if the pathPart is a path variable.
 */
const isPathVariable = (pathPart) => pathPart.startsWith(":");
/**
 * Checks if two routes match.
 *
 * @private
 * @param currentPath first route.
 * @param routePath second route.
 * @returns if the first and second route match.
 */
const routesMatch = (currentPath, routePath) => currentPath.every((currentPathPart, index) => {
    const routePathPart = routePath[index];
    if (routePathPart) {
        // Checks for variable-wildcard or equivalency
        return (isPathVariable(routePathPart) ||
            currentPathPart === routePathPart);
    }
    return false;
});
/**
 * Finds route by path.
 *
 * @private
 * @param path path string array.
 * @param routes object containing routes.
 * @returns object containing route and args, or null if none was found.
 */
const findRoute = (path, routes) => {
    const route = routes.find((routeCurrent) => routesMatch(path, routeCurrent[0]));
    if (route) {
        const args = {};
        route[0].forEach((routePathPart, index) => {
            if (isPathVariable(routePathPart)) {
                args[routePathPart.substr(1)] = path[index];
            }
        });
        return {
            route,
            args
        };
    }
    return null;
};

/**
 * Avenue class.
 *
 * @class
 */
class Avenue {
    /**
     * Avenue constructor.
     *
     * @constructor
     * @param {object} routes object of routes to use.
     */
    constructor(routes) {
        this.view = null;
        this.routes = [];
        this.fallback = () => null;
        // Change routes from {string: fn} to [string[], fn] and extract fallback route
        forEachEntry(routes, (routeItemFn, routeItemPath) => {
            if (routeItemPath === "?") {
                this.fallback = routeItemFn;
            }
            else {
                this.routes.push([splitPath(routeItemPath), routeItemFn]);
            }
        });
        window.addEventListener("hashchange", e => this.setView(getLocationHash(), e), false);
        // Load current route if it exists
        this.setView(getLocationHash());
    }
    /**
     * Sets view to a route path.
     *
     * @param {string} path string route path.
     * @param {Event|null} [e=null] event, if called through one.
     */
    setView(path, e = null) {
        const result = findRoute(splitPath(path), this.routes);
        if (result) {
            this.view = path;
            result.route[1](result.args, path, e);
        }
        else {
            this.fallback({}, path, e);
        }
    }
    /**
     * Returns active view path.
     *
     * @returns {string|null} active view, or null if none was set.
     */
    getView() {
        return this.view;
    }
}

module.exports = Avenue;
//# sourceMappingURL=avenue.common.js.map