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.
rawListenerTimeronon_passiveaftereveryscroll_yscroll_xheightwidthscroll_tooriginpathqueryhashset_hashgo_toparameterescapedunescapedpushrewritebackown_scroll_restorationkeptkeepdrop_keptcachedcachedrop_cachedepoch_mstimezone_offsetepoch_ofseems_onlineprint_pageregister_worker
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.
event: strhandler: JsValue
Timer
struct Timer
A pending timer.
id: JsValuerepeating: bool
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.
print_page
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.