StructureJS

0.15.2

A class based utility library for building modular and scalable web platform applications. Features opt-in classes and utilities which provide a solid foundation and toolset to build your next project.

File: ts/model/Route.ts

/**
 * The **Route** class is a model that keeps track of a specific route for the {{#crossLink "Router"}}{{/crossLink}} class.
 *
 * @class Route
 * @module StructureJS
 * @submodule model
 * @param routePattern {string} The string pattern you want to have match, which can be any of the following combinations {}, ::, *, ''
 * @param callback {Function} The function that should be executed when a request matches the routePattern.
 * @param callbackScope {any} The scope of the callback function that should be executed.
 * @constructor
 * @author Robert S. (www.codeBelt.com)
 * @example
 *     // Example of adding a route listener and the function callback below.
 *     let route = new Route('/games/{gameName}/:level:/', this._method, this);
 *
 *     // The above route would match the string below:
 *     route.match('/games/asteroids/2/');
 *
 * Route Pattern Options:
 * ----------------------
 * **:optional:** The two colons **::** means a part of the hash url is optional for the match. The text between can be anything you want it to be.
 *
 *     let route = new Route('/contact/:name:/', this._method, this);
 *
 *     // Will match one of the following:
 *     route.match('/contact/');
 *     route.match('/contact/heather/');
 *     route.match('/contact/john/');
 *
 *
 * **{required}** The two curly brackets **{}** means a part of the hash url is required for the match. The text between can be anything you want it to be.
 *
 *     let route = new Route('/product/{productName}/', this._method, this);
 *
 *     // Will match one of the following:
 *     route.match('/product/shoes/');
 *     route.match('/product/jackets/');
 *
 *
 * **\*** The asterisk character means it will match all or part of part the hash url.
 *
 *     let route = new Route('*', this._method, this);
 *
 *     // Will match one of the following:
 *     route.match('/anything/');
 *     route.match('/matches/any/hash/url/');
 *     route.match('/really/it/matches/any/and/all/hash/urls/');
 *
 *
 * **''** The empty string means it will match when there are no hash url.
 *
 *     let route = new Route('', this._method, this);
 *     let route = new Route('/', this._method, this);
 *
 *     // Will match one of the following:
 *     route.match('');
 *     route.match('/');
 *
 *
 * Other possible combinations but not limited too:
 *
 *     let route = new Route('/games/{gameName}/:level:/', this._method1, this);
 *     let route = new Route('/{category}/blog/', this._method2, this);
 *     let route = new Route('/about/*', this._method3, this);
 *
 */
class Route
{
    /**
     * The string pattern you want to have match, which can be any of the following combinations {}, ::, *, ?, "". See below for examples.
     *
     * @property routePattern
     * @type String
     * @public
     */
    public routePattern = '';

    /**
     * The regex representation for the routePattern that was passed into the constructor.
     *
     * @property regex
     * @type RegExp
     * @public
     * @readOnly
     */
    public regex:RegExp = null;

    /**
     * The function that should be executed when a request matches the routePattern. The {{#crossLink "Router"}}{{/crossLink}} class will be using this property.
     *
     * @property callback
     * @type {Function}
     * @public
     */
    public callback:Function = null;

    /**
     * The scope of the callback function that should be executed. The {{#crossLink "Router"}}{{/crossLink}} class will be using this property.
     *
     * @property callbackScope
     * @type {any}
     * @public
     */
    public callbackScope:any = null;

    constructor(routePattern:string, callback:Function, scope:any)
    {
        this.routePattern = routePattern;
        this.regex = this._routePatternToRegexp(routePattern);
        this.callback = callback;
        this.callbackScope = scope;
    }

    /**
     * Converts the routePattern that was passed into the constructor to a regexp object.
     *
     * @method _routePatternToRegexp
     * @param {String} routePattern
     * @returns {RegExp}
     * @protected
     */
    protected _routePatternToRegexp(routePattern):RegExp
    {
        const findFirstOrLastForwardSlash:RegExp = new RegExp('^\/|\/$', 'g'); // Finds if the first character OR if the last character is a forward slash
        const findOptionalColons:RegExp = new RegExp(':([^:]*):', 'g'); // Finds the colons : :
        const findRequiredBrackets:RegExp = new RegExp('{([^}]+)}', 'g'); // Finds the brackets { }
        const optionalFirstCharSlash = '^/?';// Allows the first character to be if a forward slash to be optional.
        const optionalLastCharSlash = '/?$';// Allows the last character to be if a forward slash to be optional.

        // Remove first and last forward slash.
        routePattern = routePattern.replace(findFirstOrLastForwardSlash, '');

        // Convert the wild card * be a regex ?(.*) to select all.
        routePattern = routePattern.replace('*', '?(.*)');

        // Make any :alphanumeric: optional
        routePattern = routePattern.replace(findOptionalColons, '?([^/]*)');

        // Make any {alphanumeric} required
        routePattern = routePattern.replace(findRequiredBrackets, '([^/]+)');

        return new RegExp(optionalFirstCharSlash + routePattern + optionalLastCharSlash, 'i');
    }

    /**
     * Determine if a route matches a routePattern.
     *
     * @method match
     * @param route {String} The route or path to match against the routePattern that was passed into the constructor.
     * @returns {Array.<any>}
     * @example
     *     let route = new Route('/games/{gameName}/:level:/', this.method, this);
     *     console.log( route.match('/games/asteroids/2/') );
     */
    public match(route):Array<any>
    {
        // Remove the query string before matching against the route pattern.
        const routeWithoutQueryString:string = route.replace(/\?.*/, '');

        return routeWithoutQueryString.match(this.regex);
    }

}

export default Route;