From 1727301f08fd0e02a5ed2604a58088d70f8ed3ed Mon Sep 17 00:00:00 2001 From: rob Date: Mon, 17 Aug 2020 22:33:04 -0400 Subject: [PATCH 1/5] feat: HtmlVideoElement, HtmlMediaElement, TextTrack, VideoTrack --- CHANGELOG.md | 15 ++ src/Webapi.re | 18 ++ .../Dom/Webapi__Dom__HtmlMediaElement.re | 139 ++++++++++++ .../Dom/Webapi__Dom__HtmlVideoElement.re | 32 +++ src/Webapi/Dom/Webapi__Dom__MediaError.re | 23 ++ .../Webapi__TextTrack__TextTrackCue.re | 17 ++ .../Webapi__TextTrack__TextTrackCueList.re | 11 + src/Webapi/Webapi__AudioTrack.re | 14 ++ src/Webapi/Webapi__AudioTrackList.re | 14 ++ src/Webapi/Webapi__Dom.re | 5 +- src/Webapi/Webapi__TextTrack.re | 33 +++ src/Webapi/Webapi__TextTrackList.re | 10 + src/Webapi/Webapi__TimeRanges.re | 21 ++ src/Webapi/Webapi__VideoTrack.re | 17 ++ src/Webapi/Webapi__VideoTrackList.re | 15 ++ .../Webapi__Dom__HtmlVideoElement__test.re | 213 ++++++++++++++++++ 16 files changed, 595 insertions(+), 2 deletions(-) create mode 100644 src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re create mode 100644 src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re create mode 100644 src/Webapi/Dom/Webapi__Dom__MediaError.re create mode 100644 src/Webapi/TextTrack/Webapi__TextTrack__TextTrackCue.re create mode 100644 src/Webapi/TextTrack/Webapi__TextTrack__TextTrackCueList.re create mode 100644 src/Webapi/Webapi__AudioTrack.re create mode 100644 src/Webapi/Webapi__AudioTrackList.re create mode 100644 src/Webapi/Webapi__TextTrack.re create mode 100644 src/Webapi/Webapi__TextTrackList.re create mode 100644 src/Webapi/Webapi__TimeRanges.re create mode 100644 src/Webapi/Webapi__VideoTrack.re create mode 100644 src/Webapi/Webapi__VideoTrackList.re create mode 100644 tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re diff --git a/CHANGELOG.md b/CHANGELOG.md index 42842ca1..3e6315c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +### 0.20.0 + +* Added binding for `HTMLVideoElement` +* Added binding for `HTMLMediaElement` +* Added binding for `MediaError` +* Added binding for `AudioTrack` +* Added binding for `AudioTrackList` +* Added binding for `TextTrack` +* Added binding for `TextTrackList` +* Added binding for `TextTrackCue` +* Added binding for `TextTrackCueList` +* Added binding for `VideoTrack` +* Added binding for `VideoTrackList` +* Added binding for `TimeRanges` + ### 0.19.1 * Removed dev dependency on `bsdoc` to allow smooth installs on non-Mac diff --git a/src/Webapi.re b/src/Webapi.re index 00ccfa56..6a604c91 100644 --- a/src/Webapi.re +++ b/src/Webapi.re @@ -23,6 +23,24 @@ module Iterator = FormData.Iterator; module Performance = Webapi__Performance; +/** @since 0.20.0 */ +module AudioTrack = Webapi__AudioTrack; +/** @since 0.20.0 */ +module AudioTrackList = Webapi__AudioTrackList; + +/** @since 0.20.0 */ +module TextTrack = Webapi__TextTrack; +/** @since 0.20.0 */ +module TextTrackList = Webapi__TextTrackList; + +/** @since 0.20.0 */ +module VideoTrack = Webapi__VideoTrack; +/** @since 0.20.0 */ +module VideoTrackList = Webapi__VideoTrackList; + +/** @since 0.20.0 */ +module TimeRanges = Webapi__TimeRanges; + /** @since 0.19.0 */ module ReadableStream = Webapi__ReadableStream; diff --git a/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re b/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re new file mode 100644 index 00000000..f5ef9e9a --- /dev/null +++ b/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re @@ -0,0 +1,139 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#htmlmediaelement + */ +module Impl = (T: {type t;}) => { + type t_htmlMediaElement = T.t; + + type mediaProvider; // TODO: one of MediaStream, MediaSource, or Blob + + /** Properties */ + [@bs.get] external buffered: t_htmlMediaElement => Webapi__TimeRanges.t = "buffered"; + + // This is part of the recommendation spec + // type mediaController; // TODO + // [@bs.get] [@bs.return nullable] external controller: t_htmlMediaElement => option(mediaController) = "controller"; + // [@bs.set] external setController: (t_htmlMediaElement, mediaController) => unit = "controller"; + + [@bs.get] external controls: t_htmlMediaElement => bool = "controls"; + [@bs.set] external setControls: (t_htmlMediaElement, bool) => unit = "controls"; + + /** One or more of "nodownload", "nofullscreen", and "noremoteplayback" */ + [@bs.get] external controlsList: t_htmlMediaElement => Dom.domTokenList = "controlsList"; + + [@bs.get] [@bs.return nullable] external crossOrigin: t_htmlMediaElement => option(string) = "crossOrigin"; + [@bs.set] + external setCrossOrigin: + ( + t_htmlMediaElement, + [@bs.string] [ | `anonymous | [@bs.as "use-credentials"] `useCredentials | [@bs.as ""] `empty] + ) => + unit = + "crossOrigin"; + + [@bs.get] external currentSrc: t_htmlMediaElement => string = "currentSrc"; + + [@bs.get] external currentTime: t_htmlMediaElement => float = "currentTime"; + [@bs.set] external setCurrentTime: (t_htmlMediaElement, float) => unit = "currentTime"; + + [@bs.get] external defaultMuted: t_htmlMediaElement => bool = "defaultMuted"; + [@bs.set] external setDefaultMuted: (t_htmlMediaElement, bool) => unit = "defaultMuted"; + + [@bs.get] external defaultPlaybackRate: t_htmlMediaElement => float = "defaultPlaybackRate"; + [@bs.set] external setDefaultPlaybackRate: (t_htmlMediaElement, float) => unit = "defaultPlaybackRate"; + + [@bs.get] external disableRemotePlayback: t_htmlMediaElement => bool = "disableRemotePlayback"; + [@bs.set] external setDisableRemotePlayback: (t_htmlMediaElement, bool) => unit = "disableRemotePlayback"; + + [@bs.get] external duration: t_htmlMediaElement => float = "duration"; + [@bs.get] external ended: t_htmlMediaElement => bool = "ended"; + [@bs.get] [@bs.return nullable] external error: t_htmlMediaElement => option(Webapi__Dom__MediaError.t) = "error"; + + [@bs.get] external loop: t_htmlMediaElement => bool = "loop"; + [@bs.set] external setLoop: (t_htmlMediaElement, bool) => unit = "loop"; + + // [@bs.get] external mediaGroup: t_htmlMediaElement => string = "mediaGroup"; // Not well supported + // [@bs.set] external setMediaGroup: (t_htmlMediaElement, string) => unit = "mediaGroup"; // Not well supported + + [@bs.get] external muted: t_htmlMediaElement => bool = "muted"; + [@bs.set] external setMuted: (t_htmlMediaElement, bool) => unit = "muted"; + + type mediaNetworkState = + | NetworkEmpty // 0 + | NetworkIdle // 1 + | NetworkLoading // 2 + | NetworkNoSource; // 3 + + let decodeNetworkState = fun + | 0 => Some(NetworkEmpty) + | 1 => Some(NetworkIdle) + | 2 => Some(NetworkLoading) + | 3 => Some(NetworkNoSource) + | _ => None; + + [@bs.get] external _networkState: t_htmlMediaElement => int = "networkState"; + let networkState = t_htmlMediaElement => _networkState(t_htmlMediaElement) |> decodeNetworkState; + + [@bs.get] external paused: t_htmlMediaElement => bool = "paused"; + + [@bs.get] external playbackRate: t_htmlMediaElement => float = "playbackRate"; + [@bs.set] external setPlaybackRate: (t_htmlMediaElement, float) => unit = "playbackRate"; + + [@bs.get] external played: t_htmlMediaElement => Webapi__TimeRanges.t = "played"; + [@bs.get] external preload: t_htmlMediaElement => string = "preload"; + + type readyStateEnum = + | HaveNothing + | HaveMetadata + | HaveCurrentData + | HaveFutureData + | HaveEnoughData; + + let decodeReadyState = fun + | 0 => Some(HaveNothing) + | 1 => Some(HaveMetadata) + | 2 => Some(HaveCurrentData) + | 3 => Some(HaveFutureData) + | 4 => Some(HaveEnoughData) + | _ => None; + + [@bs.get] external _readyState: t_htmlMediaElement => int = "readyState"; + let readyState = t => _readyState(t) |> decodeReadyState; + + [@bs.get] external seekable: t_htmlMediaElement => Webapi__TimeRanges.t = "seekable"; + [@bs.get] external seeking: t_htmlMediaElement => bool = "seeking"; + // [@bs.get] external sinkId: t_htmlMediaElement => string = "sinkId"; // experimental + + [@bs.get] external src: t_htmlMediaElement => string = "src"; + [@bs.set] external setSrc: (t_htmlMediaElement, string) => unit = "src"; + + [@bs.get] [@bs.return nullable] external srcObject: t_htmlMediaElement => option(mediaProvider) = "srcObject"; + + [@bs.get] external audioTracks: t_htmlMediaElement => Webapi__AudioTrackList.t = "textTracks"; + [@bs.get] external textTracks: t_htmlMediaElement => Webapi__TextTrackList.t = "textTracks"; + + // [@bs.get] external videoTracks: t_htmlMediaElement => Webapi__VideoTrackList.t = "videoTracks"; // Not widely available + + [@bs.get] external volume: t_htmlMediaElement => float = "volume"; + [@bs.set] external setVolume: (t_htmlMediaElement, float) => unit = "volume"; + + /** Methods */ + + [@bs.send] external addTextTrack: (t_htmlMediaElement, Webapi__TextTrack.textTrackKind, ~label: string, ~language: string, unit) => Webapi__TextTrack.t = "addTextTrack"; + + // return is one of "probably", "maybe", or "" + [@bs.send] external canPlayType: (t_htmlMediaElement, string) => string = "canPlayType"; + // [@bs.send] external fastSeek: (t_htmlMediaElement, float) => unit = "fastSeek"; // experimental + [@bs.send] external load: t_htmlMediaElement => unit = "load"; + [@bs.send] external pause: t_htmlMediaElement => unit = "pause"; + [@bs.send] external play: t_htmlMediaElement => Js.promise(unit, string /* [`NotAllowedError | `NotSupportedError] */) = "play"; + + // [@bs.send] external getStartDate: t_htmlMediaElement => Js.Date.t = "getStartDate"; // not supported? +}; + +type t; // TODO: Dom.htmlMediaElement + +include Webapi__Dom__EventTarget.Impl({type nonrec t = t;}); +include Webapi__Dom__Node.Impl({type nonrec t = t;}); +include Webapi__Dom__Element.Impl({type nonrec t = t;}); +include Webapi__Dom__HtmlElement.Impl({type nonrec t = t;}); +include Impl({type nonrec t = t;}); diff --git a/src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re b/src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re new file mode 100644 index 00000000..457bf72d --- /dev/null +++ b/src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re @@ -0,0 +1,32 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#htmlvideoelement + */ +module Impl = (T: {type t;}) => { + type t_htmlVideoElement = T.t; + + external unsafeAsVideoElement: Dom.element => t_htmlVideoElement = "%identity"; + + let asVideoElement = (el): option(t_htmlVideoElement) => switch(Webapi__Dom__Element.tagName(el)) { + | "VIDEO" => el->unsafeAsVideoElement->Some + | _ => None + }; + + /** Properties */ + [@bs.get] external height: t_htmlVideoElement => int = "height"; + [@bs.set] external setHeight: (t_htmlVideoElement, int) => unit = "height"; + [@bs.get] external poster: t_htmlVideoElement => string = "poster"; + [@bs.set] external setPoster: (t_htmlVideoElement, string) => unit = "poster"; + [@bs.get] external videoHeight: t_htmlVideoElement => int = "videoHeight"; + [@bs.get] external videoWidth: t_htmlVideoElement => int = "videoWidth"; + [@bs.get] external width: t_htmlVideoElement => int = "width"; + [@bs.set] external setWidth: (t_htmlVideoElement, int) => unit = "width"; +}; + +type t; // TODO: Dom.htmlVideoElement + +include Webapi__Dom__EventTarget.Impl({ type nonrec t = t; }); +include Webapi__Dom__Node.Impl({ type nonrec t = t; }); +include Webapi__Dom__Element.Impl({ type nonrec t = t; }); +include Webapi__Dom__HtmlElement.Impl({ type nonrec t = t; }); +include Webapi__Dom__HtmlMediaElement.Impl({ type nonrec t = t; }); +include Impl({ type nonrec t = t; }); diff --git a/src/Webapi/Dom/Webapi__Dom__MediaError.re b/src/Webapi/Dom/Webapi__Dom__MediaError.re new file mode 100644 index 00000000..321603dc --- /dev/null +++ b/src/Webapi/Dom/Webapi__Dom__MediaError.re @@ -0,0 +1,23 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#mediaerror + */ +type t; + +type errorType = + | ERR_ABORTED + | ERR_NETWORK + | ERR_DECODE + | ERR_SRC_NOT_SUPPORTED; + +let decodeErrorType = fun + | 1 => Some(ERR_ABORTED) + | 2 => Some(ERR_NETWORK) + | 3 => Some(ERR_DECODE) + | 4 => Some(ERR_SRC_NOT_SUPPORTED) + | _ => None; + +/** Properties */ + +[@bs.get] external _code: t => int = "code"; +let code = t => _code(t) |> decodeErrorType; +[@bs.get] external message: t => string = "message"; diff --git a/src/Webapi/TextTrack/Webapi__TextTrack__TextTrackCue.re b/src/Webapi/TextTrack/Webapi__TextTrack__TextTrackCue.re new file mode 100644 index 00000000..26a8b0b3 --- /dev/null +++ b/src/Webapi/TextTrack/Webapi__TextTrack__TextTrackCue.re @@ -0,0 +1,17 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#texttrackcue + */ +type t; + +include Webapi__Dom__EventTarget.Impl({ type nonrec t = t; }); + +// FIXME: circular dep +// [@bs.get] [@bs.return nullable] external track: t => option(Webapi__TextTrack.t) = "track"; +[@bs.get] external id: t => string = "id"; +[@bs.set] external setId: (t, string) => unit = "id"; +[@bs.get] external startTime: t => float = "startTime"; +[@bs.set] external setStartTime: (t, float) => unit = "startTime"; +[@bs.get] external endTime: t => float = "endTime"; +[@bs.set] external setEndTime: (t, float) => unit = "endTime"; +[@bs.get] external pauseOnExit: t => bool = "pauseOnExit"; +[@bs.set] external setPauseOnExit: (t, bool) => unit = "pauseOnExit"; diff --git a/src/Webapi/TextTrack/Webapi__TextTrack__TextTrackCueList.re b/src/Webapi/TextTrack/Webapi__TextTrack__TextTrackCueList.re new file mode 100644 index 00000000..d0b15ebe --- /dev/null +++ b/src/Webapi/TextTrack/Webapi__TextTrack__TextTrackCueList.re @@ -0,0 +1,11 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#texttrackcuelist + */ +type t; + +/** Properties */ +[@bs.get] external length: t => int = "length"; +[@bs.get_index] [@bs.return nullable] external get: (t, int) => option(Webapi__TextTrack__TextTrackCue.t) = ""; + +/** Methods */ +[@bs.send] [@bs.return nullable] external getCueById: (t, string) => option(Webapi__TextTrack__TextTrackCue.t) = "getCueById"; diff --git a/src/Webapi/Webapi__AudioTrack.re b/src/Webapi/Webapi__AudioTrack.re new file mode 100644 index 00000000..1c4a5e10 --- /dev/null +++ b/src/Webapi/Webapi__AudioTrack.re @@ -0,0 +1,14 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#audiotrack + */ +type t; + +/** Properties */ + +[@bs.get] external id: t => string = "id"; +[@bs.get] external kind: t => string = "kind"; +[@bs.get] external label: t => string = "label"; +[@bs.get] external language: t => string = "language"; + +[@bs.get] external enabled: t => bool = "enabled"; +[@bs.set] external setEnabled: (t, bool) => unit = "enabled"; diff --git a/src/Webapi/Webapi__AudioTrackList.re b/src/Webapi/Webapi__AudioTrackList.re new file mode 100644 index 00000000..15a6c871 --- /dev/null +++ b/src/Webapi/Webapi__AudioTrackList.re @@ -0,0 +1,14 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#audiotracklist + */ +type t; +include Webapi__Dom__EventTarget.Impl({ type nonrec t = t; }); + +/** Properties */ + +[@bs.get] external length: t => float = "length"; +[@bs.get_index] external get: (t, int) => option(Webapi__AudioTrack.t) = ""; + +/** Methods */ + +[@bs.send] [@bs.return nullable] external getTrackById: (t, string) => option(Webapi__AudioTrack.t) = "getTrackById"; diff --git a/src/Webapi/Webapi__Dom.re b/src/Webapi/Webapi__Dom.re index 8d658660..4e4813f7 100644 --- a/src/Webapi/Webapi__Dom.re +++ b/src/Webapi/Webapi__Dom.re @@ -29,11 +29,14 @@ module HtmlElement = Webapi__Dom__HtmlElement; module HtmlFormElement = Webapi__Dom__HtmlFormElement; module HtmlImageElement = Webapi__Dom__HtmlImageElement; module HtmlInputElement = Webapi__Dom__HtmlInputElement; +module HtmlMediaElement = Webapi__Dom__HtmlMediaElement; +module HtmlVideoElement = Webapi__Dom__HtmlVideoElement; module IdbVersionChangeEvent = Webapi__Dom__IdbVersionChangeEvent; module Image = Webapi__Dom__Image; module InputEvent = Webapi__Dom__InputEvent; module KeyboardEvent = Webapi__Dom__KeyboardEvent; module Location = Webapi__Dom__Location; +module MediaError = Webapi__Dom__MediaError; module MouseEvent = Webapi__Dom__MouseEvent; module MutationObserver = Webapi__Dom__MutationObserver; module MutationRecord = Webapi__Dom__MutationRecord; @@ -130,7 +133,6 @@ include Webapi__Dom__Types; HTMLLIElement HTMLLinkElement HTMLMapElement - HTMLMediaElement HTMLMenuElement HTMLMetaElement HTMLMeterElement @@ -164,7 +166,6 @@ include Webapi__Dom__Types; HTMLTrackElement HTMLUListElement HTMLUnknownElement - HTMLVideoElement /* Other interfaces */ CanvasRenderingContext2D diff --git a/src/Webapi/Webapi__TextTrack.re b/src/Webapi/Webapi__TextTrack.re new file mode 100644 index 00000000..c6d56ec9 --- /dev/null +++ b/src/Webapi/Webapi__TextTrack.re @@ -0,0 +1,33 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#texttrack + */ +type t; +include Webapi__Dom__EventTarget.Impl({ type nonrec t = t; }); + +/** @since 0.20.0 */ +module TextTrackCueList = Webapi__TextTrack__TextTrackCueList; +/** @since 0.20.0 */ +module TextTrackCue = Webapi__TextTrack__TextTrackCue; + +// TODO: string enum for +// enum TextTrackMode { "disabled", "hidden", "showing" }; +// enum TextTrackKind { "subtitles", "captions", "descriptions", "chapters", "metadata" }; +type textTrackMode = string; +type textTrackKind = string; + +/** Properties */ + +[@bs.get] external activeCues: t => option(TextTrackCueList.t) = "activeCues"; +[@bs.get] external cues: t => option(TextTrackCueList.t) = "cues"; +[@bs.get] external id: t => string = "id"; +[@bs.get] external inBandMetadataTrackDispatchType: t => option(string) = "inBandMetadataTrackDispatchType"; +[@bs.get] external kind: t => textTrackKind = "kind"; +[@bs.get] external label: t => string = "label"; +[@bs.get] external language: t => string = "language"; +[@bs.get] external mode: t => textTrackMode = "mode"; +[@bs.set] external setMode: (t, textTrackMode) => unit = "mode"; + +/** Methods */ + +[@bs.send] external addCue: (t, TextTrackCue.t) => unit = "addCue"; +[@bs.send] external removeCue: (t, TextTrackCue.t) => unit = "removeCue"; diff --git a/src/Webapi/Webapi__TextTrackList.re b/src/Webapi/Webapi__TextTrackList.re new file mode 100644 index 00000000..097151d1 --- /dev/null +++ b/src/Webapi/Webapi__TextTrackList.re @@ -0,0 +1,10 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#texttracklist + */ + +type t; +include Webapi__Dom__EventTarget.Impl({ type nonrec t = t; }); + +[@bs.get] external length: t => float = "length"; +[@bs.get_index] external get: (t, int) => option(Webapi__TextTrack.t) = ""; +[@bs.send] [@bs.return nullable] external getTrackById: (t, string) => option(Webapi__TextTrack.t) = "getTrackById"; diff --git a/src/Webapi/Webapi__TimeRanges.re b/src/Webapi/Webapi__TimeRanges.re new file mode 100644 index 00000000..dcfb24b0 --- /dev/null +++ b/src/Webapi/Webapi__TimeRanges.re @@ -0,0 +1,21 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#timeranges + */ + +type t; + +/** Properties */ + +[@bs.get] external length: t => int = "length"; + +/** Methods */ + +/** + * Throws an "IndexSizeError" DOMException if the index is out of range. + */ +[@bs.send] external startExn: (t, int) => int = "start"; + +/** + * Throws an "IndexSizeError" DOMException if the index is out of range. + */ +[@bs.send] external endExn: (t, int) => int = "end"; diff --git a/src/Webapi/Webapi__VideoTrack.re b/src/Webapi/Webapi__VideoTrack.re new file mode 100644 index 00000000..606202f2 --- /dev/null +++ b/src/Webapi/Webapi__VideoTrack.re @@ -0,0 +1,17 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#videotrack + */ +type t; + +/** Properties */ + +[@bs.get] external selected: t => bool = "selected"; +[@bs.set] external setSelected: (t, bool) => unit = "selected"; +[@bs.get] external id: t => string = "id"; +[@bs.get] external kind: t => string = "kind"; +[@bs.get] external label: t => string = "label"; +[@bs.get] external language: t => string = "language"; + +// Not widely available, recommendation status +// type t_sourceBuffer; +// [@bs.get] external sourceBuffer: t => t_sourceBuffer = "sourceBuffer"; diff --git a/src/Webapi/Webapi__VideoTrackList.re b/src/Webapi/Webapi__VideoTrackList.re new file mode 100644 index 00000000..7d2b3455 --- /dev/null +++ b/src/Webapi/Webapi__VideoTrackList.re @@ -0,0 +1,15 @@ +/** + * Spec: https://html.spec.whatwg.org/multipage/media.html#videotracklist + */ +type t; +include Webapi__Dom__EventTarget.Impl({ type nonrec t = t; }); + +/** Properties */ + +[@bs.get] external length: t => float = "length"; +[@bs.get] external selectedIndex: t => float = "selectedIndex"; +[@bs.get_index] external get: (t, int) => option(Webapi__VideoTrack.t) = ""; + +/** Methods */ + +[@bs.send] [@bs.return nullable] external getTrackById: (t, string) => option(Webapi__VideoTrack.t) = "getTrackById"; diff --git a/tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re b/tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re new file mode 100644 index 00000000..73f451e3 --- /dev/null +++ b/tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re @@ -0,0 +1,213 @@ +open Webapi__Dom; +open Webapi__Dom__HtmlVideoElement; + +let el = Document.createElement("video", document) + |> asVideoElement + |> Belt.Option.getUnsafe; + +let body = Document.asHtmlDocument(document) +-> Belt.Option.flatMap(HtmlDocument.body) +-> Belt.Option.getUnsafe; + +Element.appendChild(el |> Obj.magic |> Element.asNode, body); + +let test = buffered(el); +Js.log2("Webapi.Dom.HtmlVideoElement.buffered:", test); + +let test = controls(el); +Js.log2("Webapi.Dom.HtmlVideoElement.controls:", test); + +let test = setControls(el, true); +Js.log2("Webapi.Dom.HtmlVideoElement.setControls:", test); + +let test = controlsList(el); +Js.log2("Webapi.Dom.HtmlVideoElement.controlsList:", test); + +let test = crossOrigin(el); +Js.log2("Webapi.Dom.HtmlVideoElement.crossOrigin:", test); + +let test = setCrossOrigin(el, `empty); +Js.log2("Webapi.Dom.HtmlVideoElement.setCrossOrigin:", test); + +let test = currentSrc(el); +Js.log2("Webapi.Dom.HtmlVideoElement.currentSrc:", test); + +let test = currentTime(el); +Js.log2("Webapi.Dom.HtmlVideoElement.currentTime:", test); + +let test = setCurrentTime(el, 0.1); +Js.log2("Webapi.Dom.HtmlVideoElement.setCurrentTime:", test); + +let test = defaultMuted(el); +Js.log2("Webapi.Dom.HtmlVideoElement.defaultMuted:", test); + +let test = setDefaultMuted(el, true); +Js.log2("Webapi.Dom.HtmlVideoElement.setDefaultMuted:", test); + +let test = defaultPlaybackRate(el); +Js.log2("Webapi.Dom.HtmlVideoElement.defaultPlaybackRate:", test); + +let test = setDefaultPlaybackRate(el, 1.25); +Js.log2("Webapi.Dom.HtmlVideoElement.setDefaultPlaybackRate:", test); + +let test = disableRemotePlayback(el); +Js.log2("Webapi.Dom.HtmlVideoElement.disableRemotePlayback:", test); + +let test = setDisableRemotePlayback(el, true); +Js.log2("Webapi.Dom.HtmlVideoElement.setDisableRemotePlayback:", test); + +let test = duration(el); +Js.log2("Webapi.Dom.HtmlVideoElement.duration:", test); + +let test = ended(el); +Js.log2("Webapi.Dom.HtmlVideoElement.ended:", test); + +let test = error(el); +Js.log2("Webapi.Dom.HtmlVideoElement.error:", test); + +let test = loop(el); +Js.log2("Webapi.Dom.HtmlVideoElement.loop:", test); + +let test = setLoop(el, true); +Js.log2("Webapi.Dom.HtmlVideoElement.setLoop:", test); + +let test = muted(el); +Js.log2("Webapi.Dom.HtmlVideoElement.muted:", test); + +let test = setMuted(el, false); +Js.log2("Webapi.Dom.HtmlVideoElement.setMuted:", test); + +let test = networkState(el); +Js.log2("Webapi.Dom.HtmlVideoElement.networkState:", test); + +let test = paused(el); +Js.log2("Webapi.Dom.HtmlVideoElement.paused:", test); + +let test = playbackRate(el); +Js.log2("Webapi.Dom.HtmlVideoElement.playbackRate:", test); + +let test = setPlaybackRate(el, 0.1); +Js.log2("Webapi.Dom.HtmlVideoElement.setPlaybackRate:", test); + +let test = played(el); +Js.log2("Webapi.Dom.HtmlVideoElement.played:", test); + +let test = preload(el); +Js.log2("Webapi.Dom.HtmlVideoElement.preload:", test); + +let test = readyState(el); +Js.log2("Webapi.Dom.HtmlVideoElement.readyState:", test); + +let test = seekable(el); +Js.log2("Webapi.Dom.HtmlVideoElement.seekable:", test); + +let test = seeking(el); +Js.log2("Webapi.Dom.HtmlVideoElement.seeking:", test); + +let test = src(el); +Js.log2("Webapi.Dom.HtmlVideoElement.src:", test); + +let test = setSrc(el, "#"); +Js.log2("Webapi.Dom.HtmlVideoElement.setSrc:", test); + +let test = srcObject(el); +Js.log2("Webapi.Dom.HtmlVideoElement.srcObject:", test); + +let test = audioTracks(el); +Js.log2("Webapi.Dom.HtmlVideoElement.audioTracks:", test); + +let len = Webapi.AudioTrackList.length(test); +Js.log2("Webapi.AudioTrackList.length:", len); +let track = Webapi.AudioTrackList.get(test, 0); +Js.log2("Webapi.AudioTrackList.get:", track); +let track = Webapi.AudioTrackList.getTrackById(test, "1"); +Js.log2("Webapi.AudioTrackList.getTrackById:", track); + +let test = textTracks(el); +Js.log2("Webapi.Dom.HtmlVideoElement.textTracks:", test); + +let len = Webapi.TextTrackList.length(test); +Js.log2("Webapi.TextTrackList.length:", len); +let track = addTextTrack(el, "subtitles", ~label="My Text Track", ~language="en-US", ()); +Js.log2("Webapi.Dom.HtmlVideoElement.addTextTrack:", track); +let track = Webapi.TextTrackList.get(test, 0) -> Belt.Option.getUnsafe; +Js.log2("Webapi.TextTrackList.get:", track); + +let test2 = Webapi.TextTrack.id(track); +Js.log2("Webapi.TextTrack.id:", test2); +let test2 = Webapi.TextTrack.inBandMetadataTrackDispatchType(track); +Js.log2("Webapi.TextTrack.inBandMetadataTrackDispatchType:", test2); +let test2 = Webapi.TextTrack.kind(track); +Js.log2("Webapi.TextTrack.kind:", test2); +let test2 = Webapi.TextTrack.label(track); +Js.log2("Webapi.TextTrack.label:", test2); +let test2 = Webapi.TextTrack.language(track); +Js.log2("Webapi.TextTrack.language:", test2); +let test2 = Webapi.TextTrack.mode(track); +Js.log2("Webapi.TextTrack.mode:", test2); +let test2 = Webapi.TextTrack.setMode(track, "hidden"); +Js.log2("Webapi.TextTrack.setMode:", test2); + +let cues = Webapi.TextTrack.cues(track) -> Belt.Option.getUnsafe; +Js.log2("Webapi.TextTrack.cues:", cues); + +let activeCues = Webapi.TextTrack.activeCues(track) -> Belt.Option.getUnsafe; +Js.log2("Webapi.TextTrack.activeCues:", cues); + +let len = Webapi.TextTrack.TextTrackCueList.length(cues); +Js.log2("Webapi.TextTrack.TextTrackCueList.length:", len); +let len = Webapi.TextTrack.TextTrackCueList.length(activeCues); +Js.log2("Webapi.TextTrack.TextTrackCueList.length:", len); + +let track = Webapi.TextTrackList.getTrackById(test, ""); +Js.log2("Webapi.TextTrackList.getTrackById:", track); + +let track = Webapi.TextTrackList.length(test); +Js.log2("Webapi.TextTrackList.length:", track); + +// not well supported + +// let test = videoTracks(el); +// Js.log2("Webapi.Dom.HtmlVideoElement.videoTracks:", test); + +// let len = Webapi.VideoTrackList.length(test); +// Js.log2("Webapi.VideoTrackList.length:", len); +// let track = Webapi.VideoTrackList.get(test, 0); +// Js.log2("Webapi.VideoTrackList.get:", track); +// let track = Webapi.VideoTrackList.getTrackById(test, "1"); +// Js.log2("Webapi.VideoTrackList.getTrackById:", track); + +let test = volume(el); +Js.log2("Webapi.Dom.HtmlVideoElement.volume:", test); + +let test = setVolume(el, 1.0); +Js.log2("Webapi.Dom.HtmlVideoElement.setVolume:", test); + +let test = canPlayType(el, "video/webm"); +Js.log2("Webapi.Dom.HtmlVideoElement.canPlayType:", test); + +let test = load(el); +Js.log2("Webapi.Dom.HtmlVideoElement.load:", test); + +let test = pause(el); +Js.log2("Webapi.Dom.HtmlVideoElement.pause:", test); + +let test = play(el); +Js.log2("Webapi.Dom.HtmlVideoElement.play:", test); + +let test = setHeight(el, 100); +Js.log2("Webapi.Dom.HtmlVideoElement.setHeight:", test); +let test = height(el); +Js.log2("Webapi.Dom.HtmlVideoElement.height:", test); +let test = poster(el); +Js.log2("Webapi.Dom.HtmlVideoElement.poster:", test); +let test = setPoster(el, "foo"); +Js.log2("Webapi.Dom.HtmlVideoElement.setPoster:", test); +let test = videoHeight(el); +Js.log2("Webapi.Dom.HtmlVideoElement.videoHeight:", test); +let test = videoWidth(el); +Js.log2("Webapi.Dom.HtmlVideoElement.videoWidth:", test); +let test = setWidth(el, 100); +Js.log2("Webapi.Dom.HtmlVideoElement.setWidth:", test); +let test = width(el); +Js.log2("Webapi.Dom.HtmlVideoElement.width:", test); From d79658b0307de15348b8cc50100b7323d441332b Mon Sep 17 00:00:00 2001 From: rob Date: Thu, 20 Aug 2020 00:04:15 -0400 Subject: [PATCH 2/5] chore: update package version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 610bb5f0..3ae2f5a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bs-webapi", - "version": "0.19.1", + "version": "0.20.0", "description": "Reason + BuckleScript bindings to DOM", "repository": { "type": "git", From 41918ae6abfbfda6377f1d6b8a10ee8b30c65eb8 Mon Sep 17 00:00:00 2001 From: rob Date: Tue, 25 Aug 2020 23:24:49 -0400 Subject: [PATCH 3/5] refactor: rename asVideoElement to ofElement --- src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re | 6 +++--- tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re b/src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re index 457bf72d..c6fd2e83 100644 --- a/src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re +++ b/src/Webapi/Dom/Webapi__Dom__HtmlVideoElement.re @@ -4,10 +4,10 @@ module Impl = (T: {type t;}) => { type t_htmlVideoElement = T.t; - external unsafeAsVideoElement: Dom.element => t_htmlVideoElement = "%identity"; + external unsafeOfElement: Dom.element => t_htmlVideoElement = "%identity"; - let asVideoElement = (el): option(t_htmlVideoElement) => switch(Webapi__Dom__Element.tagName(el)) { - | "VIDEO" => el->unsafeAsVideoElement->Some + let ofElement = (el): option(t_htmlVideoElement) => switch(Webapi__Dom__Element.tagName(el)) { + | "VIDEO" => el->unsafeOfElement->Some | _ => None }; diff --git a/tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re b/tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re index 73f451e3..7f344e6f 100644 --- a/tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re +++ b/tests/Webapi/Dom/Webapi__Dom__HtmlVideoElement__test.re @@ -2,7 +2,7 @@ open Webapi__Dom; open Webapi__Dom__HtmlVideoElement; let el = Document.createElement("video", document) - |> asVideoElement + |> ofElement |> Belt.Option.getUnsafe; let body = Document.asHtmlDocument(document) From 4f525cc30b042b889e7b2e66ecf5a0db52060f39 Mon Sep 17 00:00:00 2001 From: rob Date: Fri, 18 Sep 2020 14:37:26 -0400 Subject: [PATCH 4/5] fix: use Js.Promise.t over Js.promise --- src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re b/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re index f5ef9e9a..dc651660 100644 --- a/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re +++ b/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re @@ -125,7 +125,7 @@ module Impl = (T: {type t;}) => { // [@bs.send] external fastSeek: (t_htmlMediaElement, float) => unit = "fastSeek"; // experimental [@bs.send] external load: t_htmlMediaElement => unit = "load"; [@bs.send] external pause: t_htmlMediaElement => unit = "pause"; - [@bs.send] external play: t_htmlMediaElement => Js.promise(unit, string /* [`NotAllowedError | `NotSupportedError] */) = "play"; + [@bs.send] external play: t_htmlMediaElement => Js.Promise.t(unit, string /* [`NotAllowedError | `NotSupportedError] */) = "play"; // [@bs.send] external getStartDate: t_htmlMediaElement => Js.Date.t = "getStartDate"; // not supported? }; From ebf0b3d0836691788c3ab44b4e3113d4b9b34576 Mon Sep 17 00:00:00 2001 From: rob Date: Fri, 18 Sep 2020 15:50:20 -0400 Subject: [PATCH 5/5] fix: type sig --- src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re b/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re index dc651660..a90478a2 100644 --- a/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re +++ b/src/Webapi/Dom/Webapi__Dom__HtmlMediaElement.re @@ -125,7 +125,7 @@ module Impl = (T: {type t;}) => { // [@bs.send] external fastSeek: (t_htmlMediaElement, float) => unit = "fastSeek"; // experimental [@bs.send] external load: t_htmlMediaElement => unit = "load"; [@bs.send] external pause: t_htmlMediaElement => unit = "pause"; - [@bs.send] external play: t_htmlMediaElement => Js.Promise.t(unit, string /* [`NotAllowedError | `NotSupportedError] */) = "play"; + [@bs.send] external play: t_htmlMediaElement => Js.Promise.t(unit) = "play"; // [@bs.send] external getStartDate: t_htmlMediaElement => Js.Date.t = "getStartDate"; // not supported? };