From 9619f39bf887765087bc94ed9841e2441b8423a9 Mon Sep 17 00:00:00 2001 From: KristofferStrube Date: Mon, 18 Nov 2024 02:44:05 +0100 Subject: [PATCH] Added status page. --- .../Layout/NavMenu.razor | 10 + .../Pages/Status.razor | 357 ++++++++++++++++++ .../IGlobalEventHandlersExtensions.cs | 69 ++++ ...eExtensinos.cs => IJSRuntimeExtensions.cs} | 0 .../IWindowEventHandlersExtensions.cs | 21 ++ src/KristofferStrube.Blazor.Window/Window.cs | 36 +- 6 files changed, 475 insertions(+), 18 deletions(-) create mode 100644 samples/KristofferStrube.Blazor.Window.WasmExample/Pages/Status.razor create mode 100644 src/KristofferStrube.Blazor.Window/Extensions/IGlobalEventHandlersExtensions.cs rename src/KristofferStrube.Blazor.Window/Extensions/{IJSRuntimeExtensinos.cs => IJSRuntimeExtensions.cs} (100%) create mode 100644 src/KristofferStrube.Blazor.Window/Extensions/IWindowEventHandlersExtensions.cs diff --git a/samples/KristofferStrube.Blazor.Window.WasmExample/Layout/NavMenu.razor b/samples/KristofferStrube.Blazor.Window.WasmExample/Layout/NavMenu.razor index a9136e3..2caee8a 100644 --- a/samples/KristofferStrube.Blazor.Window.WasmExample/Layout/NavMenu.razor +++ b/samples/KristofferStrube.Blazor.Window.WasmExample/Layout/NavMenu.razor @@ -30,6 +30,16 @@ Events + diff --git a/samples/KristofferStrube.Blazor.Window.WasmExample/Pages/Status.razor b/samples/KristofferStrube.Blazor.Window.WasmExample/Pages/Status.razor new file mode 100644 index 0000000..f08579d --- /dev/null +++ b/samples/KristofferStrube.Blazor.Window.WasmExample/Pages/Status.razor @@ -0,0 +1,357 @@ +@page "/Status" + +@inject HttpClient HttpClient +@inject IJSRuntime JSRuntime + +Blazor Window - Status + +

+        @for (int i = 0; i < compareLines.Count; i++)
+        {
+            @($"{new string(' ', 3 - i.ToString().Length)}{i}") @compareLines[i].text
+        }
+    
+ +@code { + private List<(string text, string color)> compareLines = []; + + protected override void OnInitialized() + { + compareLines = []; + var lines = webIDL.Split('\n'); + for (int i = 0; i < lines.Count(); i++) + { + var color = supportedRows.Any(interval => i >= interval.start && i <= interval.end) ? "lightgreen" : "pink"; + compareLines.Add((lines[i], color)); + } + } + + private (int start, int end)[] supportedRows = new (int start, int end)[] { + (0, 1), + (3, 6), + (30, 31), + (33, 33), + (45, 49), + (53, 53), + (57, 58), + (60, 62), + (164, 164), + (194, 194), + (220, 222), + (242, 242), + (248, 248), + (263, 264), + (267, 267), + }; + + private const string webIDL = @"[Global=Window, + Exposed=Window, + LegacyUnenumerableNamedProperties] +interface Window : EventTarget { + // the current browsing context + [LegacyUnforgeable] readonly attribute WindowProxy window; + [Replaceable] readonly attribute WindowProxy self; + [LegacyUnforgeable] readonly attribute Document document; + attribute DOMString name; + [PutForwards=href, LegacyUnforgeable] readonly attribute Location location; + readonly attribute History history; + readonly attribute Navigation navigation; + readonly attribute CustomElementRegistry customElements; + [Replaceable] readonly attribute BarProp locationbar; + [Replaceable] readonly attribute BarProp menubar; + [Replaceable] readonly attribute BarProp personalbar; + [Replaceable] readonly attribute BarProp scrollbars; + [Replaceable] readonly attribute BarProp statusbar; + [Replaceable] readonly attribute BarProp toolbar; + attribute DOMString status; + undefined close(); + readonly attribute boolean closed; + undefined stop(); + undefined focus(); + undefined blur(); + + // other browsing contexts + [Replaceable] readonly attribute WindowProxy frames; + [Replaceable] readonly attribute unsigned long length; + [LegacyUnforgeable] readonly attribute WindowProxy? top; + attribute any opener; + [Replaceable] readonly attribute WindowProxy? parent; + readonly attribute Element? frameElement; + WindowProxy? open(optional USVString url = """", optional DOMString target = ""_blank"", optional [LegacyNullToEmptyString] DOMString features = """"); + + // Since this is the global object, the IDL named getter adds a NamedPropertiesObject exotic + // object on the prototype chain. Indeed, this does not make the global object an exotic object. + // Indexed access is taken care of by the WindowProxy exotic object. + getter object (DOMString name); + + // the user agent + readonly attribute Navigator navigator; + [Replaceable] readonly attribute Navigator clientInformation; // legacy alias of .navigator + readonly attribute boolean originAgentCluster; + + // user prompts + undefined alert(); + undefined alert(DOMString message); + boolean confirm(optional DOMString message = """"); + DOMString? prompt(optional DOMString message = """", optional DOMString default = """"); + undefined print(); + + undefined postMessage(any message, USVString targetOrigin, optional sequence transfer = []); + undefined postMessage(any message, optional WindowPostMessageOptions options = {}); + + // also has obsolete members +}; +Window includes GlobalEventHandlers; +Window includes WindowEventHandlers; + +dictionary WindowPostMessageOptions : StructuredSerializeOptions { + USVString targetOrigin = ""/""; +}; + +[Exposed=Window] +interface Location { // but see also additional creation steps and overridden internal methods + [LegacyUnforgeable] stringifier attribute USVString href; + [LegacyUnforgeable] readonly attribute USVString origin; + [LegacyUnforgeable] attribute USVString protocol; + [LegacyUnforgeable] attribute USVString host; + [LegacyUnforgeable] attribute USVString hostname; + [LegacyUnforgeable] attribute USVString port; + [LegacyUnforgeable] attribute USVString pathname; + [LegacyUnforgeable] attribute USVString search; + [LegacyUnforgeable] attribute USVString hash; + + [LegacyUnforgeable] undefined assign(USVString url); + [LegacyUnforgeable] undefined replace(USVString url); + [LegacyUnforgeable] undefined reload(); + + [LegacyUnforgeable, SameObject] readonly attribute DOMStringList ancestorOrigins; +}; + +enum ScrollRestoration { ""auto"", ""manual"" }; + +[Exposed=Window] +interface History { + readonly attribute unsigned long length; + attribute ScrollRestoration scrollRestoration; + readonly attribute any state; + undefined go(optional long delta = 0); + undefined back(); + undefined forward(); + undefined pushState(any data, DOMString unused, optional USVString? url = null); + undefined replaceState(any data, DOMString unused, optional USVString? url = null); +}; + +[Exposed=Window] +interface Navigation : EventTarget { + sequence entries(); + readonly attribute NavigationHistoryEntry? currentEntry; + undefined updateCurrentEntry(NavigationUpdateCurrentEntryOptions options); + readonly attribute NavigationTransition? transition; + readonly attribute NavigationActivation? activation; + + readonly attribute boolean canGoBack; + readonly attribute boolean canGoForward; + + NavigationResult navigate(USVString url, optional NavigationNavigateOptions options = {}); + NavigationResult reload(optional NavigationReloadOptions options = {}); + + NavigationResult traverseTo(DOMString key, optional NavigationOptions options = {}); + NavigationResult back(optional NavigationOptions options = {}); + NavigationResult forward(optional NavigationOptions options = {}); + + attribute EventHandler onnavigate; + attribute EventHandler onnavigatesuccess; + attribute EventHandler onnavigateerror; + attribute EventHandler oncurrententrychange; +}; + +dictionary NavigationUpdateCurrentEntryOptions { + required any state; +}; + +dictionary NavigationOptions { + any info; +}; + +dictionary NavigationNavigateOptions : NavigationOptions { + any state; + NavigationHistoryBehavior history = ""auto""; +}; + +dictionary NavigationReloadOptions : NavigationOptions { + any state; +}; + +dictionary NavigationResult { + Promise committed; + Promise finished; +}; + +enum NavigationHistoryBehavior { + ""auto"", + ""push"", + ""replace"" +}; + +[Exposed=Window] +interface CustomElementRegistry { + [CEReactions] undefined define(DOMString name, CustomElementConstructor constructor, optional ElementDefinitionOptions options = {}); + (CustomElementConstructor or undefined) get(DOMString name); + DOMString? getName(CustomElementConstructor constructor); + Promise whenDefined(DOMString name); + [CEReactions] undefined upgrade(Node root); +}; + +callback CustomElementConstructor = HTMLElement (); + +dictionary ElementDefinitionOptions { + DOMString extends; +}; + +interface mixin GlobalEventHandlers { + attribute EventHandler onabort; + attribute EventHandler onauxclick; + attribute EventHandler onbeforeinput; + attribute EventHandler onbeforematch; + attribute EventHandler onbeforetoggle; + attribute EventHandler onblur; + attribute EventHandler oncancel; + attribute EventHandler oncanplay; + attribute EventHandler oncanplaythrough; + attribute EventHandler onchange; + attribute EventHandler onclick; + attribute EventHandler onclose; + attribute EventHandler oncontextlost; + attribute EventHandler oncontextmenu; + attribute EventHandler oncontextrestored; + attribute EventHandler oncopy; + attribute EventHandler oncuechange; + attribute EventHandler oncut; + attribute EventHandler ondblclick; + attribute EventHandler ondrag; + attribute EventHandler ondragend; + attribute EventHandler ondragenter; + attribute EventHandler ondragleave; + attribute EventHandler ondragover; + attribute EventHandler ondragstart; + attribute EventHandler ondrop; + attribute EventHandler ondurationchange; + attribute EventHandler onemptied; + attribute EventHandler onended; + attribute OnErrorEventHandler onerror; + attribute EventHandler onfocus; + attribute EventHandler onformdata; + attribute EventHandler oninput; + attribute EventHandler oninvalid; + attribute EventHandler onkeydown; + attribute EventHandler onkeypress; + attribute EventHandler onkeyup; + attribute EventHandler onload; + attribute EventHandler onloadeddata; + attribute EventHandler onloadedmetadata; + attribute EventHandler onloadstart; + attribute EventHandler onmousedown; + [LegacyLenientThis] attribute EventHandler onmouseenter; + [LegacyLenientThis] attribute EventHandler onmouseleave; + attribute EventHandler onmousemove; + attribute EventHandler onmouseout; + attribute EventHandler onmouseover; + attribute EventHandler onmouseup; + attribute EventHandler onpaste; + attribute EventHandler onpause; + attribute EventHandler onplay; + attribute EventHandler onplaying; + attribute EventHandler onprogress; + attribute EventHandler onratechange; + attribute EventHandler onreset; + attribute EventHandler onresize; + attribute EventHandler onscroll; + attribute EventHandler onscrollend; + attribute EventHandler onsecuritypolicyviolation; + attribute EventHandler onseeked; + attribute EventHandler onseeking; + attribute EventHandler onselect; + attribute EventHandler onslotchange; + attribute EventHandler onstalled; + attribute EventHandler onsubmit; + attribute EventHandler onsuspend; + attribute EventHandler ontimeupdate; + attribute EventHandler ontoggle; + attribute EventHandler onvolumechange; + attribute EventHandler onwaiting; + attribute EventHandler onwebkitanimationend; + attribute EventHandler onwebkitanimationiteration; + attribute EventHandler onwebkitanimationstart; + attribute EventHandler onwebkittransitionend; + attribute EventHandler onwheel; +}; + +interface mixin WindowEventHandlers { + attribute EventHandler onafterprint; + attribute EventHandler onbeforeprint; + attribute OnBeforeUnloadEventHandler onbeforeunload; + attribute EventHandler onhashchange; + attribute EventHandler onlanguagechange; + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; + attribute EventHandler onoffline; + attribute EventHandler ononline; + attribute EventHandler onpagehide; + attribute EventHandler onpagereveal; + attribute EventHandler onpageshow; + attribute EventHandler onpageswap; + attribute EventHandler onpopstate; + attribute EventHandler onrejectionhandled; + attribute EventHandler onstorage; + attribute EventHandler onunhandledrejection; + attribute EventHandler onunload; +}; + +[Exposed=(Window,Worker,AudioWorklet)] +interface MessageEvent : Event { + constructor(DOMString type, optional MessageEventInit eventInitDict = {}); + + readonly attribute any data; + readonly attribute USVString origin; + readonly attribute DOMString lastEventId; + readonly attribute MessageEventSource? source; + readonly attribute FrozenArray ports; + + undefined initMessageEvent(DOMString type, + optional boolean bubbles = false, + optional boolean cancelable = false, + optional any data = null, + optional USVString origin = """", + optional DOMString lastEventId = """", + optional MessageEventSource? source = null, + optional sequence ports = []); +}; + +dictionary MessageEventInit : EventInit { + any data = null; + USVString origin = ""; + DOMString lastEventId = ""; + MessageEventSource? source = null; + sequence ports = []; +}; + +typedef (WindowProxy or MessagePort or ServiceWorker) MessageEventSource; + +[Exposed=(Window,Worker,AudioWorklet), Transferable] +interface MessagePort : EventTarget { + undefined postMessage(any message, sequence transfer); + undefined postMessage(any message, optional StructuredSerializeOptions options = {}); + undefined start(); + undefined close(); + + // event handlers + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; + attribute EventHandler onclose; +}; + +dictionary StructuredSerializeOptions { + sequence transfer = []; +};"; + +} \ No newline at end of file diff --git a/src/KristofferStrube.Blazor.Window/Extensions/IGlobalEventHandlersExtensions.cs b/src/KristofferStrube.Blazor.Window/Extensions/IGlobalEventHandlersExtensions.cs new file mode 100644 index 0000000..a687bde --- /dev/null +++ b/src/KristofferStrube.Blazor.Window/Extensions/IGlobalEventHandlersExtensions.cs @@ -0,0 +1,69 @@ +using KristofferStrube.Blazor.DOM; + +namespace KristofferStrube.Blazor.Window.Extensions; + +/// +/// Extension methods that hold the implementations for so that any that wish to implement the interface can use the same implementations. +/// +public static class IGlobalEventHandlersExtensions +{ + /// + public static async Task AddOnErrorEventListenerAsync(this T globalEventHandler, EventListener callback, AddEventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.AddEventListenerAsync("error", callback, options); + } + + /// + public static async Task AddOnErrorEventListenerAsync(this T globalEventHandler, EventListener callback, AddEventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.AddEventListenerAsync("error", callback, options); + } + + /// + public static async Task RemoveOnErrorEventListenerAsync(this T globalEventHandler, EventListener callback, EventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.RemoveEventListenerAsync("error", callback, options); + } + + /// + public static async Task RemoveOnErrorEventListenerAsync(this T globalEventHandler, EventListener callback, EventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.RemoveEventListenerAsync("error", callback, options); + } + + /// + public static async Task AddOnResizeEventListenerAsync(this T globalEventHandler, EventListener callback, AddEventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.AddEventListenerAsync("resize", callback, options); + } + + /// + public static async Task RemoveOnResizeEventListenerAsync(this T globalEventHandler, EventListener callback, EventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.RemoveEventListenerAsync("resize", callback, options); + } + + /// + public static async Task AddOnScrollEventListenerAsync(this T globalEventHandler, EventListener callback, AddEventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.AddEventListenerAsync("scroll", callback, options); + } + + /// + public static async Task RemoveOnScrollEventListenerAsync(this T globalEventHandler, EventListener callback, EventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.RemoveEventListenerAsync("scroll", callback, options); + } + + /// + public static async Task AddOnScrollEndEventListenerAsync(this T globalEventHandler, EventListener callback, AddEventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.AddEventListenerAsync("scrollend", callback, options); + } + + /// + public static async Task RemoveOnScrollEndEventListenerAsync(this T globalEventHandler, EventListener callback, EventListenerOptions? options = null) where T : EventTarget, IGlobalEventHandlers + { + await globalEventHandler.RemoveEventListenerAsync("scrollend", callback, options); + } +} diff --git a/src/KristofferStrube.Blazor.Window/Extensions/IJSRuntimeExtensinos.cs b/src/KristofferStrube.Blazor.Window/Extensions/IJSRuntimeExtensions.cs similarity index 100% rename from src/KristofferStrube.Blazor.Window/Extensions/IJSRuntimeExtensinos.cs rename to src/KristofferStrube.Blazor.Window/Extensions/IJSRuntimeExtensions.cs diff --git a/src/KristofferStrube.Blazor.Window/Extensions/IWindowEventHandlersExtensions.cs b/src/KristofferStrube.Blazor.Window/Extensions/IWindowEventHandlersExtensions.cs new file mode 100644 index 0000000..5597527 --- /dev/null +++ b/src/KristofferStrube.Blazor.Window/Extensions/IWindowEventHandlersExtensions.cs @@ -0,0 +1,21 @@ +using KristofferStrube.Blazor.DOM; + +namespace KristofferStrube.Blazor.Window.Extensions; + +/// +/// Extension methods that hold the implementations for so that any that wish to implement the interface can use the same implementations. +/// +public static class IWindowEventHandlersExtensions +{ + /// + public static async Task AddOnMessageEventListenerAsync(this T windowEventHandler, EventListener callback, AddEventListenerOptions? options = null) where T : EventTarget, IWindowEventHandlers + { + await windowEventHandler.AddEventListenerAsync("message", callback, options); + } + + /// + public static async Task RemoveOnMessageEventListenerAsync(this T windowEventHandler, EventListener callback, EventListenerOptions? options = null) where T : EventTarget, IWindowEventHandlers + { + await windowEventHandler.RemoveEventListenerAsync("message", callback, options); + } +} diff --git a/src/KristofferStrube.Blazor.Window/Window.cs b/src/KristofferStrube.Blazor.Window/Window.cs index 402adf7..5427da6 100644 --- a/src/KristofferStrube.Blazor.Window/Window.cs +++ b/src/KristofferStrube.Blazor.Window/Window.cs @@ -59,24 +59,24 @@ public async Task GetSelfAsync() } /// - /// Gets the parent navigable of this . + /// Gets the opener navigable of this . /// /// - public async Task GetParentAsync() + public async Task GetOpenerAsync() { IJSObjectReference helper = await windowHelperTask.Value; - IJSObjectReference? jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "parent"); + IJSObjectReference? jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "opener"); return jSInstance is null ? null : new(JSRuntime, jSInstance, new() { DisposesJSReference = true }); } /// - /// Gets the opener navigable of this . + /// Gets the parent navigable of this . /// /// - public async Task GetOpenerAsync() + public async Task GetParentAsync() { IJSObjectReference helper = await windowHelperTask.Value; - IJSObjectReference? jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "opener"); + IJSObjectReference? jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "parent"); return jSInstance is null ? null : new(JSRuntime, jSInstance, new() { DisposesJSReference = true }); } @@ -145,72 +145,72 @@ public async Task PostMessageAsync(object message, WindowPostMessageOptions? opt /// public async Task AddOnMessageEventListenerAsync(EventListener callback, AddEventListenerOptions? options = null) { - await AddEventListenerAsync("message", callback, options); + await this.AddOnMessageEventListenerAsync(callback, options); } /// public async Task RemoveOnMessageEventListenerAsync(EventListener callback, EventListenerOptions? options = null) { - await RemoveEventListenerAsync("message", callback, options); + await this.RemoveOnMessageEventListenerAsync(callback, options); } /// public async Task AddOnErrorEventListenerAsync(EventListener callback, AddEventListenerOptions? options = null) { - await JSReference.InvokeVoidAsync("addEventListener", "error", callback, options); + await this.AddOnErrorEventListenerAsync(callback, options); } /// public async Task AddOnErrorEventListenerAsync(EventListener callback, AddEventListenerOptions? options = null) { - await JSReference.InvokeVoidAsync("addEventListener", "error", callback, options); + await this.AddOnErrorEventListenerAsync(callback, options); } /// public async Task RemoveOnErrorEventListenerAsync(EventListener callback, EventListenerOptions? options = null) { - await JSReference.InvokeVoidAsync("removeEventListener", "error", callback, options); + await this.RemoveOnErrorEventListenerAsync(callback, options); } /// public async Task RemoveOnErrorEventListenerAsync(EventListener callback, EventListenerOptions? options = null) { - await JSReference.InvokeVoidAsync("removeEventListener", "error", callback, options); + await this.RemoveOnErrorEventListenerAsync(callback, options); } /// public async Task AddOnResizeEventListenerAsync(EventListener callback, AddEventListenerOptions? options = null) { - await AddEventListenerAsync("resize", callback, options); + await this.AddOnResizeEventListenerAsync(callback, options); } /// public async Task RemoveOnResizeEventListenerAsync(EventListener callback, EventListenerOptions? options = null) { - await RemoveEventListenerAsync("resize", callback, options); + await this.RemoveOnResizeEventListenerAsync(callback, options); } /// public async Task AddOnScrollEventListenerAsync(EventListener callback, AddEventListenerOptions? options = null) { - await AddEventListenerAsync("scroll", callback, options); + await this.AddOnScrollEventListenerAsync(callback, options); } /// public async Task RemoveOnScrollEventListenerAsync(EventListener callback, EventListenerOptions? options = null) { - await RemoveEventListenerAsync("scroll", callback, options); + await this.RemoveOnScrollEventListenerAsync(callback, options); } /// public async Task AddOnScrollEndEventListenerAsync(EventListener callback, AddEventListenerOptions? options = null) { - await AddEventListenerAsync("scrollend", callback, options); + await this.AddOnScrollEndEventListenerAsync(callback, options); } /// public async Task RemoveOnScrollEndEventListenerAsync(EventListener callback, EventListenerOptions? options = null) { - await RemoveEventListenerAsync("scrollend", callback, options); + await this.RemoveOnScrollEndEventListenerAsync(callback, options); } }