Elm bindings for various web APIs
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
examples
src
test
.gitignore
.travis.yml
LICENSE
README.md
elm-package.json

README.md

Deprecation Notice

As previously noted below (under Installation), this package was, from the beginning, outside the mainstream Elm ecosystem, since it could not be installed via the Elm package manager (due to the native package block). It was also apparent that the changes planned for Elm 0.17 would have a significant impact on this package.

Now that more information is available about Elm 0.17, the following things seem clear:

  • As expected, a variety of changes would be required for this package to work with Elm 0.17.

  • The policy of @elm-lang will be that @elm-lang should own all packages which access Web APIs. For this reason, it will continue to be impossible to install this package via Elm's package manager.

  • @elm-lang will provide as much access to Web APIs as @elm-lang considers should be available in Elm, via packages owned by @elm-lang.

Reflecting on this, I find that my preference is not to continue work on this package. If anyone else should want to do something with this code, be my guest.

elm-web-api

The purpose of this package is to expose various standard web APIs to Elm, or document where they are already exposed. For reference, I have mostly relied on the following sources:

Those pages document the various facilities available in a Javascript web environment. In order for Elm to use such facilities, it is necessary to write "native" code. So, I'm plugging away at it -- this is a work in progress, but if it is useful to you, that would be great.

Contents

Installation

Because elm-web-api uses 'native' modules, it cannot be included in the Elm package repository. Thus, you cannot install it using elm-package.

However, you can still install it and use it via the following steps:

  • Download this respository in one way or another.

    • You can download specific releases from the releases page, or just check there for the version history.

    • You can clone from git, and then possibly checkout a specific tag:

      git clone https://github.com/rgrempel/elm-web-api.git
      cd elm-web-api
      git checkout 2.1.0     # If you want a specific release
      
    • Or, you might use git submodules, if you're adept at that. (I wouldn't suggest trying it if you've never heard of them before).

  • Modify your elm-package.json to refer to the src folder.

    You can choose where you want to put the downloaded code, but wherever that is, simply modify your elm-package.json file so that it can find the src folder. So, the "source-directories" entry in your elm-package.json file might end up looking like this:

    "source-directories": [
        "src",
        "elm-web-api/src"
    ],
    

    But, of course, that depends on where you've actually put it, and where the rest of your code is.

  • Modify your elm-package.json to indicate that you're using 'native' modules. To do this, add the following entry to elm-package.json:

    "native-modules": true,
    

Now, doing this would have several implications which you should be aware of.

  • You would, essentially, be trusting me (or looking to verify for yourself) that the native code in this module is of high quality and will not cause run-time errors or other problems.

  • This module will not work with Elm 0.17 without changes, and I am not planning to make those changes (see the deprecation notice above).

  • @elm-lang is planning to provide as much access to web APIs as @elm-lang considers appropriate, using packages owned by @elm-lang.

APIs

WebAPI.AnimationFrame

Bindings for window.requestAnimationFrame() and window.cancelAnimationFrame().

Note that jwmerrill/elm-animation-frame provides for a Signal of animation frames. So, this module provides a Task-oriented alternative.

Other higher-level alternatives include evancz/elm-effects and rgrempel/elm-ticker.

module WebAPI.AnimationFrame where

{-| A task which, when executed, will call `window.requestAnimationFrame()`.
The task will complete when `requestAnimationFrame()` fires its callback, and
will pass along the value provided by the callback.

So, to do something when the callback fires, just add an `andThen` to the task.
-}
task : Task x Time

{-| A more complex implementation of `window.requestAnimationFrame()` which
allows for cancelling the request.

Returns a `Task` which, when executed, will call
`window.requestAnimationFrame()`, and then immediately complete with the
identifier returned by `requestAnimationFrame()`.  You can supply this
identifier to `cancel` if you want to cancel the request.

Assuming that you don't cancel the request, the following sequence of events will occur:

* `window.requestAnimationFrame()` will eventually fire its callback, providing a timestamp
* Your function will be called with that timestamp
* The `Task` returned by your function will be immediately executed
-}
request : (Time -> Task x a) -> Task y Request

{-| Opaque type which represents an animation frame request. -}
type Request

{-| Returns a task which, when executed, will cancel the supplied request
via `window.cancelAnimationFrame()`.
-}
cancel : Request -> Task x ()

WebAPI.Cookie

Wraps the browser's document.cookie object.

module WebAPI.Cookie where

{-| A name for a cookie. -}
type alias Key = String

{-| The value of a cookie. -}
type alias Value = String

{-| Tasks will fail with `Disabled` if the user has disabled cookies, or
with `Error` for other errors.
-}
type Error
    = Disabled
    | Error String

{-| Whether cookies are enabled, according to the browser's `navigator.cookieEnabled`. -}
enabled : Task x Bool

{-| A `Task` which, when executed, will succeed with the cookies, or fail with an
error message if (for instance) cookies have been disabled in the browser.

In the resulting `Dict`, the keys and values are the key=value pairs parsed from
Javascript's `document.cookie`. The keys and values will have been uriDecoded.
-}
get : Task Error (Dict Key Value)

{-| A task which will set a cookie using the provided key and value. The key
and value will both be uriEncoded.

The task will fail with an error message if cookies have been disabled in the
browser.
-}
set : Key -> Value -> Task Error ()

{-| A task which will set a cookie using the provided options, key, and value.
The key and value will be uriEncoded, as well as the path and domain options
(if provided).

The task will fail with an error message if cookies have been disabled in
the browser.
-}
setWith : Options -> Key -> Value -> Task Error ()

{-| Options which you can provide to setWith. -}
type alias Options =
    { path : Maybe String
    , domain : Maybe String
    , maxAge : Maybe Time
    , expires : Maybe Date 
    , secure : Maybe Bool
    }

{-| The default options, in which all options are set to Nothing.

You can use this as a starting point for `setWith`, in cases where you only
want to specify some options.
-}
defaultOptions : Options

WebAPI.Date

Generally speaking, dates are dealt with by the Date and Time modules in elm-lang/core.

See Mozilla documentation.

Truly dealing with all the complexity of dates requires something that goes far beyond the Javascript API -- for instance, one could ideally think in terms of wrapping (or, even better, porting) the moment.js library (or something even better).

That isn't in scope for this module. Instead, this is intended merely to be a thin wrapper over the Javascript Date API, such as it is.

module WebAPI.Date where

{- --------------------------------- 
   Getting the current date and time
   --------------------------------- -}

{-| Get the current date, via the browser's `new Date()` -}
current : Task x Date

{-| Get the current time, via the browser's `Date.now()` -}
now : Task x Time

{- ---------
   Timezones 
   --------- -}

{-| The Javascript API allows you to perform certain operations in terms of
the "local" timezone, or in terms of UTC. So, where we wrap those APIs, we use
this type to let you pick (rather than having separate functions). Of course,
you can use partial application to create separate functions if you like.

Note that this isn't the kind of support that a more sophisticated library
would have for timezones -- it merely wraps what Javascript provides.
-}
type Timezone
    = Local
    | UTC

{-| Javascript's `getTimezoneOffset()`.

This represents what Javascript thinks is the offset between UTC and local time,
for the specified date. It can differ from date to date depending on whether
daylight savings time is in effect on that date.

Note that this is in units of `Time`, so you can scale via `Time.inMinutes` etc.
-}
timezoneOffset : Date -> Time

{- -------------------
   The parts of a date 
   ------------------- -}

{-| The parts of a date.

Note that (as in the Javascript APIs) the month is 0-based, with January = 0.
-}
type alias Parts =
    { year : Int
    , month : Int
    , day : Int
    , hour : Int
    , minute : Int
    , second : Int
    , millisecond : Int
    }

{-| Construct a `Date` from the provided parts, using the specified timezone.

For `Local`, this uses `new Date(...)`.

For `UTC`, this uses `new Date(Date.UTC(...))`.
-}
fromParts : Timezone -> Parts -> Date

{-| Break a `Date` up into its parts, using the specified timezone.

For `Local`, this uses `getFullYear()`, `getMonth()`, etc.

For `UTC`, this uses `getUTCFullYear()`, `getUTCMonth()`, etc.
-}
toParts : Timezone -> Date -> Parts

{-| Get the day of the week corresopnding to a `Date`.

This is handled separately from `Parts` because it is not symmetrical --
it makes no sense for there to be a constructor based on this.
-}
dayOfWeek : Timezone -> Date -> Date.Day

{-| Converts from Javascript's 0-based months (where January = 0) to`Date.Month`. -}
toMonth : Int -> Date.Month

{-| Converts from `Date.Month` to Javascript's 0-based months (where January = 0). -}
fromMonth : Date.Month -> Int

{-| Converts from Javascript's 0-based days (where Sunday = 0) to`Date.Day`. -}
toDay : Int -> Date.Day

{-| Converts from `Date.Day` to Javascript's 0-based days (where Sunday = 0). -}
fromDay : Date.Day -> Int

{- ---------------
   Date arithmetic
   --------------- -}

{-| Offset the `Date` by the supplied `Time` (i.e. positive values offset into
the future, and negative values into the past).

You can use `day`, `week`, `Time.minute`, etc. to scale. However, that won't
always do what you actually want, since the values are treated as durations,
rather than human-oriented intervals. For instance, `offsetTime (365 * day)
date` will advance the date by 365 days. However, if a leap year is involved,
the resulting date might be a different day of the year. If you actually want
the same day in the next year, then use `offsetYear` instead.
-}
offsetTime : Time -> Date -> Date

{-| Offset the `Date` by the specified number of years (forward or backward),
using Javascript's `setFullYear()` and `getFullYear()` (or `getUTCFullYear()`
and `setUTCFullYear`).

Leap years are handled by the underlying Javascript APIs as follows:

* If the supplied date is February 29, and the target year has no February 29,
  then you'll end up with March 1 in the target year. (Arguably, you might
  prefer February 28, but I'm not sure there is a clearly correct answer).

* If the supplied date is February 29, and the target year also has a February
  29, then you'll end up with February 29.

* The year is interpreted in terms of either the local timezone or UTC, according
  to what you specify. I think the only case in which this could make a
  difference is in determining whether it is February 29.

* If the offset "crosses" a leap day, then you'll end up with the "same" day in
  the target year ... for instance, `offsetYear 1` will sometimes move 365
  days and sometimes 366 days, depending on whether a leap year is involved.

A more sophisticated module might deal with these cases a little differently.
-}
offsetYear : Timezone -> Int -> Date -> Date

{-| Offset the `Date` by the specified number of months (forward or backward),
using Javascript's `setMonth()` and `getMonth()` (or `setUTCMonth()` and
`getUTCMonth()`).

Here are a few notes about the underlying Javascript implementation:

* Overflow and underflow basically do the right thing. That is, if you end up
  with negative numbers, the year is decremented, and if you end up with
  numbers past 11, the year is incremented. (Remember that Javascript months
  are 0-based). And, in either case, the month is set to something between
  0 and 11.

* Dates at the beginning of the month are handled as you might expect. For
  instance, adding 1 month to January 1 produces February 1, and adding 1 month
  to February 1 produces March 1. Thus, the actual number of days added can vary,
  depending on the length of the month.

* However, dates at the end of the month are handled in a way that could seem
  odd. For instance, adding 1 month to August 31 produces October 1 ... were
  you expecting September 30? That would probably be more useful, and a more
  sophisticated library might arrange for that.

* Note that the date is interpreted according to either the local timezone or UTC,
  as you specify. In some cases, that will affect whether the date is
  considered to be the last day of the month, or the first day of the next
  month, which will in turn affect whether the "end of month" anomaly is
  triggered.
-}
offsetMonth : Timezone -> Int -> Date -> Date

{- ---------------------------
   Some additional time scales
   --------------------------- -}

{-| A convenience for arithmetic, analogous to `Time.hour`, `Time.minute`, etc. -}
day : Time

{-| A convenience for arithmetic, analogous to `Time.inHours`, `Time.inMinutes`, etc. -}
inDays : Time -> Float

{-| A convenience for arithmetic, analogous to `Time.hour`, `Time.minute`, etc. -}
week : Time

{-| A convenience for arithmetic, analogous to `Time.inHours`, `Time.inMinutes`, etc. -}
inWeeks : Time -> Float

{- ------------------
   String conversions
   ------------------ -}

{-| The browser's `toDateString()` -}
dateString : Date -> String

{-| The browser's `toTimeString()` -}
timeString : Date -> String

{-| The browser's `toISOString()` -}
isoString : Date -> String

{-| The browser's `toUTCString()` -}
utcString : Date -> String

{- ----
   JSON
   ---- -}

{-| Extract a date. -}
decoder : Json.Decode.Decoder Date

{-| Encode a date. -}
encode : Date -> Json.Encode.Value

See also

new Date(String)

        Use Date.fromString from elm-lang/core.

new Date(Number)

        Use Date.fromTime from elm-lang/core.

getDate()

        Use Date.day from elm-lang/core, or toParts Local >> .day

getDay()

        Use Date.dayOfWeek from elm-lang/core, or dayOfWeek Local

getFullYear()

        Use Date.year from elm-lang/core, or toParts Local >> .year

getHours()

        Use Date.hour from elm-lang/core, or toParts Local >> .hour

getMilliseconds()

        Use Date.millisecond from elm-lang/core, or toParts Local >> .millisecond

getMinutes()

        Use Date.minute from elm-lang/core, or toParts Local >> .minute

getMonth()

        Use Date.month from elm-lang/core, or toParts Local >> .month

getSeconds()

        Use Date.second from elm-lang/core, or toParts Local >> .second

getTime()

        Use Date.toTime from elm-lang/core.

getUTCDate(), getUTCFullYear(), getUTCHours(), getUTCMilliseconds(), getUTCMinutes(), getUTCMonth(), getUTCSeconds()

        Use toParts UTC, and then pick out whichever things you need from the resulting Parts.

getUTCDay()

        Use dayOfWeek UTC

parse()

        Use Date.fromString from elm-lang/core.

setDate(), setDay(), setFullYear(), setHours(), setMilliseconds(), setMinutes(), setMonth(), setSeconds()

        What I would suggest is

  • Use toParts Local
  • Update whatever fields you with to update
  • Use fromParts Local to create a new Date

Alternatively, in some scenarios you could use offsetYear Local, offsetMonth Local or offsetTime.

setUTCDate(), setUTCDay(), setUTCFullYear(), setUTCHours(), setUTCMilliseconds(), setUTCMinutes(), setUTCMonth(), setUTCSeconds()

        What I would suggest is

  • Use toParts UTC
  • Update whatever fields you with to update
  • Use fromParts UTC to create a new Date

Alternatively, in some scenarios you could use offsetYear UTC, offsetMonth UTC or offsetTime.

toLocaleString(), toLocaleDateString(), toLocaleTimeString()

        These aren't supported by Safari, so I've left them out for the moment.


WebAPI.Document

See Mozilla documentation for the Document object.

Since the browser's document object has so many facilities attached, I've split some of them up into individual modules -- see below for the cross-references.

TODO: Finish going through the document API.

module WebAPI.Document where

{- -------
   Loading
   ------- -}

{-| Possible values for the browser's `document.readyState` -}
type ReadyState
    = Loading
    | Interactive
    | Complete

{-| A `Signal` of changes to the browser's `document.readyState` -}
readyState : Signal ReadyState

{-| A task which, when executed, succeeds with the value of the browser's
`document.readyState`.
-}
getReadyState : Task x ReadyState

{-| A task which succeeds when the `DOMContentLoaded` event fires. If that
event has already fired, then this succeeds immediately.

Note that you won't usually need this in the typical Elm application in which
it is Elm itself that generates most of the DOM. In that case, you'll just
want to make some `Task` run when the app starts up. If you're using
`StartApp`, then that would be accomplished by supplying an `Effects` as part
of the `init` when you call `StartApp.start`.
-}
domContentLoaded : Task x ()

{-| A task which succeeds when the `load` event fires. If that event has
already fired, then this succeeds immediately.
-}
loaded : Task x ()

{- ------
   Titles
   ------ -}

{-| A task which, when executed, succeeds with the value of `document.title`. -}
getTitle : Task x String

{-| A task which, when executed, sets the value of `document.title` to the
supplied `String`.
-}
setTitle : String -> Task x ()

{- ------
   Events
   ------ -}

{-| A target for responding to events sent to the `document` object. -}
target : WebAPI.Event.Target

{- ----
   JSON
   ---- -}

{-| Access the Javascript `document` object via `Json.Decode`. -}
value : Task x Json.Decode.Value

See also

cookie

        See WebAPI.Cookie


WebAPI.Event

General support for handling Javascript events.

There are more specific modules available for more specific types of events -- for instance, WebAPI.Event.BeforeUnload for the BeforeUnloadEvent. So, if you're interested in a specific type of event, check there first.

Also, there are specific modules available for some targets. For instance, WebAPI.Window has some convenient event-handling methods.

Furthermore, if you are using evancz/elm-html, this is not really meant for targets that are within the Html that your view function produces. For those, use Html.Events to deal with events. Instead, this is meant for events on target that you don't set up in your view function, such as the window and document etc. Though, of course, you could possibly achieve some interesting results by setting up listeners on the document or window and relying on bubbling.

See Mozilla documentation for the EventTarget interface, and for Event. I also found this list of events helpful.

Note: I am currently working on the event-related APIs, so you should expect some API changes in future versions.

module WebAPI.Event where

{- -----
   Event
   ----- -}

{-| Opaque type representing a Javascript event. -}
type Event

{-| The type of the event. -}
eventType : Event -> String

{-| Does the event bubble up through the DOM? -}
bubbles : Event -> Bool

{-| Can the event be canceled? -}
cancelable : Event -> Bool

{-| The time when the event was created. -}
timestamp : Event -> Time

{-| The phases in which an event can be processed. -}
type EventPhase
    = NoPhase
    | Capturing
    | AtTarget
    | Bubbling

{-| The phase in which the event is currently being processed.

Note that typically an undispatched `Event` will return `NoPhase`, but in
Opera will return `AtTarget`.
-}
eventPhase : Event -> EventPhase

{-| Has `preventDefault()` been called on this event? -}
defaultPrevented : Event -> Bool

{-| The target that the event was originally dispatched to. -}
eventTarget : Event -> Maybe Target

{-| The target that the current event listener was attached to. This may differ
from the target which originally received the event, if we are in the bubbling
or capturing phase.
-}
listenerTarget : Event -> Maybe Target

{- ----------------------------
   Constructing and Dispatching
   ---------------------------- -}

{-| Create an event with the given selector and options. -}
construct : Selector event -> Options event -> Task x event

{-| Options for creating an event. -}
type alias Options event

{-| Specify options for constructing an `Event`. -}
options : {cancelable : Bool, bubbles : Bool} -> Options Event

{-| Default options, in which `cancelable` and `bubbles` are both false. -}
defaultOptions : Options Event

{-| A task which dispatches an event, and completes when all the event handlers
have run. The task will complete with `True` if the default action should be
permitted.  If any handler calls `preventDefault()`, the task will return
`False`. The task will fail if certain exceptions occur.

To dispatch an event from a sub-module, use the submodule's `toEvent` method.
For instance, to dispatch a `CustomEvent`, do something like:

    dispatchCustomEvent : Target -> CustomEvent -> Task String Bool
    dispatchCustomEvent target customEvent =
        WebAPI.Event.dispatch target (WebAPI.Event.CustomEvent.toEvent customEvent)
-}
dispatch : Target -> Event -> Task String Bool

{- ---------
   Listening
   --------- -}

{-| Opaque type which represents a Javascript object which can respond to
Javascript's `addEventListener()` and `removeEventListener()`.

To obtain a `Target`, see methods such as `WebAPI.Document.target` and
`WebAPI.Window.target`.
-}
type Target

{-| A task which, when executed, uses Javascript's `addEventListener()` to add
a `Responder` to the `Target` for the event specified by the `Selector`.

Succeeds with a `Listener`, which you can supply to `removeListener` if you
wish.
-}
addListener : ListenerPhase -> Selector event -> Responder event -> Target -> Task x Listener

{-| Convenience method for the usual case in which you call `addListener`
for the `Bubble` phase.
-}
on : Selector event -> Responder event -> Target -> Task x Listener

{-| Like `addListener`, but only responds to the event once, and the resulting
`Task` only succeeds when the event occurs (with the value of the event object).
Thus, your `Responder` method might not need to do anything.
-}
addListenerOnce : ListenerPhase -> Selector event -> Responder event -> Target -> Task x event

{-| Like `addListenerOnce`, but supplies the default `Phase` (`Bubble`), and a
`Responder` that does nothing (so you merely chain the resulting `Task`).
-}
once : Selector event -> Target -> Task x event

{-| Opaque type representing an event name which uses an event type. -}
type alias Selector event

{-| Select an event name using the `Event` type.

You can handle any event name with the `Event` type if you like, but often
you should use a more specific event type. For instance, for 'beforeunload`,
you should use `WebAPI.Event.BeforeUnload.select`, so that you can use a
`BeforeUnloadEvent` in your `Responder`.
-}
select : String -> Selector Event

{-| Opaque type representing an event handler. -}
type Listener

{-| The type of the listener's event. -}
listenerType : Listener -> String

{-| The listener's target. -}
target : Listener -> Target

{-| The phases in which a `Responder` can be invoked. Typically, you will want `Bubble`. -}
type ListenerPhase
    = Capture
    | Bubble

{-| The listener's phase. -}
listenerPhase : Listener -> ListenerPhase

{-| A task which will remove the supplied `Listener`.

Alternatively, you can return `remove` from your `Responder` method, and the
listener will be removed.
-}
removeListener : Listener -> Task x ()

{- ----------
   Responding
   ---------- -}

{-| A function which will be called each time an event occurs, in order to
determine how to respond to the event.

* The `event` parameter is the Javascript event object.
* The `Listener` is the listener which is responsible for this event.

Your function should return a list of responses which you would like to make
to the event.
-}
type alias Responder event =
    event -> Listener -> List Response

{-| Opaque type which represents a response which you would like to make to an event. -}
type Response

{-| Indicates that you would like to set a property on the event object with
the specified key to the specified value.

Normally, you should not need this. However, there are some events which need
to be manipulated in this way -- for instance, setting the `returnValue` on the
`beforeunload` event.
-}
set : String -> Json.Encode.Value -> Response

{-| Indicates that you would like to send a message in response to the event. -}
send : Signal.Message -> Response

{-| Indicates that you would like to perform a `Task` in response to the event.

If the task is to send a message via `Signal.send`, then you can use `send` as
a convenience.
-}
performTask : Task () () -> Response

{-| Indicates that no longer wish to listen for this event on this target. -}
remove : Response

{-| Indicates that you would like to prevent further propagation of the event. -}
stopPropagation : Event -> Response

{-| Like `stopPropagation`, but also prevents other listeners on the current
target from being called.
-}
stopImmediatePropagation : Event -> Response

{-| Cancels the standard behaviour of the event. -}
preventDefault : Event -> Response

{-| A responder that does nothing. -}
noResponse : event -> Listener -> List Response

{- ----
   JSON
   ---- -}

{-| Encode an event. -}
encode : Event -> Json.Encode.Value

{-| Decode an event. -}
decoder : Json.Decode.Decoder Event

WebAPI.Event.BeforeUnload

The browser's `BeforeUnloadEvent'.

See Mozilla documentation.

See WebAPI.Window.beforeUnload and WebAPI.Window.confirmUnload for a higher-level, more convenient API.

module WebAPI.Event.BeforeUnload where

{- -----------------
   BeforeUnloadEvent
   ----------------- -}

{-| Opaque type representing a BeforeUnloadEvent. -}
type BeforeUnloadEvent

{-| Select the 'beforeunload' event. -}
select : Selector BeforeUnloadEvent

{- ----------
   Responding
   ---------- -}

{-| Provide a prompt to use in the confirmation dialog box before leaving tha page. -}
prompt : String -> BeforeUnloadEvent -> Response

{- ----------
   Conversion
   ---------- -}

{-| Convert to an `Event` in order to use `Event` functions. -}
toEvent : BeforeUnloadEvent -> WebAPI.Event.Event

{-| Convert from an `Event`. -}
fromEvent : Event -> Maybe BeforeUnloadEvent

{- ----
   JSON
   ---- -}

{-| Encode a BeforeUnloadEvent. -}
encode : BeforeUnloadEvent -> Json.Encode.Value

{-| Decode a BeforeUnloadEvent. -}
decoder : Json.Decode.Decoder BeforeUnloadEvent

WebAPI.Event.Custom

The browser's `CustomEvent'.

See Mozilla documentation.

module WebAPI.Event.Custom where

{- -----------
   CustomEvent
   ----------- -}

{-| Opaque type representing a CustomEvent. -}
type CustomEvent

{-| Data set when the `CustomEvent` was created. -}
detail : CustomEvent -> Json.Decode.Value

{-| Specify options for creating a `CustomEvent` with the given detail. -}
options : Json.Encode.Value -> Options Event -> Options CustomEvent

{-| Select a `CustomEvent` with the given event type. -}
select : String -> Selector CustomEvent

{- ----------
   Conversion
   ---------- -}

{-| Convert to an `Event` in order to use `Event` functions. -}
toEvent : CustomEvent -> Event

{-| Convert from an `Event`. -}
fromEvent : Event -> Maybe CustomEvent

{- ----
   JSON
   ---- -}

{-| Encode a CustomEvent. -}
encode : CustomEvent -> Json.Encode.Value

{-| Decode a CustomEvent. -}
decoder : Json.Decode.Decoder CustomEvent

WebAPI.Function

Support for Javascript functions. Basically, this provides two capabilities:

  • You can call Javascript functions from Elm.
  • You can provide Elm functions to Javascript to be called back from Javascript.

See Mozilla documentation

module WebAPI.Function where

{- -----
   Types
   ----- -}

{-| Opaque type representing a Javascript function. -}
type Function

{-| The number of arguments expected by a function. -}
length : Function -> Int

{-| Opaque type representing a Javascript exception. -}
type Error

{-| Construct an error to be thrown in a Javascript callback. Normally, one
does not want errors to be thrown. However, there may be some Javascript APIs
that expect callbacks to throw errors. So, this makes that possible.
-}
error : String -> Error

{-| Gets an error's message. -}
message : Error -> String

{- -----------------------------------
   Obtaining functions from Javascript 
   ----------------------------------- -}

{-| Produce a Javascript function given a list of parameter names and a
Javascript function body.
-}
javascript : List String -> String -> Result Error Function

{-| Like `javascript`, but crashes if the Javascript won't compile. -}
unsafeJavascript : List String -> String -> Function

{-| Extract a function. -}
decoder : JD.Decoder Function

{- -----------------
   Calling Functions
   ----------------- -}

{-| Call a function, using the supplied value for "this", and the supplied
parameters. If you don't want to supply a `this`, you could use
`Json.Encode.null`.
-}
apply : JE.Value -> List JE.Value -> Function -> Task Error JD.Value

{-| Call a 'pure' function, using the supplied value for "this", and the
supplied parameters. If you don't want to supply a `this`, you could use
`Json.Encode.null`.

It is your responsibility to know that the function is 'pure' -- that is:

* it has no side-effects
* it does not mutate its arguments (or anything else)
* it returns the same value for the same arguments every time

The type-checker can't verify this for you. If you have any doubt, use
`apply` instead.
-}
pure : JE.Value -> List JE.Value -> Function -> Result Error JD.Value

{-| Use a function as a constructor, via `new`, with the supplied parameters. -}
construct : List JE.Value -> Function -> Task Error JD.Value

{- ---------------------------------
   Providing functions to Javascript
   --------------------------------- -}

{-| Encode a function. -}
encode : Function -> JE.Value

{-| Given an Elm implementation, produce a function which can be called back
from Javascript.
-}
elm : Callback -> Function

{-| An Elm function which can be supplied to Javascript code as a callback.

When the function is invoked from Javascript, the parameter will be a
Javascript array in which the first element is whatever `this` was, and the
remaining elements are the parameters to the javascript function. So, you'll
want to apply some `Json.Decode` decoders to get Elm types out of that.

Since you're being given an array which you should independently know the
length of, you'll probably want to make use of `Json.Decode.tuple1`,
`Json.Decode.tuple2`, etc., depending on how many arguments you're expecting.
And, remember that the first element of the Javascript array is not the first
argument, but instead whatever `this` was, so you may or may not be interested
in it. If you just want to ignore it, you could use `Json.Decode.succeed`.

For instance, let's suppose you're expecting 2 parameters which are integers,
and you don't care about `this`. In that case, you might decode with:

    JD.tuple3 (,,) (JD.succeed Nothing) JD.int JD.int

When running `Json.Decode.decodeValue`, you'd then end up with a
`(Maybe a, Int, Int)` or an error.

Your Elm function should return a `Response`, which controls the return value
of the Javascript function, and allows for the execution of a `Task`.
-}
type alias Callback =
    JD.Value -> Response

{-| An opaque type representing your response to a function invocation from
Javascript, i.e. a response to a callback.
-}
type Response

{-| Respond to a Javascript function call with the supplied return value. -}
return : JE.Value -> Response

{-| Respond to a Javascript function call by throwing an error. Normally,
you do not want to throw Javascript errors, but there may be some Javascript
APIs that expect callbacks to do so. This makes it possible.
-}
throw : Error -> Response

{-| Respond to a Javascript function call using the supplied return value,
and also perform a `Task`.

This is like `return`, except that the supplied `Task` is also immediately
performed when the Javascript function is called. The `Task` is presumed to be
asynchronous, so its completion does not affect the return value used for the
Javascript function.

If you want to 'promote' the callback into the normal flow of your app, you
might want to use `Signal.send` to send an action to an address. (Note that
`Signal.send` is asynchronous).
-}
asyncAndReturn : Task () () -> JE.Value -> Response

{-| Respond to a Javascript function call by throwing an error,
and also perform a `Task`.

This is like `asyncReturn`, except an error will be thrown.
-}
asyncAndThrow : Task () () -> Error -> Response

{-| Respond to a Javascript function call by executing a `Task`, and using
the completion of the `Task` to control the function's return value.

If the `Task` succeeds, the result will be used as the return value for the
Javascript function. This allows you to chain other tasks in order to provide
a return value -- so long as the other tasks are synchronous.

If the `Task` fails, the result will be thrown as an error. Normally, one does
not want to throw Javascript errors, but there may be some Javascript APIs that
expect callbacks to do so.

If the `Task` turns out to be asynchronous -- that is, if it fails to complete
before the Javascript function returns -- then the supplied `JE.Value` will be
used as the default return value.
-}
syncOrReturn : Task Error JE.Value -> JE.Value -> Response

{-| Respond to a Javascript function call by executing a `Task`, and using
the completion of the `Task` to control the function's return value.

If the `Task` succeeds, the result will be used as the return value for the
Javascript function. This allows you to chain other tasks in order to provide
a return value -- so long as the other tasks are synchronous.

If the `Task` fails, the result will be thrown as an error. Normally, one does
not want to throw Javascript errors, but there may be some Javascript APIs that
expect callbacks to do so.

If the `Task` turns out to be asynchronous -- that is, if it fails to complete
before the Javascript function returns -- then the supplied `Error` will be
thrown.
-}
syncOrThrow : Task Error JE.Value -> Error -> Response

WebAPI.JSON

Generally speaking, JSON is handled by the Json.Decode and Json.Encode modules in elm-lang/core.

See Mozilla documentation.

Json.Decode and Json.Encode are a little more interesting than you might think by their names alone. Essentially, they provide a type-safe way for moving back and forth between Javascript objects (not necessarily strings) and Elm types.

elm-web-api doesn't have a separate module for JSON. However, there are some helpers sprinkled throughout, which I will list here for convenience.

{-| Extract a date. -}
WebAPI.Date.decoder : Json.Decode.Decoder Date

{-| Encode a date. -}
WebAPI.Date.encode : Date -> Json.Encode.Value

{-| Access the Javascript `window` object via `Json.Decode`. -}
WebAPI.Window.value : Task x Json.Decode.Value

{-| Access the Javascript `document` object via `Json.Decode`. -}
WebAPI.Document.value : Task x Json.Decode.Value

{-| Extract a function. -}
WebAPI.Function.decoder : Json.Decode.Decoder Function

{-| Encode a function. -}
WebAPI.Function.encode : Function -> Json.Encode.Value

WebAPI.Location

See the Mozilla documentation.

Note that there is a Signal-oriented library for location-related things at elm-community/elm-history.

module WebAPI.Location where

{-| The parts of a location object. Note `port'`, since `port` is a reserved word. -}
type alias Location =
    { href: String
    , protocol: String
    , host: String
    , hostname: String
    , port': String
    , pathname: String
    , search: String
    , hash: String
    , origin: String
    }

{-| The browser's `window.location` object. -}
location : Task x Location

{-| Reloads the page from the current URL. -}
reload : Source -> Task String ()

{-| Whether to force `reload` to use the server, or allow the cache. -}
type Source
    = ForceServer
    | AllowCache

{-| A task which, when executed, loads the resource at the provided URL,
or provides an error message upon failure.

Note that only Firefox appears to reliably report an error -- other browsers
silently fail if an invalid URL is provided.
-}
assign : String -> Task String ()

{-| Like `assign`, loads the resource at the provided URL, but replaces the
current page in the browser's history.

Note that only Firefox appears to reliably report an error -- other browsers
silently fail if an invalid URL is provided.
-}
replace : String -> Task String ()

WebAPI.Math

See the Mozilla documentation for the Math object. Note that things marked "experimental" there have been omitted here.

module WebAPI.Math where

{-| Returns E to the power of x, where x is the argument, and E is Euler's
constant (2.718…), the base of the natural logarithm.
-}
exp : number -> Float

{-| Natural logarithm of 2, approximately 0.693. -}
ln2 : Float

{-| Natural logarithm of 10, approximately 2.303. -}
ln10 : Float

{-| Returns the natural logarithm (log e, also ln) of a number. -}
log : number -> Float

{-| Base 2 logarithm of E, approximately 1.443. -}
log2e : Float

{-| Base 10 logarithm of E, approximately 0.434. -}
log10e : Float

{-| Returns a pseudo-random number between 0 and 1.

Note that there is a more sophisticated implementation of `Random` in
[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest).
However, this may sometimes be useful if you're in a `Task` context anyway.
-}
random : Task x Float

{-| Square root of 1/2; equivalently, 1 over the square root of 2,
approximately 0.707.
-}
sqrt1_2 : Float

{-| Square root of 2, approximately 1.414. -}
sqrt2 : Float

See also

abs

        Use Basics.abs in elm-lang/core.

acos

        Use Basics.acos in elm-lang/core.

asin

        Use Basics.asin in elm-lang/core.

atan

        Use Basics.atan in elm-lang/core.

atan2

        Use Basics.atan2 in elm-lang/core.

ceil

        Use Basics.ceiling in elm-lang/core.

cos

        Use Basics.cos in elm-lang/core.

E

        Use Basics.e in elm-lang/core.

floor

        Use Basics.floor in elm-lang/core.

max

        Use List.maximum in elm-lang/core.

min

        Use List.minimum in elm-lang/core.

PI

        Use Basics.pi in elm-lang/core.

pow

        Use Basics.(^) in elm-lang/core.

round

        Use Basics.round in elm-lang/core.

sin

        Use Basics.sin in elm-lang/core.

sqrt

        Use Basics.sqrt in elm-lang/core.

tan

        Use Basics.tan in elm-lang/core.


WebAPI.Number

See the Mozilla documentation for the Number object. Note that things marked "experimental" there have been omitted here.

module WebAPI.Number where

{-| The largest positive representable number. -}
maxValue : Float

{-| The smallest positive representable number - that is, the positive number
closest to zero (without actually being zero).
-}
minValue : Float

{-| Special "not a number" value. -}
nan : Float

{-| Special value representing negative infinity; returned on overflow. -}
negativeInfinity : Float

{-| Special value representing infinity; returned on overflow. -}
positiveInfinity : Float

{-| A string representing the provided number in exponential notation. -}
toExponential : number -> String

{-| Either a string representing the second parameter in exponential notation,
with the requested number of digits after the decimal point (first parameter),
or an error. An error should not occur if the requested number of digits is
between 0 and 20.
-}
toExponentialDigits : Int -> number -> Result String String

{-| A string representing the second parameter in exponential notation,
with the requested number of digits after the decimal point (first parameter).
The number of digits will be limited to between 0 and 20.
-}
safeExponentialDigits : Int -> number -> String

{-| A string representing the provided number in fixed-point notation. -}
toFixed : number -> String

{-| Either a string representing the second parameter in fixed-point notation,
with the requested number of digits after the decimal point (first parameter),
or an error. An error should not occur if the requested number of digits is
between 0 and 20.

Note that Firefox returns `Ok 0` rather than an `Error` for a negative number
of requested digits.
-}
toFixedDigits : Int -> number -> Result String String

{-| A string representing the second parameter in fixed-point notation,
with the requested number of digits after the decimal point (first parameter).
The number of digits will be limited to between 0 and 20.
-}
safeFixedDigits : Int -> number -> String

{-| Either a string representing the second parameter in fixed-point or
exponential notation, rounded to the requested number of significant digits
(first parameter), or an error. An error should not occur if the requested
number of digits is between 0 and 20.
-}
toPrecisionDigits : Int -> number -> Result String String

{-| A string representing the second parameter in fixed-point or exponential
notation, with the requested number of significant digits (first parameter).
The number of digits will be limited to between 1 and 20.
-}
safePrecisionDigits : Int -> number -> String

{-| Either a string representing the second parameter using the requested base
(first parameter), or an error. An error should not occur if the requested base
is between 2 and 36.
-}
toStringUsingBase : Int -> number -> Result String String

{-| A string representing the second parameter, using the requested base
(first parameter).  The requested base will be limited to between 2 and 36.
-}
safeStringUsingBase : Int -> number -> String

See also

toLocaleString

        Not implemented for the moment, since localization requires some thought.


WebAPI.Screen

See Mozilla documentation.

module WebAPI.Screen where

type alias Screen =
    { availTop: Int
    , availLeft: Int
    , availHeight: Int
    , availWidth: Int
    , colorDepth: Int
    , pixelDepth: Int
    , height: Int
    , width: Int
    }

{-| The browser's `window.screen` object.

This is a `Task` because, in multi-monitor setups, the result depends on which
screen the browser window is in. So, it is not necessarily a constant.
-}
screen : Task x Screen

{-| A tuple of `(window.screenX, window.screenY)`.

The first value is the horizontal distance, in CSS pixels, of the left border
of the user's browser from the left side of the screen.

The second value is the vertical distance, in CSS pixels, of the top border of
the user's browser from the top edge of the screen.
-}
screenXY : Task x (Int, Int)

WebAPI.Storage

Facilities from the browser's storage areas (localStorage and sessionStorage).

See the Mozilla documentation and WhatWG documentation.

Note that there is a more sophisticated storage module available at TheSeamau5/elm-storage.

module WebAPI.Storage where

{- -----------------
   Roles for Strings
   ----------------- -}

{-| A key. -}
type alias Key = String

{-| An old value. -}
type alias OldValue = String

{-| A new value. -}
type alias NewValue = String

{-| A value. -}
type alias Value = String

{- -------------
   Storage Areas 
   ------------- -}

{-| Represents the `localStorage` and `sessionStorage` areas. -}
type Storage
    = Local
    | Session

{-| The browser's `localStorage` area. -}
local : Storage

{-| The browser's `sessionStorage` area. -}
session : Storage

{- ------
   Errors
   ------ -}

{-| Possible error conditions.

* `Disabled` indicates that the user has disabled storage.
* `QuotaExceeded` indicates that the storage quota has been exceeded.
* `Error` indicates that some other kind of error occurred.
-}
type Error
    = Disabled
    | QuotaExceeded
    | Error String

{-| Indicates whether storage is enabled. (It can be disabled by the user). -}
enabled : Task x Bool

{- -----
   Tasks
   ----- -}

{-| A task which, when executed, determines the number of items stored in the
storage area.
-}
length : Storage -> Task Error Int

{-| A task which, when executed, determines the name of the key at the given
index (zero-based).

Succeeds with `Nothing` if the index is out of bounds.
-}
key : Storage -> Int -> Task Error (Maybe Key)

{-| A task which, when executed, gets the value at the given key.

Succeeds with `Nothing` if the key is not found.
-}
get : Storage -> Key -> Task Error (Maybe Value)

{-| A task which, when executed, sets the value at the given key, or fails with
an error message.
-}
set : Storage -> Key -> NewValue -> Task Error ()

{-| A task which, when executed, removes the item with the given key. -}
remove : Storage -> Key -> Task Error ()

{-| A task which, when executed, removes all items. -}
clear : Storage -> Task Error ()

{- ------
   Events
   ------ -}

{-| A storage event. -}
type alias Event =
    { area : Storage
    , url : String
    , change : Change
    }

{-| A change to a storage area. -}
type Change
    = Add Key NewValue
    | Remove Key OldValue
    | Modify Key OldValue NewValue
    | Clear

{-| A signal of storage events.

Note that a storage event is not fired within the same document that made a
storage change. Thus, you will only receive events for localStorage changes
that occur in a **separate** tab or window.

This behaviour reflects how Javascript does things ... let me know if you'd
prefer to have *all* localStorage events go through this `Signal` -- it could
be arranged.

At least in Safari, sessionStorage is even more restrictive than localStorage
-- it is isolated per-tab, so you will only get events on sessionStorage if
using iframes.

Note that this signal emits `Maybe Event` (rather than `Event`) because Elm
signals must have an initial value -- and there is no natural initial value for
an `Event` unless we wrap it in a `Maybe`. So, you'll often want to use
`Signal.filterMap` when you're integrating this into your own signal of
actions.

If the user has disabled storage, then nothing will ever be emitted on the
signal.
-}
events : Signal (Maybe Event)

WebAPI.Window

See Mozilla documentation for the Window object, and for function properties.

Since the browser's window object has so many facilities attached, I've typically split them up into individual modules -- see below for the cross-references.

TODO: Finish going through the window API.

module WebAPI.Window where

{- ------------------
   Alerts and dialogs
   ------------------ -}

{-| The browser's `window.alert()` function. -}
alert : String -> Task x ()

{-| The browser's `window.confirm()` function.

The task will succeed if the user confirms, and fail if the user cancels.
-}
confirm : String -> Task () ()

{-| The browser's `window.prompt()` function.

The first parameter is a message, and the second parameter is a default
response.

The task will succeed with the user's response, or fail if the user cancels
or enters blank text.
-}
prompt : String -> String -> Task () String

{- ----
   URIs
   ---- -}

{-| The browser's `encodeURIComponent()`. -}
encodeURIComponent : String -> String

{-| The browser's `decodeURIComponent()`. -}
decodeURIComponent : String -> String

{- -------------
   Online status
   ------------- -}

{-| Whether the browser is online, according to `navigator.onLine` -}
isOnline : Task x Bool

{-| A `Signal` indicating whether the browser is online, according to `navigator.onLine` -}
online : Signal Bool

{- ---------
   Unloading
   --------- -}

{-| A task which, when executed, listens for the `BeforeUnload` event.

To set up a confirmation dialog, have your responder return

    BeforeUnload.prompt "Your message" event

as one of your responses. Or, for more convenience, use `confirmUnload`.
-}
beforeUnload : Responder BeforeUnloadEvent -> Task x (Listener BeforeUnloadEvent)
beforeUnload responder =

{-| A task which, when executed, listens for the page to be unloaded, and
requires confirmation to do so.

In order to stop requiring confirmation, use `WebAPI.Event.removeListener` on
the resulting listener.

If you need to change the confirmation message, then you will need to use
`WebAPI.Event.removeListener` to remove any existing listener, and then use
this again to set up a new one.

If you need to do anything more complex when `BeforeUnload` fires, then see
`beforeUnload`.
-}
confirmUnload : String -> Task x (Listener BeforeUnloadEvent)

{-| Like `confirmUnload`, but only responds once and then removes the listener. -}
confirmUnloadOnce : String -> Task x BeforeUnloadEvent

{-| A task which, when executed, listens for the 'unload' event.

Note that it is unclear how much you can actually accomplish within
the Elm architecture before the page actually unloads. Thus, you should
experiment with this if you use it, and see how well it works.
-}
onUnload : Responder Event -> Task x (Listener Event)

{-| A task which, when executed, waits for the 'unload' event, and
then succeeds. To do something at that time, just chain additional
tasks.

Note that it is unclear how much you can actually accomplish within
the Elm architecture before the page actually unloads. Thus, you should
experiment with this if you use it, and see how well it works.
-}
unloadOnce : Task x Event

{- ------------
   Other Events
   ------------ -}

{-| A target for responding to events sent to the `window` object. -}
target : WebAPI.Event.Target

{- ----
   JSON
   ---- -}

{-| Access the Javascript `window` object via `Json.Decode`. -}
value : Task x Json.Decode.Value

See also

cancelAnimationFrame

        Use WebAPI.AnimationFrame

decodeURI

        Not implemented, since you will generally want decodeURIComponent instead.

encodeURI

        Not implemented, since you will generally want encodeURIComponent instead.

eval

        See WebAPI.Function.javascript for a limited form.

history

        See elm-community/elm-history

innerHeight, innerWidth

        See Window.dimensions in elm-lang/core.

isFinite

        Use not Basics.isInfinite from elm-lang/core (i.e. with the sense reversed).

isNan

        Use Basics.isNan in elm-lang/core.

localStorage

        Use WebAPI.Storage.localStorage

location

        For a Signal-oriented approach to things you might do with window.location, see elm-community/elm-history. For some additional Task-oriented approaches, see WebAPI.Location.

parseFloat

        Use String.toFloat in elm-lang/core.

parseInt

        Use String.toInt in elm-lang/core.

requestAnimationFrame

        Use WebAPI.AnimationFrame

setInterval

        Consider Time.fps (or its variants) from elm-lang/core, or AnimationFrame.frame (or its variants) from jwmerrill/elm-animation-frame, or its variants, or Effects.tick from evancz/elm-effects.

setTimeout

        Use Task.sleep from elm-lang/core, and then apply an andThen to do something after sleeping.

scroll, scrollBy, scrollTo, scrollX, scrollY

        There are a few puzzles about how to best adapt these for Elm, so I'm not sure a simplistic approach would be best -- a module that thought through scrolling in an Elm context would probably be better.

screen

        See WebAPI.Screen.screen.

screenX, screenY

        See WebAPI.Screen.screenXY.

sessionStorage

        See WebAPI.Storage.sessionStorage