WakaRoute

WakaRoute is a client-side router plugin for wakaPAC. It sends a MSG_ROUTE_BEFORE message before each navigation — allowing any component to cancel it by returning false — followed by MSG_ROUTE_CHANGE on commit, with route match status in msg.wparam and extracted URL parameters pre-computed in msg.detail.params. Only components that declare a data-pac-route attribute receive either message.

explanation

Getting Started

Include the script after wakaPAC, register the plugin, then navigate:

<script src="https://cdn.jsdelivr.net/gh/quellabs/wakapac@main/wakapac.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/quellabs/wakapac@main/plugins/wakaroute.min.js"></script>

<script>
    wakaPAC.use(wakaRoute); // must be called before any wakaPAC() calls

    wakaRoute.navigate('/users/42');
</script>

Declare which route a component responds to with the data-pac-route attribute. Handle MSG_ROUTE_CHANGE in msgProc — the router pre-computes msg.detail.params from the component's own pattern, so no manual matching is needed:

<div id="user-view" data-pac-id="user-view" data-pac-route="/users/{id}"></div>
wakaPAC('#user-view', {
    msgProc(msg) {
        if (msg.message !== wakaPAC.MSG_ROUTE_CHANGE) {
            return;
        }

        if (msg.wparam) {
            this.userId = msg.detail.params.id;
        }
    }
});

Only components that declare a data-pac-route attribute receive MSG_ROUTE_CHANGE. Components without a route attribute are not notified.

Usage

Route Patterns

Patterns are declared either in HTML via data-pac-route or passed directly to matchPattern(). WakaRoute uses the same URL pattern conventions as Canvas. The supported syntax is:

Syntax Description Example
{name} Named segment — matches one path segment (no slashes). Captured into params.name. /users/{id}
{name:*} Alias for {name} — explicit single-segment form. /tags/{tag:*}
{name:**} Named multi-segment wildcard — matches everything including slashes. Captured into params.name. May appear multiple times in a single pattern. /files/{rest:**}
* Bare single-segment wildcard — matches one segment but does not capture. /files/*/info
** Bare multi-segment wildcard — matches any number of segments but does not capture. May appear multiple times in a single pattern. /shop/**/item/**

Patterns are anchored — they must match the full path. A pattern /about does not match /about/team.

Extracting Parameters

Call matchPattern() inside msgProc to test the current path against a pattern and extract named parameters. It returns a plain object on match, or null if the pattern does not match:

const params = wakaRoute.matchPattern('/users/{id}/posts/{postId}', msg.detail.path);
// params → { id: '42', postId: '7' }  or  null

URL-encoded segments are automatically decoded:

wakaRoute.matchPattern('/search/{term}', '/search/hello%20world');
// → { term: 'hello world' }

Query Parameters

MSG_ROUTE_CHANGE detail always includes a query object parsed from the URL search string. Flag parameters (no value) are stored as boolean true. PHP-style bracket keys (key[]=) collect into arrays:

// URL: /users/42?tab=profile&debug
msg.detail.path   // → '/users/42'
msg.detail.query  // → { tab: 'profile', debug: true }

// URL: /search?tag[]=news&tag[]=tech
msg.detail.query  // → { tag: ['news', 'tech'] }

Showing and Hiding Views

A common pattern is to show a container only when its route matches and hide it otherwise. Use msg.wparam to drive visibility — it is 1 when the component's own pattern matched and 0 when it did not:

<div id="user-view"  data-pac-id="user-view"  data-pac-route="/users/{id}" hidden></div>
<div id="users-list" data-pac-id="users-list" data-pac-route="/users"      hidden></div>
wakaPAC('[data-pac-route]', {
    msgProc(msg) {
        if (msg.message !== wakaPAC.MSG_ROUTE_CHANGE) {
            return;
        }

        if (msg.wparam) {
            wakaPAC.showContainer(this.pacId);
        } else {
            wakaPAC.hideContainer(this.pacId);
        }
    }
});

Programmatic Navigation

Call navigate() to push a new entry onto the browser history and broadcast MSG_ROUTE_CHANGE. Pass { replace: true } to replace the current history entry instead:

wakaRoute.navigate('/users/42');
wakaRoute.navigate('/users/42', { replace: true }); // no new history entry

Browser back and forward navigation is handled automatically — pressing the back button broadcasts MSG_ROUTE_CHANGE with the restored path.

Reading the Current Route

Call currentRoute() at any time to read the current path and query without waiting for a message. The path is seeded from location.pathname when the plugin is registered:

const { path, query } = wakaRoute.currentRoute();
// → { path: '/users/42', query: { tab: 'profile' } }

The returned object is a copy — mutations do not affect internal state.

API

wakaPAC.use(wakaRoute)

Register the plugin. Must be called before any components are created.

ParameterTypeDescription
wakaRouteobjectThe WakaRoute plugin object.
Returns void

wakaRoute.navigate(path, opts?)

Navigate to path, update browser history, and send MSG_ROUTE_CHANGE to all components with a data-pac-route attribute. Before committing, broadcasts MSG_ROUTE_BEFORE — if any component returns false from msgProc, the navigation is cancelled and history is unchanged. Throws if called before wakaPAC.use(wakaRoute).

ParameterTypeDescription
pathstringThe path to navigate to, e.g. '/users/42'.
optsobjectOptional navigation options.
opts.replacebooleanWhen true, replaces the current history entry instead of pushing a new one. The back button will skip the replaced entry. Default: false.
Returns booleanfalse if navigation was blocked by a component, true otherwise.

wakaRoute.matchPattern(pattern, path)

Test path against pattern. Returns a plain object of extracted named parameters on match, or null if the pattern does not match.

ParameterTypeDescription
patternstringA route pattern string, e.g. '/users/{id}/posts/{postId}'. Patterns are compiled once and cached.
pathstringThe path to test against the pattern, e.g. msg.detail.path.
Returns object|nullPlain object of extracted named parameters on match, e.g. { id: '42', postId: '7' }. null if the pattern does not match.

wakaRoute.currentRoute()

Returns a copy of the current route state. Safe to call before the first navigation. The path is seeded from location.pathname when the plugin is registered. Mutations to the returned object do not affect internal state.

ParameterTypeDescription
Returns { path, query }Current path as a string and parsed query string as a plain object. Empty object when there is no query string.

wakaRoute.getRouteTable()

Returns a snapshot of all components that declared a data-pac-route attribute, keyed by pacId. Useful for debugging.

ParameterTypeDescription
Returns objectSnapshot of registered route components keyed by pacId. Mutations do not affect internal state.

wakaRoute.destroy()

Removes the popstate listener and clears all router state. Useful for test teardown.

ParameterTypeDescription
Returns void

Messages

WakaRoute delivers two message types to msgProc. Only components that declare a data-pac-route attribute receive them.

Before Navigate (MSG_ROUTE_BEFORE)

Fired before each navigation commits — programmatic or browser back/forward — to all components that declare a data-pac-route attribute. Return false from msgProc to cancel the navigation. If cancelled, history is not updated and MSG_ROUTE_CHANGE is not sent. To redirect instead of blocking, call wakaRoute.navigate() inside msgProc.

wakaPAC('#protected-view', {
    msgProc(msg) {
        if (msg.message === wakaPAC.MSG_ROUTE_BEFORE && msg.wparam) {
            if (!isLoggedIn()) {
                wakaRoute.navigate('/login');
                return false; // cancel original navigation
            }
        }
    }
});

Message Parameters

Parameter Type Description
wParam number 1 if the component's own data-pac-route pattern matched the target path, 0 if it did not.
lParam number Always 0.

Detail Properties

Property Type Description
detail.path string The target path, e.g. '/users/42'.
detail.query object Parsed query string of the current URL (before the navigation commits).
detail.params object|null Extracted URL parameters from the component's own data-pac-route pattern matched against the target path. null if the pattern did not match.

Route Change (MSG_ROUTE_CHANGE)

Fired on every navigation — programmatic or browser back/forward — to all components that declare a data-pac-route attribute. The router pre-computes msg.detail.params from the component's own pattern, so no manual matching is needed inside msgProc.

Message Parameters

Parameter Type Description
wParam number 1 if the component's own data-pac-route pattern matched the current path, 0 if it did not. Use this for cheap match/no-match checks before inspecting detail.params.
lParam number Always 0.

Detail Properties

Property Type Description
detail.path string The current location.pathname, e.g. '/users/42'.
detail.query object Parsed query string as a plain object, e.g. ?tab=profile&debug becomes { tab: 'profile', debug: true }. Flag parameters (no =value) are stored as boolean true. PHP-style bracket keys collect into arrays: ?tag[]=a&tag[]=b becomes { tag: ['a', 'b'] }. Empty object when there is no query string.
detail.params object|null Extracted URL parameters from the component's own data-pac-route pattern. null if the pattern did not match the current path.

Notes

WakaRoute uses the History API (pushState / replaceState). The server must serve the same HTML file for all routes, and a local HTTP server is required during development — npx serve . works well for this. The router does not manage scroll position on navigation. If your application requires scroll restoration, handle it in msgProc after matching the route.

Best Practices

  • Register before creating components — call wakaPAC.use(wakaRoute) before any wakaPAC() calls so data-pac-route attributes are picked up during component creation.
  • Use data-pac-route as the source of truth — declare the pattern on the container element. The router reads it automatically and delivers pre-computed params with each message, keeping msgProc free of pattern strings.
  • Keep guards in the component — handle MSG_ROUTE_BEFORE in the component that owns the decision. Return false to block, or call wakaRoute.navigate() before returning false to redirect.
  • Name wildcard segments descriptively — prefer /files/{rest:**} over a bare ** when you need the captured value. Reserve bare wildcards for patterns where the intermediate segments are irrelevant.
  • Use matchPattern() for ad-hoc matching — when a component needs to test paths beyond its own registered pattern, matchPattern() is available. Patterns are compiled once and cached, so repeated calls are safe and efficient.