Kite

window

! The window. Web only. ! ! std/dom is the document; this is everything around it. The ! address bar, the two storages, the history entry, the scroll offset, the ! timers and the events that happen to the page rather than to an element — ! all of them are properties of window, and none of them is a document. ! ! Ordinary Kite over std/js, with no extern declaration in ! this file, exactly as std/dom is written. JsValue does not appear in ! the interface either: the handles below have unmarked fields, so outside ! this module they can be held and passed but not read. ! ! ``kite ignore ! use std/window ! ! fn main() { ! let token = window.kept("session") ! if token == nil { ! window.go_to("/sign-in") ! return ! } ! let (sub, err) = window.on("hashchange", || { route() }) ! if err != nil { ! io.error("no router: \(err.message())") ! } ! } ! ` ! ! What is deliberately absent: anything that is a *recipe* rather than a ! platform fact. Saving a blob through a synthetic anchor, uploading a file, ! reading a <meta>` tag — each is three calls a program can make for itself, ! and each encodes a decision (a byte-order mark, an auth header, a MIME ! type) that a standard library has no business making.

raw

pub fn raw() -> JsValue

The window itself.

The escape hatch, and it is meant to be used as one. The same bargain dom.raw makes: seal the module completely and the first person who needs a property it never wrapped builds a parallel untyped world beside it. One greppable door is a boundary that holds.

Listener

struct Listener

A listener on the window, and the way to take it off again.

Returned rather than discarded because nothing else can cancel it. A listener on an element dies with the element; the window outlives every view a program will ever build, so a handler attached and forgotten runs for the life of the page.

Timer

struct Timer

A pending timer.

on

pub fn on(name: str, handler: fn()) -> (Listener, error)

Listen to something the window does — a hash change, a resize, going offline.

The handler takes nothing, and that is not a simplification: a window event carries no target worth having, and everything a handler needs is readable from the window it just heard from.

on_passive

pub fn on_passive(name: str, handler: fn()) -> (Listener, error)

The same, promising the browser the handler will not cancel the event.

Use this for scroll and for touch. A scroll listener that is not marked passive blocks the compositor until it returns, on every frame of every scroll, because the browser must wait to find out whether the handler will call preventDefault. It is the most expensive single mistake a long list can make, and the fix is one property.

after

pub fn after(ms: int, handler: fn()) -> (Timer, error)

Run something after at least ms milliseconds.

A timer and not requestAnimationFrame, which is the choice worth explaining because the frame callback looks like the better one. A background tab does not run frame callbacks at all — so the common shape of "set a flag, book a frame, clear the flag inside it" stops the moment the reader switches tabs and stays stopped when they come back, with the flag still set and every later call dropped. A timer is throttled in a background tab; it still fires. A program that genuinely wants to paint in step with the display can reach requestAnimationFrame through [raw].

every

pub fn every(ms: int, handler: fn()) -> (Timer, error)

Run something every ms milliseconds, until it is cancelled.

scroll_y

pub fn scroll_y() -> int

How far down the document the window is scrolled.

scroll_x

pub fn scroll_x() -> int

height

pub fn height() -> int

How tall the visible part of the document is.

width

pub fn width() -> int

scroll_to

pub fn scroll_to(x: int, y: int) -> error

Jump to an offset, without smoothing.

Smoothing is absent on purpose. This is what restores a position a reader already had, and animating back to it is the difference between returning to where they were and watching the page go looking for it. A program that wants the animation asks for it through [raw].

origin

pub fn origin() -> str

path

pub fn path() -> str

The path, with no query and no fragment. / when there is none.

query

pub fn query() -> str

The query string, including its ?, or "" when there is none.

hash

pub fn hash() -> str

The fragment, including its #, or "" when there is none.

set_hash

pub fn set_hash(fragment: str) -> error

Go to a fragment.

Writing the fragment rather than calling push, and the difference is a whole navigation model: assigning location.hash adds a history entry and raises hashchange, so a link forward and the Back button arrive at the same handler through the same door. pushState raises nothing, and a program built on it has to remember to do by hand what the browser was about to tell it anyway.

go_to

pub fn go_to(url: str) -> error

Leave for another address entirely.

parameter

pub fn parameter(name: str) -> Option<str>

One parameter out of the query string, or nothing.

Absent and empty are different answers: ?code= is a parameter that is there and says nothing, and an OAuth redirect that lost its code is not the same event as one that never carried a code at all.

escaped

pub fn escaped(text: str) -> str

A component of a URL, percent-encoded.

unescaped

pub fn unescaped(text: str) -> str

The other direction, + read as a space the way a form encodes one.

push

pub fn push(url: str) -> error

Add a history entry. [back] returns to the one before it.

rewrite

pub fn rewrite(url: str) -> error

Rewrite the current entry, leaving no way back to it.

What an OAuth redirect wants: an authorization code is spent the moment it is exchanged, and a Back button that returns to it returns to a failure.

Named rewrite rather than replace because the prelude's replace is about text, and two things that are not the same thing do not get one name.

back

pub fn back() -> error

own_scroll_restoration

pub fn own_scroll_restoration() -> error

Stop the browser restoring the scroll position on its own.

For a program that restores from its own record, keyed by route. The browser's heuristic cannot do that for a virtualized list — the elements it would measure against do not exist yet at the moment it looks.

kept

pub fn kept(key: str) -> Option<str>

Something kept across the browser closing — localStorage.

keep

pub fn keep(key: str, value: str) -> error

Keep it. The error is worth handling: a full store, and a browser configured to refuse storage entirely, both arrive here, and the second is a setting a real reader has switched on.

drop_kept

pub fn drop_kept(key: str) -> error

cached

pub fn cached(key: str) -> Option<str>

Something kept only as long as the tab is open — sessionStorage.

cache

pub fn cache(key: str, value: str) -> error

drop_cached

pub fn drop_cached(key: str) -> error

epoch_ms

pub fn epoch_ms() -> int

Milliseconds since 1970-01-01T00:00:00Z, from the host's own clock.

Not time.now(), which is the runtime's clock: virtual under the bytecode VM and monotonic in a page, so it measures a gap correctly and names a moment not at all. This is the reading [time.civil_of] and its neighbours are written against.

timezone_offset

pub fn timezone_offset() -> int

How far east of UTC this device is, in minutes. Yangon answers 420.

The sign is flipped from the platform's. getTimezoneOffset answers the minutes to add to local time to get UTC, so a device east of Greenwich reports a negative number — which reads backwards at every call site and is a well-known source of dates off by a day. What comes out of here is what std/time asks for: minutes east, positive going east.

epoch_of

pub fn epoch_of(iso: str) -> Option<int>

An ISO-8601 timestamp as milliseconds since the epoch, or nothing.

The host's parser, because it is already there and already correct about the parts of 8601 nobody remembers. Date.parse answers NaN for what it cannot read, and js.as_int is already the question "is this a whole number that survives the crossing" — NaN is not, so an unreadable stamp arrives as an error rather than as a number that has to be compared with itself to be caught.

seems_online

pub fn seems_online() -> bool

Whether the device believes it has a connection.

Famously optimistic: navigator.onLine says true for a laptop attached to a router with no internet behind it. It is the cheap signal and never the authority — what a status light should actually report is whether the last request succeeded.

pub fn print_page() -> error

The browser's own print dialog.

Printing a receipt is printing the page: @media print in the stylesheet decides what is on the paper, so there is nothing to build here.

register_worker

pub fn register_worker(path: str) -> error

Ask the browser to install a service worker.

Registered from Kite rather than from a <script> in the page, so a program's offline story is in the same language as the rest of it.