diff --git a/Assets/Scripts/UI/InGame/WebViewController.cs b/Assets/Scripts/UI/InGame/WebViewController.cs index c1cb33f3a370b56697153bf31e8e6efc9374f15a..8d247c58f70763c8843a3fa1d6b78a603fe603e2 100644 --- a/Assets/Scripts/UI/InGame/WebViewController.cs +++ b/Assets/Scripts/UI/InGame/WebViewController.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Text.RegularExpressions; using UnityEngine; +using UnityEngine.UIElements; public class WebViewController : ScrollView { @@ -35,9 +36,10 @@ private void OnWebViewComponentReady() SwitchScrollUI.activeScrollData.HintAvailableEvent.AddListener(OnHintAvailable); webViewComponent.tab.AddJSBinding("applyScroll", ApplyScrollHandler); webViewComponent.tab.AddJSBinding("getHint", GetHintHandler); + // webViewComponent.tab.AddJSBinding("updateAssignments", UpdateAssignmentsHandler); // The browser can just ask the MMTServer directly if (SwitchScrollUI.activeScrollData.Scroll is not null) { - SetScrollContent(SwitchScrollUI.activeScrollData.Scroll); + SetScrollContent(SwitchScrollUI.activeScrollData.RenderedScroll); } RegisterBrowserEventhandlers(); } @@ -76,42 +78,23 @@ private void DeRegisterBrowserEventhandlers() } /// <summary> - /// sets or updates the content of the scroll container dom element + /// <para> + /// Hand the ScrollView the labels (and potentially assignments) of all facts, so it can render the scroll content. (And then tell it to do so) + /// </para><para> + /// More precisely: Put a stringified and Json serialized Dictionary + /// <see cref="OMS.uri"/> slotID -> (<see cref="string"/> label, <see cref="Fact.Id"/> factId) <br/> + /// into the "data-assignments" attribute of the "assignments" node + /// </para> /// </summary> /// <param name="scroll"></param> private async void SetScrollContent(Scroll scroll) { // update scroll container content DomNodeWrapper document = await webViewComponent.tab.Document; - DomNodeWrapper scrollContainer = await document.querySelectorAsync("#scrollContainer"); + DomNodeWrapper scrollContainer = await document.querySelectorAsync("#scrollContainer"); var description = scroll.description; - if (Regex.IsMatch(description, ".*<scroll-description.*")) - { - // replace slot templates - description = Regex.Replace(description, "<scroll-slot([^>]*)>([^<]*)</scroll-slot>", match => - { - var extraAttributes = match.Groups[1].Value; - var slotId = match.Groups[2].Value; - var assignment = SwitchScrollUI.activeScrollData.Assignments[slotId]; - /** label of the assigned fact, or the slot label if nothing assigned */ - var label = assignment.IsSet ? assignment.fact.GetLabel(StageStatic.stage.factState) - : scroll.requiredFacts.Find(fact => fact.@ref.uri == slotId).label; - /** id of the assigned fact. If nothing is assigned don't add the attribute */ - var fact_id = assignment.IsSet ? $"data-fact-id='{assignment.fact.Id}'" - : ""; - return $"<mi dropzone='copy' data-slot-id='{slotId}' {fact_id} {extraAttributes}>{label}</mi>"; - }); - // replace solution templates - description = Regex.Replace(description, "<scroll-solution([^>]*)>([^<]*)</scroll-solution>", match => - { - var extraAttributes = match.Groups[1].Value; - var solutionId = match.Groups[2].Value; - var label = scroll.acquiredFacts.Find(fact => fact.@ref.uri == solutionId).label; - return $"<mi data-solution-id='{solutionId}' {extraAttributes}>{label}</mi>"; - }); - } - else + if (!Regex.IsMatch(description, ".*<scroll-description.*")) { // scroll is a legacy plain text scroll, generate html with slots for the required facts var factSlots = SwitchScrollUI.activeScrollData.Assignments @@ -120,15 +103,67 @@ private async void SetScrollContent(Scroll scroll) @$"<span class='legacySlot' dropzone='copy' data-slot-id='{pair.Key}' {(pair.Value.IsSet ? $"data-fact-id='{pair.Value.fact.Id}'" : "")}> {(pair.Value.IsSet ? pair.Value.fact.GetLabel(StageStatic.stage.factState) : - scroll.requiredFacts.Find(fact => fact.@ref.uri == pair.Key).label )} + scroll.requiredFacts.Find(fact => fact.@ref.uri == pair.Key).label)} </span>"); description = $"<scroll-description><p>{scroll.description}</p><div id='legacySlots'>{String.Join("", factSlots)}</div></scroll-description>"; + } - // display the scroll description - dropzones = null; + //else // Is done by JS. How to render the scroll is the scrolls buisness + //{ + // // replace slot templates + // description = Regex.Replace(description, "<scroll-slot([^>]*)>([^<]*)</scroll-slot>", match => + // { + // var extraAttributes = match.Groups[1].Value; + // var slotId = match.Groups[2].Value; + // var assignment = SwitchScrollUI.activeScrollData.Assignments[slotId]; + // /** label of the assigned fact, or the slot label if nothing assigned */ + // var label = assignment.IsSet ? assignment.fact.GetLabel(StageStatic.stage.factState) + // : scroll.requiredFacts.Find(fact => fact.@ref.uri == slotId).label; + // /** id of the assigned fact. If nothing is assigned don't add the attribute */ + // var fact_id = assignment.IsSet ? $"data-fact-id='{assignment.fact.Id}'" + // : ""; + // return $"<mi dropzone='copy' data-slot-id='{slotId}' {fact_id} {extraAttributes}>{label}</mi>"; + // }); + // // replace solution templates + // description = Regex.Replace(description, "<scroll-solution([^>]*)>([^<]*)</scroll-solution>", match => + // { + // var extraAttributes = match.Groups[1].Value; + // var solutionId = match.Groups[2].Value; + // var label = scroll.acquiredFacts.Find(fact => fact.@ref.uri == solutionId).label; + // return $"<mi data-solution-id='{solutionId}' {extraAttributes}>{label}</mi>"; + // }); + //} + + // Display the scroll description. await scrollContainer.setOuterHtmlAsync($"<div id='scrollContainer'>{description}</div>"); + + DomNodeWrapper assignmentsNode = await document.querySelectorAsync("#assignments"); + Dictionary<string, (string, string)> assignments = new(); + + foreach (MMTFact fact in scroll.requiredFacts) + { + string slotId = fact.@ref.uri; + ActiveScroll.SlotAssignment assignment = SwitchScrollUI.activeScrollData.Assignments[slotId]; + if (assignment.IsSet) + { + assignments.Add(slotId, (assignment.fact.GetLabel(StageStatic.stage.factState), assignment.fact.Id)); + } + else + { + assignments.Add(slotId, (fact.label, "")); + } + } + foreach (MMTFact fact in scroll.acquiredFacts) + { + assignments.Add(fact.@ref.uri, (fact.label, "")); + } + await assignmentsNode.setAttributeValue("data-assignments", JsonConvert.SerializeObject(assignments)); + _ = webViewComponent.tab.Evaluate("RenderScroll()"); + + // Update the interactive components + dropzones = null; RegisterBrowserEventhandlers(); } @@ -194,6 +229,43 @@ private void GetHintHandler(string url) SwitchScrollUI.activeScrollData.ButtonClicked(new HintScrollButton(url)); } + /// <summary> + /// <para> + /// Hand the ScrollView the labels (and potentially assignments) of all facts, so it can render the scroll. + /// </para><para> + /// More precisely: Put a stringified and Json serialized Dictionary + /// <see cref="OMS.uri"/> slotID -> (<see cref="string"/> label, <see cref="Fact.Id"/> factId) <br/> + /// into the "data-assignments" attribute of the "assignments" node + /// </para> + /// </summary> + /// <param name="youHappyNowJS">Is ignored. JS bindings need to take a string argument, so here it is.</param> + [Obsolete("This data can be aquired directly from the MMTServer")] + private async void UpdateAssignmentsHandler(string youHappyNowJS = "") + { + DomNodeWrapper document = await webViewComponent.tab.Document; + DomNodeWrapper assignmentsNode = await document.querySelectorAsync("#assignments"); + Scroll scroll = SwitchScrollUI.activeScrollData.RenderedScroll; + Dictionary<string, (string,string)> assignments = new(); + foreach (var (slotId,assignment) in SwitchScrollUI.activeScrollData.Assignments) + { + string label = assignment.IsSet ? assignment.fact.GetLabel(StageStatic.stage.factState) + : scroll.requiredFacts.Find(fact => fact.@ref.uri == slotId).label; + /** id of the assigned fact. If nothing is assigned don't add the attribute */ + string fact_id = assignment.IsSet ? assignment.fact.Id : ""; + assignments.Add(slotId,(label,fact_id)); + } + foreach(MMTFact fact in scroll.acquiredFacts) + { + string label = scroll.acquiredFacts.Find(f => fact.@ref.uri == f.@ref.uri).label; + Debug.Log(label); + Debug.Log(fact.label); + assignments.Add(fact.@ref.uri, (fact.label, "")); + } + + + await assignmentsNode.setAttributeValue("data-assignments", JsonConvert.SerializeObject(assignments)); + } + public string[] GetFactAssignments() { return dropzones.Select(dropzone => dropzone.Node.attributes.GetValueOrDefault("data-fact-id", null)).ToArray(); diff --git a/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html b/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html index fdbda223d648b86a920c896deeb72e30c8bfbc5e..3c96afb86e56c8cea7c751828b4cef3f8eeb0fd8 100644 --- a/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html +++ b/Assets/StreamingAssets/StreamToDataPath_withHandler/scrollView.html @@ -43,28 +43,28 @@ <span>Given a triangle</span> <math> <mi>△<!-- △ --></mi> - <scroll-slot>http://mathhub.info/FrameIT/frameworld?TriangleProblem?A</scroll-slot> - <scroll-slot>http://mathhub.info/FrameIT/frameworld?TriangleProblem?B</scroll-slot> - <scroll-slot>http://mathhub.info/FrameIT/frameworld?TriangleProblem?C</scroll-slot> + <mi data-slot-id="http://mathhub.info/FrameIT/frameworld?TriangleProblem?A"></mi> + <mi data-slot-id="http://mathhub.info/FrameIT/frameworld?TriangleProblem?B"></mi> + <mi data-slot-id="http://mathhub.info/FrameIT/frameworld?TriangleProblem?C"></mi> </math> <span>right-angled at</span> <math> <!--<mi>⦝</mi>--><!-- ⦝ --> - <scroll-slot>http://mathhub.info/FrameIT/frameworld?TriangleProblem_RightAngleAtC?rightAngleC</scroll-slot> + <mi data-slot-id="http://mathhub.info/FrameIT/frameworld?TriangleProblem_RightAngleAtC?rightAngleC"></mi> </math>,<br /> <span>the opposite side has length</span> <math> - <scroll-solution>http://mathhub.info/FrameIT/frameworld?OppositeLen/Solution?deducedLineCA</scroll-solution> + <mi data-solution-id="http://mathhub.info/FrameIT/frameworld?OppositeLen/Solution?deducedLineCA"></mi> <mo>=</mo> <mrow> <mi>tan</mi> <!--<mo>⁡</mo>--> <mo>(</mo> - <scroll-slot>http://mathhub.info/FrameIT/frameworld?TriangleProblem_AngleAtB?angleB</scroll-slot> + <mi data-slot-id="http://mathhub.info/FrameIT/frameworld?TriangleProblem_AngleAtB?angleB"></mi> <mo>)</mo> </mrow> <mo>⁢</mo> - <scroll-slot>http://mathhub.info/FrameIT/frameworld?OppositeLen/Problem?distanceBC</scroll-slot> + <mi data-slot-id="http://mathhub.info/FrameIT/frameworld?OppositeLen/Problem?distanceBC"></mi> </math> <span>.</span> <div> @@ -109,7 +109,6 @@ y='67.117035' id='pointB' style='fill:#0000ff' - dropzone='copy' data-slot-id='http://mathhub.info/FrameIT/frameworld?TriangleProblem?B'>${lverb B}</text> <text xml:space='preserve' @@ -117,7 +116,6 @@ y='68.080437' id='pointC' style='fill:#0000ff' - dropzone='copy' data-slot-id='http://mathhub.info/FrameIT/frameworld?TriangleProblem?C'>${lverb C}</text> <text xml:space='preserve' @@ -125,7 +123,6 @@ y='6.551136' id='pointA' style='fill:#0000ff' - dropzone='copy' data-slot-id='http://mathhub.info/FrameIT/frameworld?TriangleProblem?A'>${lverb A}</text> <text xml:space='preserve' @@ -133,7 +130,6 @@ y='62.492699' id='angleAtB' style='fill:#ffcc00' - dropzone='copy' data-slot-id='http://mathhub.info/FrameIT/frameworld?TriangleProblem_AngleAtB?angleB'>${lverb angleB}</text> <text xml:space='preserve' @@ -141,7 +137,6 @@ y='60.951256' id='rightAngleAtC' style='fill:#ffcc00' - dropzone='copy' data-slot-id='http://mathhub.info/FrameIT/frameworld?TriangleProblem_RightAngleAtC?rightAngleC'>${lverb rightAngleC}</text> <text xml:space='preserve' @@ -149,7 +144,6 @@ y='68.273117' id='distanceBC' style='fill:#008000' - dropzone='copy' data-slot-id='http://mathhub.info/FrameIT/frameworld?OppositeLen/Problem?distanceBC'>${lverb distanceBC}</text> <text xml:space='preserve' @@ -183,15 +177,10 @@ } loadScript("./scroll_interaction/Drop_facts.js") - - const container = document.getElementById('scrollContainer'); - const tmp = document.createElement('div'); - tmp.id='test' - tmp.innerText = 'test' - container.append(tmp); + loadScript("./scroll_interaction/SetScrollContent.js") </script> - <data id="assignments" hidden data-assignments="">hi</data> + <data id="assignments" hidden data-assignments="{"http://mathhub.info/FrameIT/frameworld?TriangleProblem?A":{"Item1":"(C)","Item2":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact310"},"http://mathhub.info/FrameIT/frameworld?TriangleProblem?B":{"Item1":"(B)","Item2":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact309"},"http://mathhub.info/FrameIT/frameworld?TriangleProblem?C":{"Item1":"C","Item2":""},"http://mathhub.info/FrameIT/frameworld?TriangleProblem_RightAngleAtC?rightAngleC":{"Item1":"⊾C","Item2":""},"http://mathhub.info/FrameIT/frameworld?OppositeLen/Problem?distanceBC":{"Item1":"BC","Item2":""},"http://mathhub.info/FrameIT/frameworld?TriangleProblem_AngleAtB?angleB":{"Item1":"∠ABC","Item2":""},"http://mathhub.info/FrameIT/frameworld?OppositeLen/Solution?deducedLineCA":{"Item1":"CA","Item2":""}}">hi</data> <!--<script src="visualiseCursor.mjs" defer type="module"></script>--> <!--<script> diff --git a/Assets/StreamingAssets/StreamToDataPath_withHandler/scroll_interaction/SetScrollContent.js b/Assets/StreamingAssets/StreamToDataPath_withHandler/scroll_interaction/SetScrollContent.js new file mode 100644 index 0000000000000000000000000000000000000000..f206f4fec4b38428ac25f1a8db2b0cf0f83f994d --- /dev/null +++ b/Assets/StreamingAssets/StreamToDataPath_withHandler/scroll_interaction/SetScrollContent.js @@ -0,0 +1,67 @@ + +function RenderScroll() { + const scrollContainer = document.querySelector("#scrollContainer"); + const description = scrollContainer.textContent; + const assignments = JSON.parse(document.querySelector("#assignments").dataset.assignments) + console.log(assignments); + + //everything that has a slotID is a scroll slot + scrollContainer.querySelectorAll("[data-slot-id]").forEach(element => { + //console.log(element); + let slotId = element.dataset.slotId; + let fact_assignment = assignments[slotId]; + + element.setAttribute("dropzone", "copy"); + element.textContent = fact_assignment.Item1; + + if (fact_assignment.Item2 != "") { + element.dataset.factId = fact_assignment.Item2; + } + }); + + //everything that has a solutionID is a scroll solution + scrollContainer.querySelectorAll("[data-solution-id]").forEach(element => { + element.textContent = assignments[element.dataset.solutionId].Item1; + }) + console.log('Scroll rendered') +} +RenderScroll() +// if (Regex.IsMatch(description, ".*<scroll-description.*")) +// { +// // replace slot templates +// description = Regex.Replace(description, "<scroll-slot([^>]*)>([^<]*)</scroll-slot>", match => +// { +// var extraAttributes = match.Groups[1].Value; +// var slotId = match.Groups[2].Value; +// var assignment = SwitchScrollUI.activeScrollData.Assignments[slotId]; +// /** label of the assigned fact, or the slot label if nothing assigned */ +// var label = assignment.IsSet ? assignment.fact.GetLabel(StageStatic.stage.factState) +// : scroll.requiredFacts.Find(fact => fact.@ref.uri == slotId).label; +// /** id of the assigned fact. If nothing is assigned don't add the attribute */ +// var fact_id = assignment.IsSet ? $"data-fact-id='{assignment.fact.Id}'" +// : ""; +// return $"<mi dropzone='copy' data-slot-id='{slotId}' {fact_id} {extraAttributes}>{label}</mi>"; +// }); +// // replace solution templates +// description = Regex.Replace(description, "<scroll-solution([^>]*)>([^<]*)</scroll-solution>", match => +// { +// var extraAttributes = match.Groups[1].Value; +// var solutionId = match.Groups[2].Value; +// var label = scroll.acquiredFacts.Find(fact => fact.@ref.uri == solutionId).label; +// return $"<mi data-solution-id='{solutionId}' {extraAttributes}>{label}</mi>"; +// }); +// } +// else +// { +// // scroll is a legacy plain text scroll, generate html with slots for the required facts +// var factSlots = SwitchScrollUI.activeScrollData.Assignments +// .Where(pair => pair.Value.IsVisible) +// .Select(pair => +// @$"<span class='legacySlot' dropzone='copy' data-slot-id='{pair.Key}' {(pair.Value.IsSet ? $"data-fact-id='{pair.Value.fact.Id}'" : "")}> +// {(pair.Value.IsSet ? +// pair.Value.fact.GetLabel(StageStatic.stage.factState) : +// scroll.requiredFacts.Find(fact => fact.@ref.uri == pair.Key).label )} +// </span>"); + +// description = $"<scroll-description><p>{scroll.description}</p><div id='legacySlots'>{String.Join("", factSlots)}</div></scroll-description>"; +// } \ No newline at end of file diff --git a/Assets/StreamingAssets/StreamToDataPath_withHandler/scroll_interaction/SetScrollContent.js.meta b/Assets/StreamingAssets/StreamToDataPath_withHandler/scroll_interaction/SetScrollContent.js.meta new file mode 100644 index 0000000000000000000000000000000000000000..cedf0ac5485748bf9bf7e22d808882c9b48c5327 --- /dev/null +++ b/Assets/StreamingAssets/StreamToDataPath_withHandler/scroll_interaction/SetScrollContent.js.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 215e6162e17215b4a9118b3db2f4944f +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: