]\nmodule ToastrHelper\n\nopen Sutil.Toastr\nopen Shared.Validation\nopen Sutil\n\nlet getMessage appError =\n match appError with\n | ValidationError err -> [ err ]\n | AuthenticationError _ -> [ \"Invalid session\" ]\n | InvalidSession -> [ \"Invalid session\" ]\n | AuthorizationError -> [ \"You do not have permissions for this operation\" ]\n | ServerError err -> [ \"Server error\" ]\n\nlet getMessages (err: AggregateError) =\n let errs =\n match err with\n | AggregateError list -> list\n\n errs\n |> List.map getMessage\n |> List.concat\n |> List.rev\n\nlet applyCommonStyle msg =\n msg\n |> Toastr.position TopRight\n |> Toastr.timeout 3000\n |> Toastr.withProgressBar\n |> Toastr.hideEasing Easing.Swing\n |> Toastr.showCloseButton\n\nlet errorToastMsg msg =\n Toastr.message msg\n |> Toastr.title \"Error\"\n |> applyCommonStyle\n |> Toastr.error\n\nmodule Cmd =\n let errorToast (err: AggregateError) =\n err\n |> getMessages\n |> List.map errorToastMsg\n |> Cmd.batch\n\n let successToast message =\n Toastr.message message\n |> Toastr.title \"Success\"\n |> applyCommonStyle\n |> Toastr.success\n","namespace Feliz\r\n\r\nopen System\r\n\r\ntype HtmlEngine<'Node>\r\n /// Customizable HTML generator API.\r\n ///\r\n /// Make a node with tag name and children.\r\n /// Make a node from a string.\r\n /// Make an empty node.\r\n (mk: string -> 'Node seq -> 'Node, ofStr: string -> 'Node, empty: unit -> 'Node) =\r\n\r\n /// Create a custom element\r\n ///\r\n /// You generally shouldn't need to use this, if you notice an element missing please submit an issue.\r\n member _.custom (key: string, children: seq<'Node>) = mk key children\r\n /// The empty element, renders nothing on screen\r\n member _.none : 'Node = empty()\r\n\r\n member _.a (children: seq<'Node>) = mk \"a\" children\r\n\r\n member _.abbr (value: float) = mk \"abbr\" [Util.asString value |> ofStr]\r\n member _.abbr (value: int) = mk \"abbr\" [Util.asString value |> ofStr]\r\n member _.abbr (value: 'Node) = mk \"abbr\" [value]\r\n member _.abbr (value: string) = mk \"abbr\" [ofStr value]\r\n member _.abbr (children: seq<'Node>) = mk \"abbr\" children\r\n\r\n member _.address (value: float) = mk \"address\" [Util.asString value |> ofStr]\r\n member _.address (value: int) = mk \"address\" [Util.asString value |> ofStr]\r\n member _.address (value: 'Node) = mk \"address\" [value]\r\n member _.address (value: string) = mk \"address\" [ofStr value]\r\n member _.address (children: seq<'Node>) = mk \"address\" children\r\n\r\n member _.anchor (children: seq<'Node>) = mk \"a\" children\r\n\r\n member _.area (children: seq<'Node>) = mk \"area\" children\r\n\r\n member _.article (children: seq<'Node>) = mk \"article\" children\r\n\r\n member _.aside (children: seq<'Node>) = mk \"aside\" children\r\n\r\n member _.audio (children: seq<'Node>) = mk \"audio\" children\r\n\r\n member _.b (value: float) = mk \"b\" [Util.asString value |> ofStr]\r\n member _.b (value: int) = mk \"b\" [Util.asString value |> ofStr]\r\n member _.b (value: 'Node) = mk \"b\" [value]\r\n member _.b (value: string) = mk \"b\" [ofStr value]\r\n member _.b (children: seq<'Node>) = mk \"b\" children\r\n\r\n member _.base' (children: seq<'Node>) = mk \"base\" children\r\n\r\n member _.bdi (value: float) = mk \"bdi\" [Util.asString value |> ofStr]\r\n member _.bdi (value: int) = mk \"bdi\" [Util.asString value |> ofStr]\r\n member _.bdi (value: 'Node) = mk \"bdi\" [value]\r\n member _.bdi (value: string) = mk \"bdi\" [ofStr value]\r\n member _.bdi (children: seq<'Node>) = mk \"bdi\" children\r\n\r\n member _.bdo (value: float) = mk \"bdo\" [Util.asString value |> ofStr]\r\n member _.bdo (value: int) = mk \"bdo\" [Util.asString value |> ofStr]\r\n member _.bdo (value: 'Node) = mk \"bdo\" [value]\r\n member _.bdo (value: string) = mk \"bdo\" [ofStr value]\r\n member _.bdo (children: seq<'Node>) = mk \"bdo\" children\r\n\r\n member _.blockquote (value: float) = mk \"blockquote\" [Util.asString value |> ofStr]\r\n member _.blockquote (value: int) = mk \"blockquote\" [Util.asString value |> ofStr]\r\n member _.blockquote (value: 'Node) = mk \"blockquote\" [value]\r\n member _.blockquote (value: string) = mk \"blockquote\" [ofStr value]\r\n member _.blockquote (children: seq<'Node>) = mk \"blockquote\" children\r\n\r\n member _.body (value: float) = mk \"body\" [Util.asString value |> ofStr]\r\n member _.body (value: int) = mk \"body\" [Util.asString value |> ofStr]\r\n member _.body (value: 'Node) = mk \"body\" [value]\r\n member _.body (value: string) = mk \"body\" [ofStr value]\r\n member _.body (children: seq<'Node>) = mk \"body\" children\r\n\r\n member _.br (children: seq<'Node>) = mk \"br\" children\r\n\r\n member _.button (children: seq<'Node>) = mk \"button\" children\r\n\r\n member _.canvas (children: seq<'Node>) = mk \"canvas\" children\r\n\r\n member _.caption (value: float) = mk \"caption\" [Util.asString value |> ofStr]\r\n member _.caption (value: int) = mk \"caption\" [Util.asString value |> ofStr]\r\n member _.caption (value: 'Node) = mk \"caption\" [value]\r\n member _.caption (value: string) = mk \"caption\" [ofStr value]\r\n member _.caption (children: seq<'Node>) = mk \"caption\" children\r\n\r\n member _.cite (value: float) = mk \"cite\" [Util.asString value |> ofStr]\r\n member _.cite (value: int) = mk \"cite\" [Util.asString value |> ofStr]\r\n member _.cite (value: 'Node) = mk \"cite\" [value]\r\n member _.cite (value: string) = mk \"cite\" [ofStr value]\r\n member _.cite (children: seq<'Node>) = mk \"cite\" children\r\n\r\n // member _.code (value: bool) = mk \"code\" value\r\n member _.code (value: float) = mk \"code\" [Util.asString value |> ofStr]\r\n member _.code (value: int) = mk \"code\" [Util.asString value |> ofStr]\r\n member _.code (value: 'Node) = mk \"code\" [value]\r\n member _.code (value: string) = mk \"code\" [ofStr value]\r\n member _.code (children: seq<'Node>) = mk \"code\" children\r\n\r\n member _.col (children: seq<'Node>) = mk \"col\" children\r\n\r\n member _.colgroup (children: seq<'Node>) = mk \"colgroup\" children\r\n\r\n member _.data (value: float) = mk \"data\" [Util.asString value |> ofStr]\r\n member _.data (value: int) = mk \"data\" [Util.asString value |> ofStr]\r\n member _.data (value: 'Node) = mk \"data\" [value]\r\n member _.data (value: string) = mk \"data\" [ofStr value]\r\n member _.data (children: seq<'Node>) = mk \"data\" children\r\n\r\n member _.datalist (value: float) = mk \"datalist\" [Util.asString value |> ofStr]\r\n member _.datalist (value: int) = mk \"datalist\" [Util.asString value |> ofStr]\r\n member _.datalist (value: 'Node) = mk \"datalist\" [value]\r\n member _.datalist (value: string) = mk \"datalist\" [ofStr value]\r\n member _.datalist (children: seq<'Node>) = mk \"datalist\" children\r\n\r\n member _.dd (value: float) = mk \"dd\" [Util.asString value |> ofStr]\r\n member _.dd (value: int) = mk \"dd\" [Util.asString value |> ofStr]\r\n member _.dd (value: 'Node) = mk \"dd\" [value]\r\n member _.dd (value: string) = mk \"dd\" [ofStr value]\r\n member _.dd (children: seq<'Node>) = mk \"dd\" children\r\n\r\n member _.del (value: float) = mk \"del\" [Util.asString value |> ofStr]\r\n member _.del (value: int) = mk \"del\" [Util.asString value |> ofStr]\r\n member _.del (value: 'Node) = mk \"del\" [value]\r\n member _.del (value: string) = mk \"del\" [ofStr value]\r\n member _.del (children: seq<'Node>) = mk \"del\" children\r\n\r\n member _.details (children: seq<'Node>) = mk \"details\" children\r\n\r\n member _.dfn (value: float) = mk \"dfn\" [Util.asString value |> ofStr]\r\n member _.dfn (value: int) = mk \"dfn\" [Util.asString value |> ofStr]\r\n member _.dfn (value: 'Node) = mk \"dfn\" [value]\r\n member _.dfn (value: string) = mk \"dfn\" [ofStr value]\r\n member _.dfn (children: seq<'Node>) = mk \"dfn\" children\r\n\r\n member _.dialog (value: float) = mk \"dialog\" [Util.asString value |> ofStr]\r\n member _.dialog (value: int) = mk \"dialog\" [Util.asString value |> ofStr]\r\n member _.dialog (value: 'Node) = mk \"dialog\" [value]\r\n member _.dialog (value: string) = mk \"dialog\" [ofStr value]\r\n member _.dialog (children: seq<'Node>) = mk \"dialog\" children\r\n\r\n member _.div (value: float) = mk \"div\" [Util.asString value |> ofStr]\r\n member _.div (value: int) = mk \"div\" [Util.asString value |> ofStr]\r\n member _.div (value: 'Node) = mk \"div\" [value]\r\n member _.div (value: string) = mk \"div\" [ofStr value]\r\n /// The `` tag defines a division or a section in an HTML document\r\n member _.div (children: seq<'Node>) = mk \"div\" children\r\n\r\n member _.dl (children: seq<'Node>) = mk \"dl\" children\r\n\r\n member _.dt (value: float) = mk \"dt\" [Util.asString value |> ofStr]\r\n member _.dt (value: int) = mk \"dt\" [Util.asString value |> ofStr]\r\n member _.dt (value: 'Node) = mk \"dt\" [value]\r\n member _.dt (value: string) = mk \"dt\" [ofStr value]\r\n member _.dt (children: seq<'Node>) = mk \"dt\" children\r\n\r\n member _.em (value: float) = mk \"em\" [Util.asString value |> ofStr]\r\n member _.em (value: int) = mk \"em\" [Util.asString value |> ofStr]\r\n member _.em (value: 'Node) = mk \"em\" [value]\r\n member _.em (value: string) = mk \"em\" [ofStr value]\r\n member _.em (children: seq<'Node>) = mk \"em\" children\r\n\r\n member _.fieldSet (children: seq<'Node>) = mk \"fieldset\" children\r\n\r\n member _.figcaption (children: seq<'Node>) = mk \"figcaption\" children\r\n\r\n member _.figure (children: seq<'Node>) = mk \"figure\" children\r\n\r\n member _.footer (children: seq<'Node>) = mk \"footer\" children\r\n\r\n member _.form (children: seq<'Node>) = mk \"form\" children\r\n\r\n member _.h1 (value: float) = mk \"h1\" [Util.asString value |> ofStr]\r\n member _.h1 (value: int) = mk \"h1\" [Util.asString value |> ofStr]\r\n member _.h1 (value: 'Node) = mk \"h1\" [value]\r\n member _.h1 (value: string) = mk \"h1\" [ofStr value]\r\n member _.h1 (children: seq<'Node>) = mk \"h1\" children\r\n member _.h2 (value: float) = mk \"h2\" [Util.asString value |> ofStr]\r\n member _.h2 (value: int) = mk \"h2\" [Util.asString value |> ofStr]\r\n member _.h2 (value: 'Node) = mk \"h2\" [value]\r\n member _.h2 (value: string) = mk \"h2\" [ofStr value]\r\n member _.h2 (children: seq<'Node>) = mk \"h2\" children\r\n\r\n member _.h3 (value: float) = mk \"h3\" [Util.asString value |> ofStr]\r\n member _.h3 (value: int) = mk \"h3\" [Util.asString value |> ofStr]\r\n member _.h3 (value: 'Node) = mk \"h3\" [value]\r\n member _.h3 (value: string) = mk \"h3\" [ofStr value]\r\n member _.h3 (children: seq<'Node>) = mk \"h3\" children\r\n\r\n member _.h4 (value: float) = mk \"h4\" [Util.asString value |> ofStr]\r\n member _.h4 (value: int) = mk \"h4\" [Util.asString value |> ofStr]\r\n member _.h4 (value: 'Node) = mk \"h4\" [value]\r\n member _.h4 (value: string) = mk \"h4\" [ofStr value]\r\n member _.h4 (children: seq<'Node>) = mk \"h4\" children\r\n\r\n member _.h5 (value: float) = mk \"h5\" [Util.asString value |> ofStr]\r\n member _.h5 (value: int) = mk \"h5\" [Util.asString value |> ofStr]\r\n member _.h5 (value: 'Node) = mk \"h5\" [value]\r\n member _.h5 (value: string) = mk \"h5\" [ofStr value]\r\n member _.h5 (children: seq<'Node>) = mk \"h5\" children\r\n\r\n member _.h6 (value: float) = mk \"h6\" [Util.asString value |> ofStr]\r\n member _.h6 (value: int) = mk \"h6\" [Util.asString value |> ofStr]\r\n member _.h6 (value: 'Node) = mk \"h6\" [value]\r\n member _.h6 (value: string) = mk \"h6\" [ofStr value]\r\n member _.h6 (children: seq<'Node>) = mk \"h6\" children\r\n\r\n member _.head (children: seq<'Node>) = mk \"head\" children\r\n\r\n member _.header (children: seq<'Node>) = mk \"header\" children\r\n\r\n member _.hr (children: seq<'Node>) = mk \"hr\" children\r\n\r\n member _.html (children: seq<'Node>) = mk \"html\" children\r\n\r\n member _.i (value: float) = mk \"i\" [Util.asString value |> ofStr]\r\n member _.i (value: int) = mk \"i\" [Util.asString value |> ofStr]\r\n member _.i (value: 'Node) = mk \"i\" [value]\r\n member _.i (value: string) = mk \"i\" [ofStr value]\r\n member _.i (children: seq<'Node>) = mk \"i\" children\r\n\r\n member _.iframe (children: seq<'Node>) = mk \"iframe\" children\r\n\r\n member _.img (children: seq<'Node>) = mk \"img\" children\r\n\r\n member _.input (children: seq<'Node>) = mk \"input\" children\r\n\r\n member _.ins (value: float) = mk \"ins\" [Util.asString value |> ofStr]\r\n member _.ins (value: int) = mk \"ins\" [Util.asString value |> ofStr]\r\n member _.ins (value: 'Node) = mk \"ins\" [value]\r\n member _.ins (value: string) = mk \"ins\" [ofStr value]\r\n member _.ins (children: seq<'Node>) = mk \"ins\" children\r\n\r\n member _.kbd (value: float) = mk \"kbd\" [Util.asString value |> ofStr]\r\n member _.kbd (value: int) = mk \"kbd\" [Util.asString value |> ofStr]\r\n member _.kbd (value: 'Node) = mk \"kbd\" [value]\r\n member _.kbd (value: string) = mk \"kbd\" [ofStr value]\r\n member _.kbd (children: seq<'Node>) = mk \"kbd\" children\r\n\r\n member _.label (children: seq<'Node>) = mk \"label\" children\r\n\r\n member _.legend (value: float) = mk \"legend\" [Util.asString value |> ofStr]\r\n member _.legend (value: int) = mk \"legend\" [Util.asString value |> ofStr]\r\n member _.legend (value: 'Node) = mk \"legend\" [value]\r\n member _.legend (value: string) = mk \"legend\" [ofStr value]\r\n member _.legend (children: seq<'Node>) = mk \"legend\" children\r\n\r\n member _.li (value: float) = mk \"li\" [Util.asString value |> ofStr]\r\n member _.li (value: int) = mk \"li\" [Util.asString value |> ofStr]\r\n member _.li (value: 'Node) = mk \"li\" [value]\r\n member _.li (value: string) = mk \"li\" [ofStr value]\r\n member _.li (children: seq<'Node>) = mk \"li\" children\r\n\r\n member _.listItem (value: float) = mk \"li\" [Util.asString value |> ofStr]\r\n member _.listItem (value: int) = mk \"li\" [Util.asString value |> ofStr]\r\n member _.listItem (value: 'Node) = mk \"li\" [value]\r\n member _.listItem (value: string) = mk \"li\" [ofStr value]\r\n member _.listItem (children: seq<'Node>) = mk \"li\" children\r\n\r\n member _.main (children: seq<'Node>) = mk \"main\" children\r\n\r\n member _.map (children: seq<'Node>) = mk \"map\" children\r\n\r\n member _.mark (value: float) = mk \"mark\" [Util.asString value |> ofStr]\r\n member _.mark (value: int) = mk \"mark\" [Util.asString value |> ofStr]\r\n member _.mark (value: 'Node) = mk \"mark\" [value]\r\n member _.mark (value: string) = mk \"mark\" [ofStr value]\r\n member _.mark (children: seq<'Node>) = mk \"mark\" children\r\n\r\n member _.metadata (children: seq<'Node>) = mk \"metadata\" children\r\n\r\n member _.meter (value: float) = mk \"meter\" [Util.asString value |> ofStr]\r\n member _.meter (value: int) = mk \"meter\" [Util.asString value |> ofStr]\r\n member _.meter (value: 'Node) = mk \"meter\" [value]\r\n member _.meter (value: string) = mk \"meter\" [ofStr value]\r\n member _.meter (children: seq<'Node>) = mk \"meter\" children\r\n\r\n member _.nav (children: seq<'Node>) = mk \"nav\" children\r\n\r\n member _.noscript (children: seq<'Node>) = mk \"noscript\" children\r\n\r\n member _.object (children: seq<'Node>) = mk \"object\" children\r\n\r\n member _.ol (children: seq<'Node>) = mk \"ol\" children\r\n\r\n member _.option (value: float) = mk \"option\" [Util.asString value |> ofStr]\r\n member _.option (value: int) = mk \"option\" [Util.asString value |> ofStr]\r\n member _.option (value: 'Node) = mk \"option\" [value]\r\n member _.option (value: string) = mk \"option\" [ofStr value]\r\n member _.option (children: seq<'Node>) = mk \"option\" children\r\n\r\n member _.optgroup (children: seq<'Node>) = mk \"optgroup\" children\r\n\r\n member _.orderedList (children: seq<'Node>) = mk \"ol\" children\r\n\r\n member _.output (value: float) = mk \"output\" [Util.asString value |> ofStr]\r\n member _.output (value: int) = mk \"output\" [Util.asString value |> ofStr]\r\n member _.output (value: 'Node) = mk \"output\" [value]\r\n member _.output (value: string) = mk \"output\" [ofStr value]\r\n member _.output (children: seq<'Node>) = mk \"output\" children\r\n\r\n member _.p (value: float) = mk \"p\" [Util.asString value |> ofStr]\r\n member _.p (value: int) = mk \"p\" [Util.asString value |> ofStr]\r\n member _.p (value: 'Node) = mk \"p\" [value]\r\n member _.p (value: string) = mk \"p\" [ofStr value]\r\n member _.p (children: seq<'Node>) = mk \"p\" children\r\n\r\n member _.paragraph (value: float) = mk \"p\" [Util.asString value |> ofStr]\r\n member _.paragraph (value: int) = mk \"p\" [Util.asString value |> ofStr]\r\n member _.paragraph (value: 'Node) = mk \"p\" [value]\r\n member _.paragraph (value: string) = mk \"p\" [ofStr value]\r\n member _.paragraph (children: seq<'Node>) = mk \"p\" children\r\n\r\n member _.picture (children: seq<'Node>) = mk \"picture\" children\r\n\r\n // member _.pre (value: bool) = mk \"pre\" value\r\n member _.pre (value: float) = mk \"pre\" [Util.asString value |> ofStr]\r\n member _.pre (value: int) = mk \"pre\" [Util.asString value |> ofStr]\r\n member _.pre (value: 'Node) = mk \"pre\" [value]\r\n member _.pre (value: string) = mk \"pre\" [ofStr value]\r\n member _.pre (children: seq<'Node>) = mk \"pre\" children\r\n\r\n member _.progress (children: seq<'Node>) = mk \"progress\" children\r\n\r\n member _.q (children: seq<'Node>) = mk \"q\" children\r\n\r\n member _.rb (value: float) = mk \"rb\" [Util.asString value |> ofStr]\r\n member _.rb (value: int) = mk \"rb\" [Util.asString value |> ofStr]\r\n member _.rb (value: 'Node) = mk \"rb\" [value]\r\n member _.rb (value: string) = mk \"rb\" [ofStr value]\r\n member _.rb (children: seq<'Node>) = mk \"rb\" children\r\n\r\n member _.rp (value: float) = mk \"rp\" [Util.asString value |> ofStr]\r\n member _.rp (value: int) = mk \"rp\" [Util.asString value |> ofStr]\r\n member _.rp (value: 'Node) = mk \"rp\" [value]\r\n member _.rp (value: string) = mk \"rp\" [ofStr value]\r\n member _.rp (children: seq<'Node>) = mk \"rp\" children\r\n\r\n member _.rt (value: float) = mk \"rt\" [Util.asString value |> ofStr]\r\n member _.rt (value: int) = mk \"rt\" [Util.asString value |> ofStr]\r\n member _.rt (value: 'Node) = mk \"rt\" [value]\r\n member _.rt (value: string) = mk \"rt\" [ofStr value]\r\n member _.rt (children: seq<'Node>) = mk \"rt\" children\r\n\r\n member _.rtc (value: float) = mk \"rtc\" [Util.asString value |> ofStr]\r\n member _.rtc (value: int) = mk \"rtc\" [Util.asString value |> ofStr]\r\n member _.rtc (value: 'Node) = mk \"rtc\" [value]\r\n member _.rtc (value: string) = mk \"rtc\" [ofStr value]\r\n member _.rtc (children: seq<'Node>) = mk \"rtc\" children\r\n\r\n member _.ruby (value: float) = mk \"ruby\" [Util.asString value |> ofStr]\r\n member _.ruby (value: int) = mk \"ruby\" [Util.asString value |> ofStr]\r\n member _.ruby (value: 'Node) = mk \"ruby\" [value]\r\n member _.ruby (value: string) = mk \"ruby\" [ofStr value]\r\n member _.ruby (children: seq<'Node>) = mk \"ruby\" children\r\n\r\n member _.s (value: float) = mk \"s\" [Util.asString value |> ofStr]\r\n member _.s (value: int) = mk \"s\" [Util.asString value |> ofStr]\r\n member _.s (value: 'Node) = mk \"s\" [value]\r\n member _.s (value: string) = mk \"s\" [ofStr value]\r\n member _.s (children: seq<'Node>) = mk \"s\" children\r\n\r\n member _.samp (value: float) = mk \"samp\" [Util.asString value |> ofStr]\r\n member _.samp (value: int) = mk \"samp\" [Util.asString value |> ofStr]\r\n member _.samp (value: 'Node) = mk \"samp\" [value]\r\n member _.samp (value: string) = mk \"samp\" [ofStr value]\r\n member _.samp (children: seq<'Node>) = mk \"samp\" children\r\n\r\n member _.script (children: seq<'Node>) = mk \"script\" children\r\n\r\n member _.section (children: seq<'Node>) = mk \"section\" children\r\n\r\n member _.select (children: seq<'Node>) = mk \"select\" children\r\n member _.small (value: float) = mk \"small\" [Util.asString value |> ofStr]\r\n member _.small (value: int) = mk \"small\" [Util.asString value |> ofStr]\r\n member _.small (value: 'Node) = mk \"small\" [value]\r\n member _.small (value: string) = mk \"small\" [ofStr value]\r\n member _.small (children: seq<'Node>) = mk \"small\" children\r\n\r\n member _.source (children: seq<'Node>) = mk \"source\" children\r\n\r\n member _.span (value: float) = mk \"span\" [Util.asString value |> ofStr]\r\n member _.span (value: int) = mk \"span\" [Util.asString value |> ofStr]\r\n member _.span (value: 'Node) = mk \"span\" [value]\r\n member _.span (value: string) = mk \"span\" [ofStr value]\r\n member _.span (children: seq<'Node>) = mk \"span\" children\r\n\r\n member _.strong (value: float) = mk \"strong\" [Util.asString value |> ofStr]\r\n member _.strong (value: int) = mk \"strong\" [Util.asString value |> ofStr]\r\n member _.strong (value: 'Node) = mk \"strong\" [value]\r\n member _.strong (value: string) = mk \"strong\" [ofStr value]\r\n member _.strong (children: seq<'Node>) = mk \"strong\" children\r\n\r\n member _.style (value: string) = mk \"style\" [ofStr value]\r\n\r\n member _.sub (value: float) = mk \"sub\" [Util.asString value |> ofStr]\r\n member _.sub (value: int) = mk \"sub\" [Util.asString value |> ofStr]\r\n member _.sub (value: 'Node) = mk \"sub\" [value]\r\n member _.sub (value: string) = mk \"sub\" [ofStr value]\r\n member _.sub (children: seq<'Node>) = mk \"sub\" children\r\n\r\n member _.summary (value: float) = mk \"summary\" [Util.asString value |> ofStr]\r\n member _.summary (value: int) = mk \"summary\" [Util.asString value |> ofStr]\r\n member _.summary (value: 'Node) = mk \"summary\" [value]\r\n member _.summary (value: string) = mk \"summary\" [ofStr value]\r\n member _.summary (children: seq<'Node>) = mk \"summary\" children\r\n\r\n member _.sup (value: float) = mk \"sup\" [Util.asString value |> ofStr]\r\n member _.sup (value: int) = mk \"sup\" [Util.asString value |> ofStr]\r\n member _.sup (value: 'Node) = mk \"sup\" [value]\r\n member _.sup (value: string) = mk \"sup\" [ofStr value]\r\n member _.sup (children: seq<'Node>) = mk \"sup\" children\r\n\r\n member _.table (children: seq<'Node>) = mk \"table\" children\r\n\r\n member _.tableBody (children: seq<'Node>) = mk \"tbody\" children\r\n\r\n member _.tableCell (children: seq<'Node>) = mk \"td\" children\r\n\r\n member _.tableHeader (children: seq<'Node>) = mk \"th\" children\r\n\r\n member _.tableRow (children: seq<'Node>) = mk \"tr\" children\r\n\r\n member _.tbody (children: seq<'Node>) = mk \"tbody\" children\r\n\r\n member _.td (value: float) = mk \"td\" [Util.asString value |> ofStr]\r\n member _.td (value: int) = mk \"td\" [Util.asString value |> ofStr]\r\n member _.td (value: 'Node) = mk \"td\" [value]\r\n member _.td (value: string) = mk \"td\" [ofStr value]\r\n member _.td (children: seq<'Node>) = mk \"td\" children\r\n\r\n member _.template (children: seq<'Node>) = mk \"template\" children\r\n\r\n member _.text (value: float) : 'Node = Util.asString value |> ofStr\r\n member _.text (value: int) : 'Node = Util.asString value |> ofStr\r\n member _.text (value: string) : 'Node = ofStr value\r\n member _.text (value: System.Guid) : 'Node = Util.asString value |> ofStr\r\n\r\n member this.textf fmt = Printf.kprintf this.text fmt\r\n\r\n member _.textarea (children: seq<'Node>) = mk \"textarea\" children\r\n\r\n member _.tfoot (children: seq<'Node>) = mk \"tfoot\" children\r\n\r\n member _.th (value: float) = mk \"th\" [Util.asString value |> ofStr]\r\n member _.th (value: int) = mk \"th\" [Util.asString value |> ofStr]\r\n member _.th (value: 'Node) = mk \"th\" [value]\r\n member _.th (value: string) = mk \"th\" [ofStr value]\r\n member _.th (children: seq<'Node>) = mk \"th\" children\r\n\r\n member _.thead (children: seq<'Node>) = mk \"thead\" children\r\n\r\n member _.time (children: seq<'Node>) = mk \"time\" children\r\n\r\n member _.tr (children: seq<'Node>) = mk \"tr\" children\r\n\r\n member _.track (children: seq<'Node>) = mk \"track\" children\r\n\r\n member _.u (value: float) = mk \"u\" [Util.asString value |> ofStr]\r\n member _.u (value: int) = mk \"u\" [Util.asString value |> ofStr]\r\n member _.u (value: 'Node) = mk \"u\" [value]\r\n member _.u (value: string) = mk \"u\" [ofStr value]\r\n member _.u (children: seq<'Node>) = mk \"u\" children\r\n\r\n member _.ul (children: seq<'Node>) = mk \"ul\" children\r\n\r\n member _.unorderedList (children: seq<'Node>) = mk \"ul\" children\r\n\r\n member _.var (value: float) = mk \"var\" [Util.asString value |> ofStr]\r\n member _.var (value: int) = mk \"var\" [Util.asString value |> ofStr]\r\n member _.var (value: 'Node) = mk \"var\" [value]\r\n member _.var (value: string) = mk \"var\" [ofStr value]\r\n member _.var (children: seq<'Node>) = mk \"var\" children\r\n\r\n member _.video (children: seq<'Node>) = mk \"video\" children\r\n\r\n member _.wbr (children: seq<'Node>) = mk \"wbr\" children\r\n","/// \n/// Easing functions for CSS transitions\n/// \nmodule Sutil.Easing\n// Adapted from svelte/easing/index.js, which in turn are..\n (*\n Adapted from https://github.com/mattdesl\n Distributed under MIT License https://github.com/mattdesl/eases/blob/master/LICENSE.md\n *)\n\n// For visualizations of these easing functions\n// https://easings.net/\n\nopen System\n\nlet linear = id\n\nlet backInOut t =\n let s = 1.70158 * 1.525\n if (t < 0.5) then\n let tin = t * 2.0\n 0.5 * (tin * tin * ((s + 1.0) * tin - s))\n else\n let tout = t - 1.0\n 0.5 * (tout * tout * ((s + 1.0) * tout + s) + 2.0)\n\nlet backIn t =\n let s = 1.70158\n t * t * ((s + 1.0) * t - s)\n\nlet backOut t =\n let s = 1.70158\n let t' = t - 1.0\n t' * t' * ((s + 1.0) * t' + s) + 1.0\n\nlet cubicIn (t : float) = t * t * t\n\nlet cubicOut t =\n let f = t - 1.0\n f * f * f + 1.0\n\nlet cubicInOut t =\n if t < 0.5 then 4.0 * t * t * t else 0.5 * System.Math.Pow(2.0 * t - 2.0, 3.0) + 1.0\n\n// todo; ported from JS, might read better if refactored. Might not run as fast though...do the refactor and see how it looks\nlet quadInOut t =\n let tin = t / 0.5;\n if (tin < 1.0) then\n 0.5 * tin * tin // In: t < 0.5, tin = 0 .. 1\n else\n let tout = tin - 1.0 // Out: t>= 0.5, tout = 0 .. 1\n -0.5 * (tout * (tout - 2.0) - 1.0)\n\nlet quadIn (t : float) =\n t * t\n\nlet quadOut t =\n -t * (t - 2.0)\n\nlet quartIn t = Math.Pow(t,4.0)\n\nlet quartOut t =\n Math.Pow(t - 1.0, 3.0) * (1.0 - t) + 1.0;\n\nlet quartInOut t =\n if t < 0.5 then 8.0 * t * t * t * t else -8.0 * System.Math.Pow(t - 1.0, 4.0) + 1.0\n //return t < 0.5\n // ? +8.0 * Math.pow(t, 4.0)\n // : -8.0 * Math.pow(t - 1.0, 4.0) + 1.0;\n\nlet elasticIn t =\n Math.Sin((13.0 * t * Math.PI) / 2.0) * Math.Pow(2.0, 10.0 * (t - 1.0))\n\nlet elasticOut t =\n Math.Sin((-13.0 * (t + 1.0) * Math.PI) / 2.0) * Math.Pow(2.0, -10.0 * t) + 1.0\n\nlet quintIn (t:float) =\n t * t * t * t * t\n\nlet quintOut t =\n let t' = t - 1.0\n t' * t' * t' * t' * t' + 1.0\n\nlet expoInOut t =\n if t = 0.0 || t = 1.0 then\n t\n else if t < 0.5 then\n +0.5 * Math.Pow(2.0, 20.0 * t - 10.0)\n else\n -0.5 * Math.Pow(2.0, 10.0 - t * 20.0) + 1.0\n\nlet expoIn t =\n if t = 0.0 then t else Math.Pow(2.0, 10.0 * (t - 1.0))\n\nlet expoOut t =\n if t = 1.0 then t else 1.0 - Math.Pow(2.0, -10.0 * t)\n\n(*\n/*\nAdapted from https://github.com/mattdesl\nDistributed under MIT License https://github.com/mattdesl/eases/blob/master/LICENSE.md\n*/\n*DONE* function backInOut(t) {\n const s = 1.70158 * 1.525;\n if ((t *= 2) < 1)\n return 0.5 * (t * t * ((s + 1) * t - s));\n return 0.5 * ((t -= 2) * t * ((s + 1) * t + s) + 2);\n}\n*DONE* function backIn(t) {\n const s = 1.70158;\n return t * t * ((s + 1) * t - s);\n}\n*DONE* function backOut(t) {\n const s = 1.70158;\n return --t * t * ((s + 1) * t + s) + 1;\n}\nfunction bounceOut(t) {\n const a = 4.0 / 11.0;\n const b = 8.0 / 11.0;\n const c = 9.0 / 10.0;\n const ca = 4356.0 / 361.0;\n const cb = 35442.0 / 1805.0;\n const cc = 16061.0 / 1805.0;\n const t2 = t * t;\n return t < a\n ? 7.5625 * t2\n : t < b\n ? 9.075 * t2 - 9.9 * t + 3.4\n : t < c\n ? ca * t2 - cb * t + cc\n : 10.8 * t * t - 20.52 * t + 10.72;\n}\nfunction bounceInOut(t) {\n return t < 0.5\n ? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0))\n : 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5;\n}\nfunction bounceIn(t) {\n return 1.0 - bounceOut(1.0 - t);\n}\nfunction circInOut(t) {\n if ((t *= 2) < 1)\n return -0.5 * (Math.sqrt(1 - t * t) - 1);\n return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);\n}\nfunction circIn(t) {\n return 1.0 - Math.sqrt(1.0 - t * t);\n}\nfunction circOut(t) {\n return Math.sqrt(1 - --t * t);\n}\n*DONE* function cubicInOut(t) {\n return t < 0.5 ? 4.0 * t * t * t : 0.5 * Math.pow(2.0 * t - 2.0, 3.0) + 1.0;\n}\n*DONE* function cubicIn(t) {\n return t * t * t;\n}\n*DONE* function cubicOut(t) {\n const f = t - 1.0;\n return f * f * f + 1.0;\n}\nfunction elasticInOut(t) {\n return t < 0.5\n ? 0.5 *\n Math.sin(((+13.0 * Math.PI) / 2) * 2.0 * t) *\n Math.pow(2.0, 10.0 * (2.0 * t - 1.0))\n : 0.5 *\n Math.sin(((-13.0 * Math.PI) / 2) * (2.0 * t - 1.0 + 1.0)) *\n Math.pow(2.0, -10.0 * (2.0 * t - 1.0)) +\n 1.0;\n}\n*DONE* function elasticIn(t) {\n return Math.sin((13.0 * t * Math.PI) / 2) * Math.pow(2.0, 10.0 * (t - 1.0));\n}\n*DONE* function elasticOut(t) {\n return (Math.sin((-13.0 * (t + 1.0) * Math.PI) / 2) * Math.pow(2.0, -10.0 * t) + 1.0);\n}\nfunction expoInOut(t) {\n return t === 0.0 || t === 1.0\n ? t\n : t < 0.5\n ? +0.5 * Math.pow(2.0, 20.0 * t - 10.0)\n : -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0;\n}\nfunction expoIn(t) {\n return t === 0.0 ? t : Math.pow(2.0, 10.0 * (t - 1.0));\n}\nfunction expoOut(t) {\n return t === 1.0 ? t : 1.0 - Math.pow(2.0, -10.0 * t);\n}\nfunction quadInOut(t) {\n t /= 0.5;\n if (t < 1)\n return 0.5 * t * t;\n t--;\n return -0.5 * (t * (t - 2) - 1);\n}\nfunction quadIn(t) {\n return t * t;\n}\nfunction quadOut(t) {\n return -t * (t - 2.0);\n}\nfunction quartInOut(t) {\n return t < 0.5\n ? +8.0 * Math.pow(t, 4.0)\n : -8.0 * Math.pow(t - 1.0, 4.0) + 1.0;\n}\nfunction quartIn(t) {\n return Math.pow(t, 4.0);\n}\nfunction quartOut(t) {\n return Math.pow(t - 1.0, 3.0) * (1.0 - t) + 1.0;\n}\nfunction quintInOut(t) {\n if ((t *= 2) < 1)\n return 0.5 * t * t * t * t * t;\n return 0.5 * ((t -= 2) * t * t * t * t + 2);\n}\n*DONE* function quintIn(t) {\n return t * t * t * t * t;\n}\n*DONE* function quintOut(t) {\n return --t * t * t * t * t + 1;\n}\nfunction sineInOut(t) {\n return -0.5 * (Math.cos(Math.PI * t) - 1);\n}\nfunction sineIn(t) {\n const v = Math.cos(t * Math.PI * 0.5);\n if (Math.abs(v) < 1e-14)\n return 1;\n else\n return 1 - v;\n}\nfunction sineOut(t) {\n return Math.sin((t * Math.PI) / 2);\n}\n\n\n\n*DONE* Object.defineProperty(exports, 'linear', {\n\tenumerable: true,\n\tget: function () {\n\t\treturn internal.identity;\n\t}\n});\n\n*)\n","/// \n/// Support CSS styling\n/// \nmodule Sutil.Styling\n\nopen System\nopen Browser.Types\nopen Sutil.Core\nopen Sutil.DomHelpers\nopen Browser.Dom\n\nlet private logEnabled() = Logging.isEnabled \"style\"\nlet private log s = Logging.log \"style\" s\nlet private findElement (doc : Document) selector = doc.querySelector(selector)\n\nlet parseStyleAttr (style : string) =\n style.Split([|';'|], StringSplitOptions.RemoveEmptyEntries)\n |> Array.collect (fun entry ->\n entry.Split([|':'|],2)\n |> Array.chunkBySize 2\n |> Array.map (fun pair -> pair.[0].Trim(), pair.[1].Trim()))\n\nlet emitStyleAttr (keyValues : (string * string) array) =\n keyValues\n |> Array.map (fun (k,v) -> sprintf \"%s:%s;\" k v )\n |> String.concat \"\"\n\nlet filterStyleAttr name style =\n parseStyleAttr style\n |> Array.filter (fun (k,v) -> k <> name)\n |> emitStyleAttr\n\nlet getStyleAttr (el : HTMLElement) =\n match el.getAttribute(\"style\") with\n | null -> \"\"\n | s -> s\n\nlet addStyleAttr (el : HTMLElement) name value =\n let style = getStyleAttr el |> filterStyleAttr name\n el.setAttribute( \"style\", sprintf \"%s%s:%s;\" style name value )\n\nlet removeStyleAttr (el : HTMLElement) name =\n if logEnabled() then log( sprintf \"filter by %s: %A -> %A\" name (getStyleAttr el) (getStyleAttr el |> filterStyleAttr name) )\n el.setAttribute( \"style\", getStyleAttr el |> filterStyleAttr name )\n\nlet newStyleElement (doc : Document)=\n let head = \"head\" |> findElement doc\n let style = doc.createElement(\"style\")\n head.appendChild(style :> Node) |> ignore\n style\n\nlet splitMapJoin (delim:char) (f : string -> string) (s:string) =\n s.Split([| delim |], StringSplitOptions.RemoveEmptyEntries )\n |> Array.map f\n |> fun values -> String.Join(string delim, values)\n\nlet mapPseudo (f : string -> string) (s : string) =\n let i = s.IndexOf(':')\n if i < 0 then\n f s\n else\n f (s.Substring(0,i)) + (s.Substring(i))\n\nlet isPseudo s =\n s = \"hover\" || s = \"active\" || s = \"visited\" || s = \"link\" || s = \"before\" || s = \"after\" || s = \"checked\" || s = \"marker\"\n\nlet isGlobal s = s = \"body\" || s = \"html\"\n\nlet specifySelector (styleName : string) (selectors : string) =\n if (styleName = \"\") then\n selectors\n else\n let trans s = if isPseudo s || isGlobal s then s else sprintf \"%s.%s\" s styleName // button -> button.styleA\n splitMapJoin ',' (splitMapJoin ' ' (mapPseudo trans)) selectors\n\nlet private styleListToText (css : list) =\n \" {\\n\" + String.Join (\"\\n\", css |> Seq.map (fun (nm,v) -> $\" {nm}: {v};\")) + \" }\\n\"\n\nlet private frameToText (f : KeyFrame) =\n sprintf \"%d%% %s\" f.StartAt (styleListToText f.Style)\n\nlet private framesToText (frames : KeyFrames) =\n sprintf \"@keyframes %s {\\n%s\\n}\\n\"\n frames.Name\n (String.Join(\"\\n\", frames.Frames |> List.map frameToText))\n\nlet private isSutilRule (nm:string,v) = nm.StartsWith(\"sutil\")\n\nlet private ruleToText (styleName : string) (rule:StyleRule) =\n //rule.SelectorSpec + (styleListToText rule.Style)\n let styleText = String.Join (\"\\n\", rule.Style |> Seq.filter (not << isSutilRule) |> Seq.map (fun (nm,v) -> $\" {nm}: {v};\"))\n [\n specifySelector styleName rule.SelectorSpec\n \" {\\n\"\n styleText\n \"}\\n\"\n ] |> String.concat \"\"\n\nlet rec mediaRuleToText styleName rule =\n sprintf \"@media %s {\\n%s\\n}\\n\" (rule.Condition) (rule.Rules |> List.map (entryToText styleName) |> String.concat \"\\n\")\n\nand entryToText (styleName : string) = function\n | Rule rule ->\n ruleToText styleName rule\n | KeyFrames frames ->\n framesToText frames\n | MediaRule rule ->\n mediaRuleToText styleName rule\n\nlet private styleSheetAsText (styleSheet : StyleSheetDefinitions) =\n System.String.Join(\"\\n\", styleSheet |> List.map (entryToText \"\"))\n\nlet private addStyleSheet (doc:Document) styleName (styleSheet : StyleSheetDefinitions) =\n let style = newStyleElement doc\n for entry in styleSheet do\n entryToText styleName entry |> doc.createTextNode |> style.appendChild |> ignore\n (fun () -> style.parentElement.removeChild(style) |> ignore)\n\nlet addGlobalStyleSheet (doc:Document) (styleSheet : StyleSheetDefinitions) =\n addStyleSheet doc \"\" styleSheet\n\n/// \n/// Define a CSS styling rule\n/// \nlet rule selector style =\n let result = Rule {\n SelectorSpec = selector\n //Selector = parseSelector selector\n Style = style\n }\n //log($\"%s{selector} -> %A{result.Selector}\")\n result\n\n/// \n/// Define a CSS keyframe as part of a keyframes sequence\n/// See also: \n/// \nlet keyframe startAt style =\n {\n StartAt = startAt\n Style = style\n }\n\n/// \n/// Define a CSS keyframes sequence\n/// \n/// \n/// \n/// keyframes \"dashdraw\" [\n/// keyframe 0 [\n/// Css.custom(\"stroke-dashoffset\", \"10\")\n/// ]\n/// ]\n///
\n/// \nlet keyframes name frames =\n KeyFrames {\n Name = name\n Frames = frames\n }\n\nlet internal showEl (el : HTMLElement) isVisible =\n if isVisible then\n if Interop.exists el \"_display\" then\n addStyleAttr el \"display\" (Interop.get el \"_display\")\n else\n removeStyleAttr el \"display\"\n else\n addStyleAttr el \"display\" \"none\"\n let ev = Interop.customEvent (if isVisible then Event.Show else Event.Hide) {| |}\n el.dispatchEvent(ev) |> ignore\n ()\n\nlet internal makeMediaRule condition rules =\n MediaRule { Condition = condition; Rules = rules }\n\nopen Browser.Css\n\n#if !FABLE_REPL_LIB\nConstructStyleSheetsPolyfill.register()\n#endif\n\nopen Fable.Core\n\n\ntype internal Node with\n /// returns this DocumentOrShadow adopted stylesheets or sets them.\n /// https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets\n []\n member __.adoptedStyleSheets with get(): CSSStyleSheet array = jsNative and set(v: CSSStyleSheet array) = jsNative\n []\n member __.getRootNode() : Node = jsNative\n\nlet adoptStyleSheet (styleSheet : StyleSheetDefinitions) =\n SutilElement.Define( \"adoptStyleSheet\",\n fun ctx ->\n let run() =\n let sheet = CSSStyleSheet.Create()\n sheet.replaceSync (styleSheetAsText styleSheet)\n\n let rootNode : Node = ctx.ParentNode.getRootNode()\n\n rootNode.adoptedStyleSheets <- Array.concat [ rootNode.adoptedStyleSheets; [| sheet |] ]\n\n if ctx.Parent.IsConnected() then\n run()\n else\n rafu run\n () )\n\nlet private ruleMatchEl (el: HTMLElement) (rule: StyleRule) =\n el.matches(rule.SelectorSpec)\n\nlet private rulesOf (styleSheet: StyleSheetDefinitions) =\n styleSheet\n |> List.map (function\n | Rule r -> Some r\n | _ -> None)\n |> List.choose id\n\nopen Browser.Types\nopen Browser.Css\nopen Browser.CssExtensions\n\nlet private applyCustomRulesToElement (rules : StyleRule list) (e: HTMLElement) =\n for rule in rules |> List.filter (ruleMatchEl e) do\n for custom in rule.Style do\n match custom with\n | (nm, v) when nm = \"sutil-use-global\" ->\n failwith \"sutil-use-global not supported\"\n | (nm, v) when nm = \"sutil-use-parent\" -> ()\n | (nm, v) when nm = \"sutil-add-class\" ->\n ClassHelpers.addToClasslist (string v) e\n | (nm,v) ->\n e.style.setProperty( nm, string v )\n\n\nlet private applyCustomRules (rules : StyleSheetDefinitions) (ctx: BuildContext, result : SutilEffect) =\n match result with\n | DomNode n ->\n n |> applyIfElement (rulesOf rules |> applyCustomRulesToElement)\n | _ -> ()\n (ctx, result)\n\n/// \n/// Support for the custom rules \"sutil-add-class\". They're clever but also difficult to understand. See their usage in Sutil.Bulma\n/// \nlet withCustomRules (rules : StyleSheetDefinitions) (element : SutilElement) =\n SutilElement.Define(\"withCustomRules\",\n fun ctx ->\n ctx\n |> ContextHelpers.withPostProcess (applyCustomRules rules)\n |> build element )\n\nlet private applyStyleSheet (namedSheet : NamedStyleSheet) (ctx: BuildContext, result : SutilEffect)=\n match result with\n | DomNode _ ->\n result.AsDomNode\n |> applyIfElement\n (fun el ->\n if not (Interop.exists el NodeKey.StyleClass) then\n Interop.set el (NodeKey.StyleClass) namedSheet.Name\n ClassHelpers.addToClasslist namedSheet.Name el)\n | _ -> ()\n (ctx, result)\n\nlet withStyle styleSheet (element : SutilElement) : SutilElement =\n SutilElement.Define(\"withStyle\",\n fun ctx ->\n let name = ctx.MakeName \"sutil\"\n let namedSheet = { Name = name; StyleSheet = styleSheet }\n addStyleSheet ctx.Document name styleSheet |> ignore\n ctx\n |> ContextHelpers.withPreProcess (applyStyleSheet namedSheet)\n |> build element )\n","/// \n/// Support for CSS transitions that can react to store values\n/// \nmodule Sutil.Transition\n// Adapted from svelte/transitions/index.js\n\nopen Browser.CssExtensions\nopen Browser.Types\nopen Styling\nopen Core\nopen DomHelpers\nopen System.Collections.Generic\nopen System\nopen Fable.Core\nopen Interop\n\nlet private logEnabled() = Logging.isEnabled \"trans\"\nlet private log s = Logging.log \"trans\" s\n\nmodule private LoopTasks =\n\n type private Task = { C : float -> bool; F : unit -> unit }\n\n type LoopTask = { Promise : JS.Promise; Abort: (unit -> unit) }\n\n let mutable private tasks = new HashSet()\n\n let rec runTasks(now) =\n tasks |> Array.ofSeq |> Array.iter (fun task ->\n if not (task.C(now)) then\n tasks.Remove(task) |> ignore\n task.F()\n )\n if tasks.Count <> 0 then\n raf runTasks |> ignore\n\n (**\n * For testing purposes only!\n *)\n let private clearLoops =\n tasks.Clear()\n\n (**\n * Creates a new task that runs on each raf frame\n * until it returns a falsy value or is aborted\n *)\n let loop (callback: float -> bool) =\n let mutable task = Unchecked.defaultof\n\n if tasks.Count = 0 then\n raf runTasks |> ignore\n\n {\n Promise = Promise.create( fun fulfill _ ->\n task <- { C = callback; F = fulfill }\n tasks.Add(task) |> ignore\n )\n\n Abort = fun _ -> tasks.Remove (task) |> ignore\n }\n\ntype TransitionProp =\n | Key of string\n | X of float\n | Y of float\n | Opacity of float\n | Delay of float\n | Duration of float\n | DurationFn of (float -> float)\n | Ease of (float -> float)\n | CssGen of (float -> float -> string )\n | Tick of (float -> float -> unit)\n | Speed of float\n | Fallback of TransitionBuilder\n\nand Transition =\n {\n Key : string\n X : float\n Y : float\n Opacity : float\n Delay : float\n Duration : float\n DurationFn : (float->float) option\n Speed : float\n Ease : (float -> float)\n CssGen : (float -> float -> string ) option\n Tick: (float -> float -> unit) option\n Fallback: TransitionBuilder option\n } with\n static member Default = {\n Key =\"\"\n X = 0.0\n Y = 0.0\n Delay = 0.0\n Opacity = 0.0\n Duration = 0.0\n DurationFn = None\n Speed = 0.0\n Ease = Easing.linear\n CssGen = None\n Fallback = None\n Tick = None }\n\nand CreateTransition =\n unit -> Transition\n\nand TransitionBuilder = TransitionProp list -> HTMLElement -> CreateTransition\n\ntype Animation = {\n From: ClientRect\n To: ClientRect\n}\n\nlet mergeProps newerProps existingProps : TransitionProp list =\n existingProps @ newerProps\n\nlet withProps (userProps : TransitionProp list) (f : TransitionBuilder) : TransitionBuilder =\n fun (initProps : TransitionProp list) ->\n initProps |> mergeProps userProps |> f\n\ntype TransitionAttribute =\n | InOut of TransitionBuilder\n | In of TransitionBuilder\n | Out of TransitionBuilder\n\nlet private overrideDuration d = if Sutil.DevToolsControl.Options.SlowAnimations then 10.0 * d else d\nlet private overrideDurationFn fo = if Sutil.DevToolsControl.Options.SlowAnimations then (fo |> Option.map (fun f -> ((*)10.0 << f))) else fo\n\nlet private applyProp (r:Transition) (prop : TransitionProp) =\n match prop with\n | Delay d -> { r with Delay = d }\n | Duration d -> { r with Duration = d; DurationFn = None }\n | DurationFn fo -> { r with DurationFn = Some fo; Duration = 0.0 }\n | Ease f -> { r with Ease = f }\n | CssGen f -> { r with CssGen = Some f }\n | Tick f -> { r with Tick = Some f }\n | Speed s -> { r with Speed = s }\n | X n -> { r with X = n }\n | Y n -> { r with Y = n }\n | Opacity n -> { r with Opacity = n }\n | Key f -> { r with Key = f }\n | Fallback f -> { r with Fallback = Some f }\n\nlet applyProps (props : TransitionProp list) (tr:Transition) = props |> List.fold applyProp tr\nlet makeTransition (props : TransitionProp list) = applyProps props Transition.Default\nlet mapTrans (f: Transition -> TransitionProp list) t = applyProps (f t) t\n\nlet element (doc:Document) tag = doc.createElement(tag)\n\nlet mutable private numActiveAnimations = 0\nlet mutable private tasks : (unit -> unit) list = []\nlet mutable private activeDocs : Map = Map.empty\n\nlet private registerDoc (doc:Document) =\n activeDocs <- activeDocs.Add( doc.GetHashCode(), doc )\n if logEnabled() then log($\"Active docs: {activeDocs.Count}\")\n\nlet private runTasks() =\n let copy = tasks\n tasks <- []\n if (copy.Length > 0) then\n if logEnabled() then log($\"- - - Tasks: running {copy.Length} tasks - - - - - - - - - - - - - -\")\n for f in copy do f()\n\nlet private waitAnimationFrame f =\n let init = tasks.IsEmpty\n tasks <- f :: tasks\n if init then\n Window.requestAnimationFrame( fun _ ->\n runTasks()\n ) |> ignore\n\nlet private getSutilStyleElement (doc : Document) =\n let mutable e = doc.querySelector(\"head style#__sutil_keyframes\")\n if (isNull e) then\n e <- element doc \"style\"\n e.setAttribute(\"id\", \"__sutil_keyframes\")\n doc.head.appendChild(e) |> ignore\n e\n\nlet private dotSheet styleElem : CSSStyleSheet = Interop.get styleElem \"sheet\"\n\nlet private getSutilStylesheet (doc : Document) = getSutilStyleElement doc |> dotSheet\n\nlet private nextRuleId = Helpers.makeIdGenerator()\n\nlet private toEmptyStr s = if System.String.IsNullOrEmpty(s) then \"\" else s\n\nlet createRule (node : HTMLElement) (a:float) (b:float) tr (uid:int) =\n registerDoc (documentOf node)\n\n let css = match tr.CssGen with\n | Some f -> f\n | None -> failwith \"No CSS function supplied\"\n\n if (tr.DurationFn.IsSome) then\n failwith \"Duration function not permitted in createRule\"\n\n let durn = tr.Duration |> overrideDuration\n\n let step = 16.666 / durn\n let mutable keyframes = [ \"{\\n\" ];\n\n for p in [0.0 ..step.. 1.0] do\n let t = a + (b - a) * tr.Ease(p)\n keyframes <- keyframes @ [ sprintf \"%f%%{%s}\\n\" (p * 100.0) (css t (1.0 - t)) ]\n\n let rule = keyframes @ [ sprintf \"100%% {%s}\\n\" (css b (1.0 - b)) ] |> String.concat \"\"\n\n let name = sprintf \"__sutil_%d\" (if uid = 0 then nextRuleId() else uid)\n let keyframeText = sprintf \"@keyframes %s %s\" name rule\n if logEnabled() then log <| sprintf \"keyframe: %s\" (keyframes |> List.skip (keyframes.Length / 2) |> List.head)\n if logEnabled() then log($\"createRule {name} {durn}ms for {nodeStr node}\")\n\n let stylesheet = getSutilStylesheet (documentOf node)\n stylesheet.insertRule( keyframeText, stylesheet.cssRules.length) |> ignore\n\n let animations =\n if String.IsNullOrEmpty(node.style.animation) then [] else [ node.style.animation ]\n @ [ sprintf \"%s %fms linear %fms 1 both\" name durn tr.Delay ]\n\n node.style.animation <- animations |> String.concat \", \"\n numActiveAnimations <- numActiveAnimations + 1\n name\n\nlet clearAnimations (node:HTMLElement) = node.style.animation <-\"\"\n\nlet private clearRules() =\n Window.requestAnimationFrame( fun _ ->\n if (numActiveAnimations = 0) then\n for kv in activeDocs do\n let doc = kv.Value\n let stylesheet = getSutilStylesheet doc\n if logEnabled() then log <| sprintf \"clearing %d rules\" (int stylesheet.cssRules.length)\n for i in [(int stylesheet.cssRules.length-1) .. -1 .. 0] do\n stylesheet.deleteRule( float i )\n //doc.__svelte_rules = {};\n activeDocs <- Map.empty\n ) |> ignore\n\nlet private deleteRule (node:HTMLElement) (name:string) =\n let previous = (toEmptyStr node.style.animation).Split( ',' )\n let next =\n previous |> Array.filter\n (if System.String.IsNullOrEmpty(name)\n then (fun anim -> anim.IndexOf(name) < 0) // remove specific animation\n else (fun anim -> anim.IndexOf(\"__sutil\") < 0)) // remove all Svelte animations\n let deleted = previous.Length - next.Length\n if (deleted > 0) then\n //log <| sprintf \"Deleted rule(s) %s (%d removed)\" name deleted\n node.style.animation <- next |> Array.map (fun s -> s.Trim()) |> String.concat \", \"\n numActiveAnimations <- numActiveAnimations - deleted\n if (numActiveAnimations = 0) then clearRules()\n\nlet private rectToStr (c : ClientRect ) =\n sprintf \"[%f,%f -> %f,%f]\" c.left c.top c.right c.bottom\n\nlet flip (node:Element) (animation:Animation) props =\n let tr = applyProps props {\n Transition.Default with\n Delay = 0.0\n DurationFn = Some (fun d -> System.Math.Sqrt(d) * 60.0)\n Ease = Easing.quintOut }\n let style = Window.getComputedStyle(node)\n let transform = if style.transform = \"none\" then \"\" else style.transform\n let scaleX = animation.From.width / node.clientWidth\n let scaleY = animation.From.height / node.clientHeight\n let dx = (animation.From.left - animation.To.left) / scaleX\n let dy = (animation.From.top - animation.To.top) / scaleY\n let d = Math.Sqrt(dx * dx + dy * dy)\n if logEnabled() then log( sprintf \"flip: %A,%A %A %A -> %A\" dx dy transform (rectToStr animation.From) (rectToStr animation.To))\n {\n tr with\n Duration = match tr.DurationFn with\n | None -> tr.Duration //\n | Some f -> f(d) // Use user's function or our default\n DurationFn = None // Original converts any function into a scalar value\n CssGen = Some (fun t u -> sprintf \"transform: %s translate(%fpx, %fpx);`\" transform (u * dx) (u * dy))\n }\n\nlet createAnimation (node:HTMLElement) (from:ClientRect) (animateFn : Element -> Animation -> TransitionProp list -> Transition) props =\n //if (!from)\n // return noop;\n let tgt (* to *) = node.getBoundingClientRect()\n\n let shouldCreate =\n not (isNull from) &&\n not (from.width = 0) &&\n not (from.height = 0) &&\n not (from.left = tgt.left && from.right = tgt.right && from.top = tgt.top && from.bottom = tgt.bottom)\n // return noop;\n\n //let { delay = 0, duration = 300, easing = identity,\n // start: start_time = exports.now() + delay,\n // end = start_time + duration, tick = noop, css } = fn(node, { From = from; To = tgt }, props);\n\n // TODO : Tick loop\n\n if (shouldCreate) then\n let a = animateFn node { From = from; To = tgt } props\n let r = { a with Duration = if (a.Duration = 0.0 && a.DurationFn.IsNone) then 300.0 else a.Duration }\n createRule node 0.0 1.0 r 0\n else\n \"\"\n\nlet private waitAnimationEnd (el : HTMLElement) (f : unit -> unit) =\n let rec cb _ =\n el.removeEventListener(\"animationend\",cb)\n f()\n el.addEventListener(\"animationend\", cb)\n\nlet animateNode (node : HTMLElement) from =\n waitAnimationFrame <| fun () ->\n let name = createAnimation node from flip []\n waitAnimationEnd node <| fun _ ->\n deleteRule node name\n\nlet private tickGen = Helpers.makeIdGenerator()\n\nlet private findTransition (intro:bool) (trans : TransitionAttribute list) : TransitionBuilder option =\n let mutable result : TransitionBuilder option = None\n for x in trans do\n result <- match result, x, intro with\n | Some _, _, _ -> result\n | None, In x, true -> Some x\n | None, Out x, false -> Some x\n | None, InOut x, _ -> Some x\n | _ -> None\n result\n\nlet transitionNode (el : HTMLElement)\n (trans : TransitionAttribute list)\n (initProps : TransitionProp list) // Likely to just be Key, if anything\n (isVisible : bool)\n (start: HTMLElement -> unit)\n (complete: HTMLElement -> unit) =\n\n let mutable ruleName = \"\"\n\n let cancelTick () =\n match NodeKey.get el NodeKey.TickTask with\n | Some f ->\n NodeKey.clear el NodeKey.TickTask\n f()\n | None -> ()\n\n let runTick tr b durn =\n let logEnabled = Logging.isEnabled \"tick\"\n let log = Logging.log \"tick\"\n\n let a = if b = 0.0 then 1.0 else 0.0\n let d = b - a\n let tickId = tickGen()\n let tick = match tr.Tick with|Some f -> f|None -> failwith \"No tick function supplied\"\n let ease = tr.Ease\n let delay = tr.Delay\n\n let mutable t = a\n let mutable start = 0.0\n let mutable finish = 0.0\n let mutable started = false\n let mutable finished = false\n\n Interop.set el NodeKey.TickTask (fun () ->\n if logEnabled then log $\"#{tickId}: cancel\"\n finished <- true\n )\n\n if logEnabled then log $\"#{tickId}: run b={b} durn={durn}\"\n if (b > 0.0) then\n tick 0.0 1.0\n\n LoopTasks.loop <| fun now ->\n if not started then\n start <- now + delay\n finish <- start + durn\n if logEnabled then log $\"#{tickId}: start: start={start} finish={finish}\"\n started <- true\n\n if finished || now >= finish then\n if logEnabled then log $\"#{tickId}: finish: t={t}\"\n t <- b\n tick t (1.0 - t)\n finished <- true\n\n else if now >= start then\n let e = now - start\n let t0 = e / durn\n t <- a + d * (ease t0)\n if logEnabled then log $\"#{tickId}: tick: t={t} t0={t0} e={e}\"\n tick t (1.0 - t)\n\n not finished\n\n let hide() =\n if logEnabled() then log $\"hide {nodeStr el}\"\n showEl el false\n complete el\n if ruleName <> \"\" then deleteRule el ruleName\n CustomDispatch<_>.dispatch(el,\"outroend\")\n\n let show() =\n if logEnabled() then log $\"show {nodeStr el}\"\n showEl el true\n complete el\n if ruleName <> \"\" then deleteRule el ruleName\n CustomDispatch<_>.dispatch(el, \"introend\")\n\n let tr = findTransition isVisible trans\n\n let startTransition createTrans =\n let event = if isVisible then \"introstart\" else \"outrostart\"\n let (a,b) = if isVisible then (0.0, 1.0) else (1.0, 0.0)\n let onEnd = if isVisible then show else hide\n\n cancelTick()\n\n waitAnimationFrame <| fun () ->\n CustomDispatch<_>.dispatch(el,event)\n start el\n waitAnimationEnd el onEnd\n if (isVisible) then\n showEl el true // Check: we're doing this again at animationEnd\n let tr = createTrans()\n if tr.DurationFn.IsSome then failwith \"Duration function not permitted\"\n let d = tr.Duration\n if tr.CssGen.IsSome then\n ruleName <- createRule el a b tr 0\n if tr.Tick.IsSome then\n // Wait for the cancelled runTick to finish\n DomHelpers.wait el (fun () ->\n let t = runTick tr b d\n t.Promise)\n\n // Save the value of display for use by showEl\n let _display = el.style.display\n if not (isNull _display) && _display <> \"\" && _display <> \"none\" then\n Interop.set el \"_display\" _display\n\n match tr with\n | None ->\n showEl el isVisible\n complete el\n | Some init ->\n deleteRule el \"\"\n let createTrans = (init initProps) el\n startTransition createTrans\n\ntype Hideable = {\n predicate : IObservable\n element : SutilElement\n transOpt : TransitionAttribute list\n}\n\ntype HideableRuntime = {\n hideable : Hideable\n mutable target : SutilEffect\n mutable cache : bool\n mutable unsubscribe : System.IDisposable\n}\n\nlet createHideableRuntime h =\n {\n hideable = h\n target = SideEffect\n cache = false\n unsubscribe = null\n }\n\n\nlet collectNodes (sn : SutilEffect option) = sn |> Option.map (fun n -> n.collectDomNodes()) |> Option.defaultValue []\n\nlet transitionList (list : Hideable list) : SutilElement =\n SutilElement.Define( \"transitionList\",\n fun ctx ->\n let runtimes = list |> List.map createHideableRuntime\n for rt in runtimes do\n rt.unsubscribe <- rt.hideable.predicate |> Store.subscribe ( fun show ->\n if (rt.target.IsEmpty) then\n rt.target <- build rt.hideable.element ctx\n rt.cache <- not show\n\n if (rt.cache <> show) then\n rt.cache <- show\n rt.target.collectDomNodes() |> List.iter (fun node ->\n transitionNode (node :?> HTMLElement) rt.hideable.transOpt [] show ignore ignore )\n )\n () )\n\ntype MatchOption<'T> = ('T -> bool) * SutilElement * TransitionAttribute list\n\nlet makeHideable guard element transOpt = {\n element = element\n transOpt = transOpt\n predicate = guard\n}\n\nlet transitionMatch<'T> (store : IObservable<'T>) (options : MatchOption<'T> list) =\n options |> List.map (fun (p,e,t) -> makeHideable (store |> Store.map p) e t) |> transitionList\n\nlet transitionOpt (trans : TransitionAttribute list)\n (store : IObservable)\n (element: SutilElement)\n (elseElement : SutilElement option) : SutilElement =\n SutilElement.Define(\"transitionOpt\",\n fun ctx ->\n let transResult = SutilEffect.MakeGroup( \"transition\", ctx.Parent, ctx.Previous ) |> Group\n ctx.AddChild transResult\n let transCtx = ctx |> ContextHelpers.withParent transResult\n\n let mutable target : SutilEffect = SideEffect\n let mutable cache = false\n let mutable targetElse : SutilEffect = SideEffect\n\n let unsub = store |> Store.subscribe (fun isVisible ->\n let wantTransition = not target.IsEmpty\n\n if target.IsEmpty then\n target <- build element transCtx\n cache <- not isVisible\n match elseElement with\n | Some e -> targetElse <- build e transCtx\n | None -> ()\n\n if cache <> isVisible then\n cache <- isVisible\n let trans' = if wantTransition then trans else []\n\n target.collectDomNodes() |> List.iter (fun node ->\n transitionNode (node :?> HTMLElement) trans' [] isVisible ignore ignore\n )\n targetElse.collectDomNodes() |> List.iter (fun node ->\n transitionNode (node :?> HTMLElement) trans' [] (not isVisible) ignore ignore\n )\n //if not (isNull targetElse) then transitionNode (targetElse :?> HTMLElement) trans' [] (not isVisible) ignore ignore\n )\n\n transResult )\n\n/// Show or hide according to an IObservable<bool> using a transition\nlet transition (options : TransitionAttribute list) visibility element =\n transitionOpt options visibility element None\n\n/// Alternate between a pair of elements according to an IObservable<bool> with no transition\nlet transitionElse(options : TransitionAttribute list) visibility element otherElement=\n transitionOpt options visibility element (Some otherElement)\n\n/// Show or hide according to an IObservable<bool> with no transition\nlet showIf visibility element =\n transitionOpt [] visibility element None\n\n/// Alternate between a pair of elements according to an IObservable<bool> with no transition\nlet showIfElse visibility element otherElement=\n transitionOpt [] visibility element (Some otherElement)\n\n\n\n","///\nmodule internal Sutil.Bindings\n\nopen Transition\nopen Core\nopen DomHelpers\nopen Browser.Types\nopen System\nopen Fable.Core\nopen CoreElements\n\nlet private logEnabled() = Logging.isEnabled \"bind\"\nlet private log s = Logging.log \"bind\" s\nlet private bindId = Helpers.makeIdGenerator()\n\n// Binding helper\nlet bindSub<'T> (source : IObservable<'T>) (handler : BuildContext -> 'T -> unit) =\n SutilElement.Define( \"bindSub\",\n fun ctx ->\n let unsub = source.Subscribe( handler ctx )\n SutilEffect.RegisterDisposable(ctx.Parent,unsub)\n () )\n\nlet elementFromException (x : exn) =\n el \"div\" [\n attr (\"style\",\"color: #FF8888;\")\n attr (\"title\", \"See console for details\")\n text (\"sutil: exception in bind: \" + x.Message)\n ]\n\nlet bindElementC<'T> (store : IObservable<'T>) (element: 'T -> SutilElement) (compare : 'T -> 'T -> bool)=\n SutilElement.Define( \"bindElementC\",\n fun ctx ->\n let mutable node = SideEffect\n let group = SutilEffect.MakeGroup(\"bindc\",ctx.Parent,ctx.Previous)\n let bindNode = Group group\n\n if logEnabled() then log($\"bind: {group.Id} ctx={ctx.Action} prev={ctx.Previous}\")\n ctx.AddChild bindNode\n\n let run() =\n let bindCtx = { ctx with Parent = bindNode }\n let disposable = store |> Observable.distinctUntilChangedCompare compare |> Store.subscribe (fun next ->\n try\n if logEnabled() then log($\"bind: rebuild {group.Id} with {next}\")\n node <- build (element(next)) (bindCtx |> ContextHelpers.withReplace (node,group.NextDomNode))\n with\n | x ->\n //Logging.error $\"Exception in bindo: {x.StackTrace}: parent={ctx.Parent} node={node.ToString()}\"\n JS.console.error(x)\n node <- build (elementFromException x) (bindCtx |> ContextHelpers.withReplace (node,group.NextDomNode))\n\n )\n group.RegisterUnsubscribe ( fun () ->\n if logEnabled() then log($\"dispose: Bind.el: {group}\")\n node.Dispose()\n disposable.Dispose())\n\n run()\n\n bindNode )\n\nlet bindElementCO<'T> (store : IObservable<'T>) (element: IObservable<'T> -> SutilElement) (compare : 'T -> 'T -> bool)=\n SutilElement.Define( \"bindElementCO\",\n fun ctx ->\n let mutable node = SideEffect\n let group = SutilEffect.MakeGroup(\"bindco\",ctx.Parent,ctx.Previous)\n let bindNode = Group group\n\n if logEnabled() then log($\"bind: {group.Id} ctx={ctx.Action} prev={ctx.Previous}\")\n ctx.AddChild bindNode\n\n let run() =\n let bindCtx = { ctx with Parent = bindNode }\n let disposable = store |> Observable.distinctUntilChangedCompare compare |> Store.subscribe (fun next ->\n try\n if logEnabled() then log($\"bind: rebuild {group.Id} with {next}\")\n node <- build (element(store)) (bindCtx |> ContextHelpers.withReplace (node,group.NextDomNode))\n with\n | x ->\n JS.console.error(x)\n node <- build (elementFromException x) (bindCtx |> ContextHelpers.withReplace (node,group.NextDomNode))\n )\n group.RegisterUnsubscribe ( fun () ->\n if logEnabled() then log($\"dispose: Bind.el: {group}\")\n node.Dispose()\n disposable.Dispose())\n\n\n run()\n\n bindNode )\n\nlet bindElement<'T> (store : IObservable<'T>) (element: 'T -> SutilElement) : SutilElement=\n bindElementCO store (Store.current >> element) (fun _ _-> false)\n\n/// Backwards compatibility\nlet bindFragment = bindElement\n\nlet bindElement2<'A,'B> (a : IObservable<'A>) (b : IObservable<'B>) (element: ('A*'B) -> SutilElement) =\n SutilElement.Define(\"bindElement2\",\n fun ctx ->\n let mutable node : SutilEffect = SideEffect\n let group = SutilEffect.MakeGroup(\"bind2\",ctx.Parent,ctx.Previous)\n let bindNode = Group group\n ctx.AddChild bindNode\n\n let bindCtx = { ctx with Parent = bindNode }\n\n let d = Store.subscribe2 a b (fun next ->\n try\n node <- build (element(next)) (bindCtx |> ContextHelpers.withReplace (node,group.NextDomNode))\n with\n | x -> Logging.error $\"Exception in bind: {x.Message}\"\n )\n\n group.RegisterUnsubscribe (Helpers.unsubify d)\n\n bindNode\n )\n\nlet bindElementKO<'T,'K when 'K : equality> (store : IObservable<'T>) (element: IObservable<'T> -> SutilElement) (key : 'T -> 'K) : SutilElement =\n let compare a b = key a = key b\n bindElementCO store element compare\n\nlet bindElementK<'T,'K when 'K : equality> (store : IObservable<'T>) (element: 'T -> SutilElement) (key : 'T -> 'K) : SutilElement =\n let compare a b = key a = key b\n bindElementC store element compare\n\nlet bindPromiseStore<'T> (p : ObservablePromise<'T>)\n (waiting : SutilElement)\n (result: 'T -> SutilElement)\n (fail : Exception -> SutilElement)\n : SutilElement =\n bindElement p <| (function\n | PromiseState.Waiting -> waiting\n | PromiseState.Result r -> result r\n | PromiseState.Error x -> fail x)\n\nlet bindPromise<'T> (p : JS.Promise<'T>)\n (waiting : SutilElement)\n (result: 'T -> SutilElement)\n (fail : Exception -> SutilElement)\n : SutilElement =\n let x = ObservablePromise<'T>(p)\n //x.Run p\n bindPromiseStore x waiting result fail\n\ntype BindFn<'T> = IObservable<'T> -> ('T -> SutilElement) -> SutilElement\nlet private getInputChecked el = Interop.get el \"checked\"\nlet private setInputChecked (el : Node) (v:obj) = Interop.set el \"checked\" v\nlet private getInputValue el : string = Interop.get el \"value\"\nlet private setInputValue el (v:string) = Interop.set el \"value\" v\n\nlet bindSelected<'T when 'T : equality> (selection:IObservable>) (dispatch : List<'T> -> unit) : SutilElement =\n SutilElement.Define(\"bindSelected\",\n fun ctx ->\n\n let selectElement = ctx.ParentElement :?> HTMLSelectElement\n let selOps = selectElement.selectedOptions\n let op (coll:HTMLCollection) i = coll.[i] :?> HTMLOptionElement\n let opValue op : 'T = Interop.get op \"__value\"\n\n let getValueList() =\n [0..selOps.length-1] |> List.map (fun i -> opValue (op selOps i))\n\n let updateSelected (v : List<'T>) =\n let ops = selectElement.options\n for i in [0..ops.length-1] do\n let o = op ops i\n o.selected <- v |> List.contains (opValue o)\n\n let unsubInput = listen \"input\" selectElement <| fun _ ->\n getValueList() |> dispatch\n\n // We need to finalize checked status after all attrs have been processed for input,\n // in case 'value' hasn't been set yet\n once Event.ElementReady selectElement <| fun _ ->\n let unsub = selection |> Store.subscribe (updateSelected)\n SutilEffect.RegisterDisposable(ctx.Parent, unsub)\n\n SutilEffect.RegisterUnsubscribe(ctx.Parent,unsubInput)\n ()\n )\n\nlet bindSelectMultiple<'T when 'T : equality> (store:IStore>) : SutilElement =\n bindSelected store (fun sln -> store <~ sln)\n\nlet bindSelectSingle<'T when 'T : equality> (store:IStore<'T>) : SutilElement =\n bindSelected (store .> List.singleton) (fun sln -> sln |> List.exactlyOne |> Store.set store)\n\nlet bindSelectOptional<'T when 'T : equality> (store:IStore<'T option>) : SutilElement =\n let toList topt = match topt with |None -> []|Some t -> List.singleton t\n let fromList list = match list with |[] -> None |x::_ -> Some x\n bindSelected (store .> toList) (fun sln -> sln |> fromList |> Store.set store)\n\nlet private isNullString (obj:obj) =\n isNull obj || System.String.IsNullOrEmpty(downcast obj)\n\nlet private getId (s : IStore<'T>) = s.GetHashCode()\n\nlet bindGroup<'T> (store:Store>) : SutilElement =\n SutilElement.Define( \"bindGroup\",\n fun ctx ->\n let parent = ctx.ParentNode\n let name = match Interop.get parent \"name\" with\n | s when isNullString s -> $\"store-{getId store}\"\n | s -> s\n\n // Group this input with all other inputs that reference the same store\n Interop.set parent \"name\" name\n\n let getValueList() =\n let inputs = (documentOf parent).querySelectorAll(@$\"input[name=\"\"{name}\"\"]\")\n [0..(inputs.length-1)] |> List.map (fun i -> inputs.[i]) |> List.filter getInputChecked |> List.map getInputValue\n\n let updateChecked (v : List) =\n setInputChecked parent ( v |> List.contains (getInputValue parent) )\n\n // Update the store when the radio box is clicked on\n let unsubInput = DomHelpers.listen \"input\" parent <| fun _ ->\n getValueList() |> Store.set store\n\n // We need to finalize checked status after all attrs have been processed for input,\n // in case 'value' hasn't been set yet\n once Event.ElementReady parent <| fun _ ->\n store |> Store.get |> updateChecked\n\n // When store changes make sure check status is synced\n let unsub = store |> Store.subscribe (updateChecked)\n\n SutilEffect.RegisterDisposable(ctx.Parent,unsub)\n SutilEffect.RegisterUnsubscribe(ctx.Parent,unsubInput)\n () )\n\n// T can realistically only be numeric or a string. We're relying (I think!) on JS's ability\n// to turn a string into an int automatically in the Store.set call (maybe it's Fable doing that)\n//\nlet bindRadioGroup<'T> (store:Store<'T>) : SutilElement =\n SutilElement.Define( \"bindRadioGroup\",\n fun ctx ->\n let parent = ctx.ParentNode\n let name = match Interop.get parent \"name\" with\n | s when isNullString s -> $\"store-{getId store}\"\n | s -> s\n // Group this input with all other inputs that reference the same store\n Interop.set parent \"name\" name\n\n let updateChecked (v : obj) =\n setInputChecked parent ( (string v) = getInputValue parent )\n\n // Update the store when the radio box is clicked on\n let inputUnsub = listen \"input\" parent <| fun _ ->\n Interop.get parent \"value\" |> Store.set store\n\n // We need to finalize checked status after all attrs have been processed for input,\n // in case 'value' hasn't been set yet\n once Event.ElementReady parent <| fun _ ->\n store |> Store.get |> updateChecked\n\n // When store changes make sure check status is synced\n let unsub = store |> Store.subscribe updateChecked\n\n SutilEffect.RegisterDisposable(ctx.Parent,unsub)\n SutilEffect.RegisterUnsubscribe(ctx.Parent,inputUnsub)\n\n () )\n\nlet bindClassToggle (toggle:IObservable) (classesWhenTrue:string) (classesWhenFalse:string) =\n bindSub toggle <| fun ctx active ->\n if active then\n ctx.ParentElement |> ClassHelpers.removeFromClasslist classesWhenFalse\n ctx.ParentElement |> ClassHelpers.addToClasslist classesWhenTrue\n else\n ctx.ParentElement |> ClassHelpers.removeFromClasslist classesWhenTrue\n ctx.ParentElement |> ClassHelpers.addToClasslist classesWhenFalse\n\n// Deprecated\nlet bindClass (toggle:IObservable) (classes:string) = bindClassToggle toggle classes \"\"\n\nlet bindClassNames (classNames:IObservable<#seq>) =\n bindSub classNames <| fun ctx current ->\n ctx.ParentElement.className <- \"\"\n ctx.ParentElement.classList.add( current |> Array.ofSeq )\n\nlet bindClassName (classNames:IObservable) =\n bindSub classNames <| fun ctx current ->\n ctx.ParentElement.className <- current\n\n/// Bind a store value to an element attribute. Updates to the element are unhandled\nlet bindAttrIn<'T> (attrName:string) (store : IObservable<'T>) : SutilElement =\n SutilElement.Define(\"bindAttrIn\",\n fun ctx ->\n let unsub =\n if attrName = \"class\" then\n store |> Store.subscribe (fun cls -> ctx.ParentElement.className <- (string cls))\n else\n store |> Store.subscribe (DomHelpers.setAttribute ctx.ParentElement attrName)\n SutilEffect.RegisterDisposable(ctx.Parent,unsub)\n () )\n\nlet bindAttrOut<'T> (attrName:string) (onchange : 'T -> unit) : SutilElement =\n SutilElement.Define( \"bindAttrOut\",\n fun ctx ->\n let parent = ctx.ParentNode\n let unsubInput = listen \"input\" parent <| fun _ ->\n Interop.get parent attrName |> onchange\n SutilEffect.RegisterUnsubscribe(ctx.Parent,unsubInput)\n () )\n\n// Bind a scalar value to an element attribute. Listen for onchange events and dispatch the\n// attribute's current value to the given function. This form is useful for view templates\n// where v is invariant (for example, an each that already filters on the value of v, like Todo.Done)\nlet attrNotify<'T> (attrName:string) (value :'T) (onchange : 'T -> unit) : SutilElement =\n SutilElement.Define( \"attrNotify\",\n fun ctx ->\n let parent = ctx.ParentNode\n let unsubInput = listen \"input\" parent <| fun _ ->\n Interop.get parent attrName |> onchange\n Interop.set parent attrName value\n SutilEffect.RegisterUnsubscribe(ctx.Parent, unsubInput)\n () )\n\n// Bind an observable value to an element attribute. Listen for onchange events and dispatch the\n// attribute's current value to the given function\nlet bindAttrBoth<'T> (attrName:string) (value : IObservable<'T>) (onchange : 'T -> unit) : SutilElement =\n fragment [\n bindAttrIn attrName value\n bindAttrOut attrName onchange\n ]\n\nlet bindListen<'T> (attrName:string) (store : IObservable<'T>) (event:string) (handler : Event -> unit) : SutilElement =\n SutilElement.Define( \"bindListen\",\n fun ctx ->\n let parent = ctx.ParentNode\n let unsubA = DomHelpers.listen event parent handler\n let unsubB = store |> Store.subscribe ( Interop.set parent attrName )\n SutilEffect.RegisterUnsubscribe(ctx.Parent,unsubA)\n SutilEffect.RegisterDisposable(ctx.Parent,unsubB)\n () )\n\n// Bind a store value to an element attribute. Listen for onchange events write the converted\n// value back to the store\nlet private bindAttrConvert<'T> (attrName:string) (store : Store<'T>) (convert : obj -> 'T) : SutilElement =\n SutilElement.Define( \"bindAttrConvert\",\n fun ctx ->\n let parent = ctx.ParentNode\n //let attrName' = if attrName = \"value\" then \"__value\" else attrName\n let unsubInput = DomHelpers.listen \"input\" parent <| fun _ ->\n Interop.get parent attrName |> convert |> Store.set store\n let unsub = store |> Store.subscribe ( Interop.set parent attrName )\n SutilEffect.RegisterUnsubscribe(parent,unsubInput)\n SutilEffect.RegisterDisposable(parent,unsub)\n () )\n\n// Unsure how to safely convert Element.getAttribute():string to 'T\nlet private convertObj<'T> (v:obj) : 'T =\n v :?> 'T\n\n// Bind a store to an attribute in both directions\nlet bindAttrStoreBoth<'T> (attrName:string) (store : Store<'T>) =\n bindAttrConvert attrName store convertObj<'T>\n\nlet bindAttrStoreOut<'T> (attrName:string) (store : Store<'T>) : SutilElement =\n SutilElement.Define( \"bindAttrStoreOut\",\n fun ctx ->\n let parent = ctx.ParentNode\n let unsubInput = DomHelpers.listen \"input\" parent <| fun _ ->\n Interop.get parent attrName |> convertObj<'T> |> Store.set store\n //(asEl parent).addEventListener(\"input\", (fun _ -> Interop.get parent attrName |> convertObj<'T> |> Store.set store ))\n SutilEffect.RegisterUnsubscribe(ctx.Parent,unsubInput)\n ()\n )\n\nlet private attrIsSizeRelated (attrName:string) =\n let upr = attrName.ToUpper()\n upr.IndexOf(\"WIDTH\") >= 0 || upr.IndexOf(\"HEIGHT\") >= 0\n\nlet listenToProp<'T> (attrName:string) (dispatch: 'T -> unit) : SutilElement =\n SutilElement.Define( sprintf \"listenToProp %s\" attrName,\n fun ctx ->\n let parent = ctx.ParentNode\n let notify() = Interop.get parent attrName |> convertObj<'T> |> dispatch\n\n once Event.ElementReady parent <| fun _ ->\n if attrIsSizeRelated attrName then\n SutilEffect.RegisterDisposable(parent,(ResizeObserver.getResizer (downcast parent)).Subscribe( notify ))\n else\n SutilEffect.RegisterUnsubscribe(parent, DomHelpers.listen \"input\" parent (fun _ -> notify()))\n\n rafu notify\n () )\n\nlet bindPropOut<'T> (attrName:string) (store : Store<'T>) : SutilElement =\n listenToProp attrName (Store.set store)\n\ntype KeyedStoreItem<'T,'K> = {\n Key : 'K\n //CachedElement : HTMLElement\n Node : SutilEffect\n SvId : int\n Position : IStore\n Value: IStore<'T>\n Rect: ClientRect\n}\n\nlet private findCurrentNode doc (current:Node) (id:int) =\n if (isNull current || isNull current.parentNode) then\n if logEnabled() then log($\"each: Find node with id {id}\")\n match DomHelpers.findNodeWithSvId doc id with\n | None ->\n if logEnabled() then log(\"each: Disaster: cannot find node\")\n null\n | Some n ->\n if logEnabled() then log($\"each: Found it: {n}\")\n n\n else\n //log($\"Cannot find node with id {id}\")\n current\n\nlet private findCurrentElement doc (current:Node) (id:int) =\n let node = findCurrentNode doc current id\n match node with\n | null -> null\n | n when isElementNode n -> n :?> HTMLElement\n | x -> if logEnabled() then log $\"each: Disaster: found node but it's not an HTMLElement\"\n null\n\nlet private genEachId = Helpers.makeIdGenerator()\n\n\nlet private asDomNode (element: SutilEffect) (ctx: BuildContext) : Node =\n //let result = (ctx |> build element)\n match element.collectDomNodes () with\n | [ n ] -> n\n | [] -> errorNode ctx.Parent $\"Error: Empty node from {element} #{element.Id}\"\n | xs ->\n let doc = ctx.Document\n let tmpDiv = doc.createElement (\"div\")\n\n let en =\n errorNode (DomNode tmpDiv) \"'fragment' not allowed as root for 'each' blocks\"\n\n DomEdit.appendChild tmpDiv en\n ctx.Parent.AppendChild tmpDiv\n\n xs\n |> List.iter (fun x -> DomEdit.appendChild tmpDiv x)\n\n upcast tmpDiv\n\nlet private asDomElement (element: SutilEffect) (ctx: BuildContext) : HTMLElement =\n let node = asDomNode element ctx\n\n if isElementNode node then\n downcast node\n else\n let doc = ctx.Document\n let span = doc.createElement (\"span\")\n DomEdit.appendChild span node\n ctx.Parent.AppendChild span\n span\n\nlet eachiko_wrapper (items:IObservable>) (view : IObservable * IObservable<'T> -> SutilElement) (key:int*'T->'K) (trans : TransitionAttribute list) : SutilElement =\n let log s = Logging.log \"each\" s\n\n SutilElement.Define(\"eachiko_wrapper\",\n fun ctx ->\n log($\"eachiko: Previous = {ctx.Previous}\")\n let eachGroup = SutilEffect.MakeGroup(\"each\",ctx.Parent,ctx.Previous)\n let eachNode = Group eachGroup\n ctx.AddChild eachNode\n\n let mutable state = ([| |] : KeyedStoreItem<'T,'K> array) .ToCollectionWrapper()\n let eachId = genEachId() + 1\n let idKey = \"svEachId\"\n let hasEid (n : Node) = Interop.exists n idKey\n let eachIdOf n : int = if hasEid n then Interop.get n idKey else -1\n let setEid n = Interop.set n idKey eachId\n let eachCtx = ctx |> ContextHelpers.withParent eachNode\n\n#if LOGGING_ENABLED\n let logState state' =\n Browser.Dom.console.groupCollapsed(\"each state #\" + eachGroup.Id)\n state' |> List.map (fun s -> sprintf \"%s %f,%f\" (string s.Key) s.Rect.left s.Rect.top) |> List.iter (fun s -> log(s))\n Browser.Dom.console.groupEnd()\n\n let logItems (items : list<'T>) =\n Browser.Dom.console.groupCollapsed(\"each items #\" + eachGroup.Id)\n items |> List.mapi (fun i s -> sprintf \"%s\" (string (key(i,s)))) |> List.iter (fun s -> log(s))\n Browser.Dom.console.groupEnd()\n#endif\n\n let unsub = items |> Store.subscribe (fun newItems ->\n let wantAnimate = true\n\n if Logging.isEnabled \"each\" then\n log(\"-- Each Block Render -------------------------------------\")\n log($\"caching rects for render. Previous: {state |> CollectionWrapper.length} items. Current {newItems |> CollectionWrapper.length} items\")\n\n state <- state |> CollectionWrapper.map (fun ki ->\n let el = findCurrentElement ctx.Document (*ki.Element*)null ki.SvId\n { ki with (*Element = el; *)Rect = el.getBoundingClientRect() })\n\n //logItems newItems\n //logState state\n\n // Last child that doesn't have our eachId\n if Logging.isEnabled \"each\" then log($\"Previous = {ctx.Previous}\")\n //let prevNodeInit : Node = vnode.PrevDomNode\n let mutable prevNode = SideEffect\n\n let newState = newItems |> CollectionWrapper.mapi (fun itemIndex item ->\n let itemKey = key(itemIndex,item)\n let optKi = state |> CollectionWrapper.tryFind (fun x -> x.Key = itemKey)\n match optKi with\n | None ->\n let storePos = Store.make itemIndex\n let storeVal = Store.make item\n let ctx2 = eachCtx |> ContextHelpers.withPrevious prevNode\n if Logging.isEnabled \"each\" then log $\"++ creating new item '{item}' (key={itemKey}) with prev='{prevNode}' action={ctx2.Action}\"\n let sutilNode = ctx2 |> build (view (storePos,storeVal))\n let itemNode = ctx2 |> asDomElement sutilNode\n if Logging.isEnabled \"each\" then log $\"-- created #{svId itemNode} with prev='{nodeStrShort (itemNode.previousSibling)}'\"\n setEid itemNode\n SutilEffect.RegisterDisposable(sutilNode,storePos)\n SutilEffect.RegisterDisposable(sutilNode,storeVal)\n transitionNode itemNode trans [Key (string itemKey)] true ignore ignore\n let newKi = {\n SvId = svId itemNode\n Key = itemKey\n Node = sutilNode\n //CachedElement = itemNode\n Position = storePos\n Rect = itemNode.getBoundingClientRect()\n Value = storeVal\n }\n\n let prevEl = itemNode.previousSibling :?> HTMLElement\n if Logging.isEnabled \"each\" then log $\"new item #{newKi.SvId} eid={eachIdOf itemNode} {itemKey} {rectStr newKi.Rect} prevNode={prevNode} prevSibling={nodeStr prevEl}\"\n prevNode <- sutilNode\n newKi\n | Some ki ->\n ki.Position |> Store.modify (fun _ -> itemIndex)\n ki.Value |> Store.modify (fun _ -> item)\n let el = findCurrentElement ctx.Document null ki.SvId (*ki.Element*)\n if Logging.isEnabled \"each\" then log $\"existing item {ki.SvId} {ki.Key} {rectStr ki.Rect}\"\n if wantAnimate then\n clearAnimations el\n animateNode el (ki.Rect)\n prevNode <- ki.Node\n ki\n )\n\n //logState newState\n\n if Logging.isEnabled \"each\" then log(\"Remove old items\")\n // Remove old items\n for oldItem in state do\n if not (newState |> CollectionWrapper.exists (fun x -> x.Key = oldItem.Key)) then\n if Logging.isEnabled \"each\" then log($\"removing key {oldItem.Key}\")\n let el = findCurrentElement ctx.Document null oldItem.SvId (*oldItem.Element*)\n fixPosition el\n //ctx.Parent.RemoveChild(el) |> ignore\n ctx.Parent.InsertBefore(el,null) |> ignore\n //oldItem.Node.Dispose()\n transitionNode el trans [Key (string oldItem.Key)] false\n ignore (fun e -> eachGroup.RemoveChild(oldItem.Node))\n\n //ctx.Parent.PrettyPrint(\"each #\" + vnode.Id + \": before reorder\")\n\n // Reorder\n let mutable prevDomNode = eachGroup.PrevDomNode\n for ki in newState do\n if Logging.isEnabled \"each\" then log($\"Checking order: #{ki.SvId}\")\n let el = findCurrentElement ctx.Document null ki.SvId (*ki.Element*)\n if not (isNull el) then\n if not(isSameNode prevDomNode el.previousSibling) then\n if Logging.isEnabled \"each\" then log($\"reordering: ki={nodeStr el} prevNode={nodeStr prevDomNode}\")\n if Logging.isEnabled \"each\" then log($\"reordering key {ki.Key} {nodeStrShort el} parent={el.parentNode}\")\n //ctx.Parent.RemoveChild(el) |> ignore\n ctx.Parent.InsertAfter(el, prevDomNode)\n prevDomNode <- el\n\n //ctx.Parent.PrettyPrint(\"each #\" + vnode.Id + \": after reorder\")\n\n state <- newState\n )\n\n eachGroup.RegisterUnsubscribe (Helpers.unsubify unsub)\n eachNode\n )\n\nlet private duc = Observable.distinctUntilChanged\n\nlet eachiko = eachiko_wrapper\n\nlet each (items:IObservable>) (view : 'T -> SutilElement) (trans : TransitionAttribute list) =\n eachiko_wrapper items (fun (_,item) -> bindElement (duc item) view) (fun (i,v) -> i,v.GetHashCode()) trans\n\nlet eachi (items:IObservable>) (view : (int*'T) -> SutilElement) (trans : TransitionAttribute list) : SutilElement =\n eachiko items (fun (index,item) -> bindElement2 (duc index) (duc item) view) fst trans\n\nlet eachio (items:IObservable>) (view : (IObservable*IObservable<'T>) -> SutilElement) (trans : TransitionAttribute list) =\n eachiko items view fst trans\n\nlet eachk (items:IObservable>) (view : 'T -> SutilElement) (key:'T -> 'K) (trans : TransitionAttribute list) =\n eachiko\n items\n (fun (_,item) -> bindElement (duc item) view)\n (snd>>key)\n trans\n\n#if false\nlet each_seq (items:IObservable>) (view : 'T -> SutilElement) (trans : TransitionAttribute list) =\n eachiko_seq items (fun (_,item) -> bindElement (duc item) view) (fun (i,v) -> i,v.GetHashCode()) trans\n\nlet eachi_seq (items:IObservable>) (view : (int*'T) -> SutilElement) (trans : TransitionAttribute list) : SutilElement =\n eachiko items (fun (index,item) -> bindElement2 (duc index) (duc item) view) fst trans\n\nlet eachio_seq (items:IObservable>) (view : (IObservable*IObservable<'T>) -> SutilElement) (trans : TransitionAttribute list) =\n eachiko_seq items view fst trans\n\nlet eachk_seq (items:IObservable>) (view : 'T -> SutilElement) (key:'T -> 'K) (trans : TransitionAttribute list) =\n eachiko_seq\n items\n (fun (_,item) -> bindElement (duc item) view)\n (snd>>key)\n trans\n#endif\n\nlet bindStore<'T> (init:'T) (app:Store<'T> -> Core.SutilElement) : Core.SutilElement =\n SutilElement.Define( \"bindStore\",\n fun ctx ->\n let s = Store.make init\n SutilEffect.RegisterDisposable(ctx.Parent,s)\n ctx |> (s |> app |> build)\n )\n\nlet declareStore<'T> (init : 'T) (f : Store<'T> -> unit) =\n declareResource (fun () -> Store.make init) f\n\nopen Browser.CssExtensions\n\nlet bindStyle<'T> (value : IObservable<'T>) (f : CSSStyleDeclaration -> 'T -> unit) =\n SutilElement.Define( \"bindStyle\",\n fun ctx ->\n let style = ctx.ParentElement.style\n let unsub = value.Subscribe(f style)\n SutilEffect.RegisterDisposable( ctx.Parent, unsub )\n () )\n\nlet bindWidthHeight (wh: IObservable) =\n bindStyle wh (fun style (w,h) ->\n if w <> 0.0 && h <> 0.0 then\n style.width <- w.ToString() + \"px\"\n style.height <- h.ToString() + \"px\"\n )\n\nlet bindLeftTop (xy : IObservable) =\n bindStyle xy (fun style (x,y) ->\n if x <> 0.0 && y <> 0.0 then\n style.left <- x.ToString() + \"px\"\n style.top <- y.ToString() + \"px\"\n )\n\nlet (|=>) store element = bindElement store element\n\nlet cssAttrsToString (cssAttrs) =\n cssAttrs |> Seq.map (fun (n,v) -> $\"{n}: {v};\") |> String.concat \"\"\n\nlet listWrap( list : 'T list ) = list.ToCollectionWrapper()\nlet listWrapO (list : IObservable<'T list>) = list |> Store.map listWrap\n\nlet arrayWrap( arr : 'T array ) = arr.ToCollectionWrapper()\nlet arrayWrapO (arr : IObservable<'T array>) = arr |> Store.map arrayWrap\n","namespace Sutil\n\nopen Transition\nopen Core\nopen Browser.Types\nopen System\nopen Fable.Core\nopen Bindings\nopen CoreElements\n\n/// \n/// Bindings for observables and the Core. For example, use an IObservable<bool> to toggle an element's class attribute\n/// \ntype Bind =\n\n static member visibility( isVisible : IObservable) = Transition.transition [] isVisible\n static member visibility( isVisible : IObservable,trans : TransitionAttribute list) = Transition.transition trans isVisible\n\n /// \n /// For input[type='radio']\n /// Only the checkbox with store's current value will be checked at any one time.\n /// \n static member radioValue<'T>( store : Store<'T> ) = Sutil.Bindings.bindRadioGroup store\n\n /// \n /// For multiple input elements. The input elements are grouped explicitly by name, or will be implicitly grouped by\n /// the (internal) name of the binding store.\n /// Each checkbox in the group is checked if its value is contained in the current string list\n /// \n static member checkboxGroup( store : Store ) = Sutil.Bindings.bindGroup store\n\n static member selectMultiple<'T when 'T : equality>( store : IStore<'T list> ) = Sutil.Bindings.bindSelectMultiple store\n static member selectOptional<'T when 'T : equality>( store : Store<'T option> ) = Sutil.Bindings.bindSelectOptional store\n static member selectSingle<'T when 'T : equality>( store : Store<'T> ) = Sutil.Bindings.bindSelectSingle store\n\n static member selectSingle<'T when 'T : equality>( value : System.IObservable<'T>, dispatch : 'T -> unit ) =\n bindSelected (value .> List.singleton) (List.exactlyOne >> dispatch)\n\n static member selectMultiple<'T when 'T : equality>( value : System.IObservable<'T list>, dispatch: 'T list -> unit ) =\n bindSelected value dispatch\n\n ///\n /// Bind a scalar value to an element attribute. Listen for onchange events and dispatch the\n /// attribute's current value to the given function. This form is useful for view templates\n /// where v is invariant (for example, an each that already filters on the value of v, like Todo.Done)\n ///\n static member attrInit<'T>( attrName : string, initValue : 'T, dispatch : 'T -> unit) = attrNotify attrName initValue dispatch\n\n /// Two-way binding from value to attribute and from attribute to dispatch function\n static member attr<'T> (name:string, value: IObservable<'T>, dispatch: 'T -> unit) =\n bindAttrBoth name value dispatch\n\n /// Dual-binding for a given attribute. Changes to value are written to the attribute, while\n /// changes to the attribute are written back to the store. Note that an IStore is also\n /// an IObservable, for which a separate overload exists.\n static member attr<'T> (name:string, value: IStore<'T>) = bindAttrStoreBoth name value\n\n /// One-way binding from value to attribute. Note that passing store to this function will\n /// select the more specific `attr<'T>( string, IStore<'T>)` overload.\n /// If that looks to be a problem, we'll rename both of them to force a considered choice.\n static member attr<'T> (name:string, value: IObservable<'T>) = bindAttrIn name value\n\n /// One-way binding from attribute to dispatch function\n static member attr<'T> (name:string, dispatch: 'T -> unit) = bindAttrOut name dispatch\n\n /// One way binding from style values into style attribute\n static member style (attrs : IObservable<#seq>) =\n Bind.attr(\"style\", attrs |> Store.map cssAttrsToString)\n\n /// One way binding from custom values to style updater function. This allows updating of the element's