# runtime/coordinators/route.coordinator.ts

> 66 lines of code and 12 definitions.

Tree: Site tree
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/tree#file-runtime-coordinators-route-coordinator-ts
Source text: https://banes-lab.com/assets/sources/source.de57f416e8f41185bd43660d88b70017107d065e01313768cdbfda9068bc2e76.generated.txt

## Definitions

- `render` (lexical_declaration, line 13)
- `unsubscribe` (lexical_declaration, line 60, exported)
- `onPopState` (lexical_declaration, line 52, exported)
- `release` (lexical_declaration, line 31)
- `restore` (lexical_declaration, line 63, exported)
- `startRouting` (lexical_declaration, line 51, exported)
- `restoreOnReload` (lexical_declaration, line 26)
- `navigate` (lexical_declaration, line 44)
- `onPageHide` (lexical_declaration, line 55, exported)
- `latest` (lexical_declaration, line 11)
- `ticket` (lexical_declaration, line 15)
- `top` (lexical_declaration, line 27)

## Uses

- [core/assets/link.assets.ts](https://banes-lab.com/source/tree/core/assets/link.assets.ts.md)
- [core/buses/base.bus.ts](https://banes-lab.com/source/tree/core/buses/base.bus.ts.md)
- [core/stores/route.store.ts](https://banes-lab.com/source/tree/core/stores/route.store.ts.md)
- [domain/registries/page.registry.ts](https://banes-lab.com/source/tree/domain/registries/page.registry.ts.md)
- [presentation/renderers/page.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/page.renderer.ts.md)

## Source

```typescript
import { ROUTE_CHANGED, ROUTE_REQUESTED } from "#core/ids/route.ids";
import { applyHead, headOf, renderPage } from "#presentation/renderers/page.renderer";
import { emitEvent, subscribeEvent } from "#core/buses/base.bus";
import { holdScroll, isReload } from "#core/observers/route.observer";
import { loadPage, loadedPage } from "#domain/registries/page.registry";
import { pageFromPath, pagePath } from "#assets/link.assets";
import { readScroll, writeScroll } from "#core/stores/route.store";
import type { Disposer } from "#types/base.types";
import { HOME_PAGE } from "#core/ids/page.ids";

let latest = 0;

const render = async function render(main: HTMLElement, page: string): Promise<void> {
    latest += 1;
    const ticket = latest;
    await loadPage(page);
    if (ticket !== latest) {
        return;
    }
    main.replaceChildren(...renderPage(page));
    applyHead(document, headOf(page, loadedPage(page), window.location.pathname));
    main.scrollTo(0, 0);
    emitEvent({ name: ROUTE_CHANGED, page });
};

const restoreOnReload = function restoreOnReload(main: HTMLElement): Disposer {
    const top = isReload() ? readScroll(window.location.pathname) : null;
    if (top === null) {
        return () => {};
    }
    let release: Disposer = () => {};
    const unsubscribe = subscribeEvent(ROUTE_CHANGED, () => {
        unsubscribe();
        requestAnimationFrame(() => {
            release = holdScroll(main, top);
        });
    });
    return () => {
        unsubscribe();
        release();
    };
};

const navigate = function navigate(main: HTMLElement, page: string, path: string): void {
    if (window.location.pathname + window.location.search + window.location.hash !== path) {
        window.history.pushState({ page }, "", path);
    }
    void render(main, page);
};

export const startRouting = function startRouting(main: HTMLElement): Disposer {
    const onPopState = function onPopState(): void {
        void render(main, pageFromPath(window.location.pathname, HOME_PAGE));
    };
    const onPageHide = function onPageHide(): void {
        writeScroll(window.location.pathname, main.scrollTop);
    };
    window.addEventListener("popstate", onPopState);
    window.addEventListener("pagehide", onPageHide);
    const unsubscribe = subscribeEvent(ROUTE_REQUESTED, (event) => {
        navigate(main, event.page, event.path ?? pagePath(event.page));
    });
    const restore = restoreOnReload(main);
    onPopState();
    return () => {
        window.removeEventListener("popstate", onPopState);
        window.removeEventListener("pagehide", onPageHide);
        unsubscribe();
        restore();
    };
};
```
