# core/evaluators/page.evaluator.ts

> 59 lines of code and 22 definitions.

Tree: Bane's Lab Social Share
Language: typescript
Layer: runtime
Canonical: https://banes-lab.com/anatomy/social-share#file-social-share-core-evaluators-page-evaluator-ts
Source text: https://banes-lab.com/source/social-share/core/evaluators/page.evaluator.ts.txt

Listed in [core/evaluators](https://banes-lab.com/api/source/social-share/core/evaluators.md), after [core/evaluators/card.fragment.evaluator.ts](https://banes-lab.com/source/social-share/core/evaluators/card.fragment.evaluator.ts.md).

## Definitions

- `pageAddress` (lexical_declaration, line 6, exported)
- `wrapCount` (lexical_declaration, line 14, exported)
- `rowX` (lexical_declaration, line 40)
- `rowHeight` (lexical_declaration, line 30)
- `columnOf` (lexical_declaration, line 47, exported)
- `path` (lexical_declaration, line 7, exported)
- `WORD_SEPARATOR` (lexical_declaration, line 11)
- `HALF` (lexical_declaration, line 12)
- `limit` (lexical_declaration, line 15, exported)
- `lines` (lexical_declaration, line 16, exported)
- `used` (lexical_declaration, line 17, exported)
- `needed` (lexical_declaration, line 19, exported)
- `row` (lexical_declaration, line 31)
- `size` (lexical_declaration, line 32)
- `columns` (lexical_declaration, line 36)
- `heights` (lexical_declaration, line 52, exported)
- `gaps` (lexical_declaration, line 53, exported)
- `total` (lexical_declaration, line 54, exported)
- `top` (lexical_declaration, line 55, exported)
- `boxes` (lexical_declaration, line 56, exported)
- `width` (lexical_declaration, line 59, exported)
- `height` (lexical_declaration, line 60, exported)

## Contained in

- [core/evaluators](https://banes-lab.com/anatomy/social-share/folder-social-share-core-evaluators.md)

## Used by

- [core/factories/page.factory.ts](https://banes-lab.com/source/social-share/core/factories/page.factory.ts.md)
- [core/loaders/card.loader.ts](https://banes-lab.com/source/social-share/core/loaders/card.loader.ts.md)

## Linked from

- [core/evaluators](https://banes-lab.com/anatomy/social-share/folder-social-share-core-evaluators.md)
- [core/factories](https://banes-lab.com/anatomy/social-share/folder-social-share-core-factories.md)
- [core/loaders](https://banes-lab.com/anatomy/social-share/folder-social-share-core-loaders.md)

## Source

```typescript
import type { ColumnBox, ColumnEntry, ColumnRow, ColumnSpec, FrameContext } from "#types/card.types";
import { HOME_PATH, pagePath } from "@banes-lab/web/core/assets/link.assets.ts";
import { MONO_ADVANCE } from "#configuration/constants/card.constants";
import { SITE_HOST } from "#configuration/data/site.data";

export const pageAddress = function pageAddress(page: string): string {
    const path = pagePath(page);
    return path === HOME_PATH ? SITE_HOST : SITE_HOST + path;
};

const WORD_SEPARATOR = " ";
const HALF = 2;

export const wrapCount = function wrapCount(text: string, columns: number): number {
    const limit = Math.max(1, Math.floor(columns));
    let lines = 1;
    let used = 0;
    for (const word of text.split(WORD_SEPARATOR)) {
        const needed = used === 0 ? word.length : used + 1 + word.length;
        if (needed <= limit || used === 0) {
            used = needed;
        } else {
            lines += 1;
            used = word.length;
        }
    }
    return lines;
};

const rowHeight = function rowHeight(spec: ColumnSpec, entry: ColumnEntry, frame: FrameContext): number {
    const row = spec.rows[entry.row];
    const size = row.size * frame.profile.width;
    if (entry.text.length === 0) {
        return size / frame.profile.height;
    }
    const columns = (spec.width * frame.profile.width) / (MONO_ADVANCE * size);
    return (wrapCount(entry.text, columns) * size * row.leading) / frame.profile.height;
};

const rowX = function rowX(spec: ColumnSpec, width: number): number {
    if (spec.align === "center") {
        return spec.x + (spec.width - width) / HALF;
    }
    return spec.align === "right" ? spec.x + spec.width - width : spec.x;
};

export const columnOf = function columnOf(
    spec: ColumnSpec,
    entries: readonly ColumnEntry[],
    frame: FrameContext,
): ReadonlyMap<ColumnRow, ColumnBox> {
    const heights = entries.map((entry) => rowHeight(spec, entry, frame));
    const gaps = entries.map((entry, index) => (index === 0 ? 0 : spec.rows[entry.row].gap));
    const total = heights.reduce((sum, height, index) => sum + height + (gaps[index] ?? 0), 0);
    let top = spec.y - total / HALF;
    const boxes = new Map<ColumnRow, ColumnBox>();
    entries.forEach((entry, index) => {
        top += gaps[index] ?? 0;
        const width = spec.rows[entry.row].width ?? spec.width;
        const height = heights[index] ?? 0;
        boxes.set(entry.row, { height, width, x: rowX(spec, width), y: top });
        top += height;
    });
    return boxes;
};
```
