From 822cad09dec4c968aece052368470b9c481a1a44 Mon Sep 17 00:00:00 2001 From: Simon Martens Date: Thu, 14 Nov 2024 17:24:17 +0100 Subject: [PATCH] Fixed a few bugs; fixed marginals; fixed google indexing --- HaWeb/.gitignore | 1 + HaWeb/FileHelpers/HaDocumentWrapper.cs | 9 +- HaWeb/FileHelpers/XMLFileProvider.cs | 45 ++- HaWeb/Program.cs | 2 +- HaWeb/appsettings.Development.json | 6 +- HaWeb/wwwroot/js/marginals.mjs | 401 +++++++++++++------------ 6 files changed, 238 insertions(+), 226 deletions(-) diff --git a/HaWeb/.gitignore b/HaWeb/.gitignore index 5ab7e06..fd7428c 100644 --- a/HaWeb/.gitignore +++ b/HaWeb/.gitignore @@ -23,3 +23,4 @@ bin/* *.njsproj *.sln *.sw? +testdata/ diff --git a/HaWeb/FileHelpers/HaDocumentWrapper.cs b/HaWeb/FileHelpers/HaDocumentWrapper.cs index 991cdc1..2d5913b 100644 --- a/HaWeb/FileHelpers/HaDocumentWrapper.cs +++ b/HaWeb/FileHelpers/HaDocumentWrapper.cs @@ -13,7 +13,7 @@ using System.Xml.Linq; using System.Diagnostics; public class HaDocumentWrapper : IHaDocumentWrappper { - private IFileInfo _ActiveFile; + private IFileInfo? _ActiveFile; private ILibrary? Library; private IXMLInteractionService _xmlService; private int _startYear; @@ -51,7 +51,7 @@ public class HaDocumentWrapper : IHaDocumentWrappper { // 1. Parse the Document, create search Index var sw = new Stopwatch(); sw.Start(); - if (_xmlService != null) + if (_xmlService != null) _xmlService.CreateCollections(doc); sw.Stop(); Console.WriteLine("Parsed Collections, elapsed: " + sw.ElapsedMilliseconds); @@ -60,7 +60,8 @@ public class HaDocumentWrapper : IHaDocumentWrappper { // 2. Set ILibrary try { Library = HaDocument.Document.Create(new HaWeb.Settings.HaDocumentOptions() { HamannXMLFilePath = path, AvailableYearRange = (_startYear, _endYear) }, doc.Root); - } catch (Exception ex) { + } + catch (Exception ex) { if (ModelState != null) ModelState.AddModelError("Error", "Das Dokument konnte nicht geparst werden: " + ex.Message); return null; } @@ -87,4 +88,4 @@ public class HaDocumentWrapper : IHaDocumentWrappper { public ILibrary? GetLibrary() { return Library; } -} \ No newline at end of file +} diff --git a/HaWeb/FileHelpers/XMLFileProvider.cs b/HaWeb/FileHelpers/XMLFileProvider.cs index a70b26c..543440c 100644 --- a/HaWeb/FileHelpers/XMLFileProvider.cs +++ b/HaWeb/FileHelpers/XMLFileProvider.cs @@ -3,10 +3,8 @@ using Microsoft.Extensions.FileProviders; using Microsoft.AspNetCore.Mvc.ModelBinding; using HaWeb.Models; using HaWeb.XMLParser; -using HaWeb.XMLTests; using System.Xml.Linq; using System.Runtime.InteropServices; -using System.Diagnostics; using Microsoft.Extensions.Primitives; // XMLProvider provides a wrapper around the available XML data on a FILE basis @@ -17,7 +15,7 @@ public class XMLFileProvider : IXMLFileProvider { private IFileProvider _hamannFileProvider; private IFileProvider _bareRepositoryFileProvider; private IFileProvider _workingTreeFileProvider; - + public event EventHandler FileChange; public event EventHandler ConfigReload; public event EventHandler NewState; @@ -28,7 +26,7 @@ public class XMLFileProvider : IXMLFileProvider { private List? _WorkingTreeFiles; private List? _HamannFiles; - + private GitState? _GitState; private System.Timers.Timer? _changeTokenTimer; @@ -42,13 +40,13 @@ public class XMLFileProvider : IXMLFileProvider { _URL = config.GetValue("RepositoryURL"); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _hamannFileProvider = new PhysicalFileProvider(config.GetValue("HamannFileStoreWindows")); - _bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue("BareRepositoryPathWindows")); - _workingTreeFileProvider = new PhysicalFileProvider(config.GetValue("WorkingTreePathWindows")); - } + _bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue("BareRepositoryPathWindows")); + _workingTreeFileProvider = new PhysicalFileProvider(config.GetValue("WorkingTreePathWindows")); + } else { _hamannFileProvider = new PhysicalFileProvider(config.GetValue("HamannFileStoreLinux")); _bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue("BareRepositoryPathLinux")); - _workingTreeFileProvider = new PhysicalFileProvider(config.GetValue("WorkingTreePathLinux")); + _workingTreeFileProvider = new PhysicalFileProvider(config.GetValue("WorkingTreePathLinux")); } // Create File Lists; Here and in xmlservice, which does preliminary checking @@ -58,16 +56,16 @@ public class XMLFileProvider : IXMLFileProvider { xmlservice.SetState(state); } _HamannFiles = _ScanHamannFiles(); - + _RegisterChangeToken(); - // Check if hamann file already is current working tree status + // Check if hamann file already is current working tree status // -> YES: Load up the file via _lib.SetLibrary(); if (_IsAlreadyParsed()) { _Lib.SetLibrary(_HamannFiles!.First(), null, null); if (_Lib.GetLibrary() != null) return; } - // -> NO: Try to create a new file + // -> NO: Try to create a new file var created = _XMLService.TryCreate(_XMLService.GetState()); if (created != null) { var file = SaveHamannFile(created, _hamannFileProvider.GetFileInfo("./").PhysicalPath, null); @@ -82,11 +80,11 @@ public class XMLFileProvider : IXMLFileProvider { _Lib.SetLibrary(_HamannFiles.First(), null, null); if (_Lib.GetLibrary() != null) return; } - + // -> There is none? Use Fallback: else { var options = new HaWeb.Settings.HaDocumentOptions(); - if (_Lib.SetLibrary(null, null, null) == null) { + if (_Lib.SetLibrary(null, null, null) == null) { throw new Exception("Die Fallback Hamann.xml unter " + options.HamannXMLFilePath + " kann nicht geparst werden."); } } @@ -116,7 +114,7 @@ public class XMLFileProvider : IXMLFileProvider { if (_Lib.GetLibrary() != null) return; } - // -> NO: Try to create a new file + // -> NO: Try to create a new file var created = _XMLService.TryCreate(_XMLService.GetState()); if (created != null) { var file = SaveHamannFile(created, _hamannFileProvider.GetFileInfo("./").PhysicalPath, null); @@ -131,11 +129,11 @@ public class XMLFileProvider : IXMLFileProvider { _Lib.SetLibrary(_HamannFiles.First(), null, null); if (_Lib.GetLibrary() != null) return; } - + // -> There is none? Use Fallback: else { var options = new HaWeb.Settings.HaDocumentOptions(); - if (_Lib.SetLibrary(null, null, null) == null) { + if (_Lib.SetLibrary(null, null, null) == null) { throw new Exception("Die Fallback Hamann.xml unter " + options.HamannXMLFilePath + " kann nicht geparst werden."); } } @@ -165,7 +163,7 @@ public class XMLFileProvider : IXMLFileProvider { public IFileInfo? SaveHamannFile(XElement element, string basefilepath, ModelStateDictionary? ModelState) { if (_GitState == null) return null; - var filename = "hamann_" + _GitState.PullTime.Year + "-" + _GitState.PullTime.Month + "-" + _GitState.PullTime.Day + "_" + _GitState.PullTime.Hour + "-" + _GitState.PullTime.Minute + "." + _GitState.Commit.Substring(0,7) + ".xml"; + var filename = "hamann_" + _GitState.PullTime.Year + "-" + _GitState.PullTime.Month + "-" + _GitState.PullTime.Day + "_" + _GitState.PullTime.Hour + "-" + _GitState.PullTime.Minute + "." + _GitState.Commit.Substring(0, 7) + ".xml"; var path = Path.Combine(basefilepath, filename); try { @@ -173,7 +171,8 @@ public class XMLFileProvider : IXMLFileProvider { Directory.CreateDirectory(basefilepath); using (var targetStream = System.IO.File.Create(path)) element.Save(targetStream, SaveOptions.DisableFormatting); - } catch (Exception ex) { + } + catch (Exception ex) { if (ModelState != null) ModelState.AddModelError("Error", "Die Datei konnte nicht gespeichert werden: " + ex.Message); return null; } @@ -237,7 +236,7 @@ public class XMLFileProvider : IXMLFileProvider { private bool _IsAlreadyParsed() { if (_HamannFiles == null || !_HamannFiles.Any() || _GitState == null) return false; var fhash = _GetHashFromHamannFilename(_HamannFiles.First().Name); - var ghash = _GitState.Commit.Substring(0,7); + var ghash = _GitState.Commit.Substring(0, 7); return fhash == ghash; } @@ -261,8 +260,8 @@ public class XMLFileProvider : IXMLFileProvider { _XMLService.SetState(state); OnNewState(state); } - - // -> Try to create a new file + + // -> Try to create a new file var created = _XMLService.TryCreate(_XMLService.GetState()); if (created != null) { var file = SaveHamannFile(created, _hamannFileProvider.GetFileInfo("./").PhysicalPath, null); @@ -285,7 +284,7 @@ public class XMLFileProvider : IXMLFileProvider { protected virtual void OnFileChange(GitState? state) { EventHandler eh = FileChange; eh?.Invoke(this, state); - } + } protected virtual void OnNewState(XMLParsingState? state) { EventHandler eh = NewState; @@ -301,4 +300,4 @@ public class XMLFileProvider : IXMLFileProvider { EventHandler eh = NewData; eh?.Invoke(this, System.EventArgs.Empty); } -} \ No newline at end of file +} diff --git a/HaWeb/Program.cs b/HaWeb/Program.cs index f8190da..8cd75b3 100644 --- a/HaWeb/Program.cs +++ b/HaWeb/Program.cs @@ -53,7 +53,7 @@ var app = builder.Build(); // // Websockets for realtime notification of changes app.UseWebSockets(new WebSocketOptions { - KeepAliveInterval = TimeSpan.FromMinutes(180), + KeepAliveInterval = TimeSpan.FromMinutes(30), }); app.UseMiddleware(); diff --git a/HaWeb/appsettings.Development.json b/HaWeb/appsettings.Development.json index b8abe39..a81506a 100644 --- a/HaWeb/appsettings.Development.json +++ b/HaWeb/appsettings.Development.json @@ -13,13 +13,13 @@ }, "AllowedWebSocketConnections": "*", "AllowedHosts": "*", - "HamannFileStoreLinux": "/home/simon/test/", + "HamannFileStoreLinux": "/home/simon/source/hamann-ausgabe-core/HaWeb/testdata/", "HamannFileStoreWindows": "C:/Users/simon/Downloads/test/", "BareRepositoryPathLinux": "/home/simon/source/hamann-xml/.git/", "BareRepositoryPathWindows": "D:/Simon/source/hamann-xml/.git/", "WorkingTreePathLinux": "/home/simon/source/hamann-xml/", "WorkingTreePathWindows": "D:/Simon/source/hamann-xml/", - "RepositoryBranch": "Release", + "RepositoryBranch": "Main", "RepositoryURL": "https://github.com/Theodor-Springmann-Stiftung/hamann-xml", "StoredPDFPathWindows": "", "StoredPDFPathLinux": "", @@ -27,4 +27,4 @@ "AvailableStartYear": 1700, "AvailableEndYear": 1800, "LettersOnPage": 80 -} \ No newline at end of file +} diff --git a/HaWeb/wwwroot/js/marginals.mjs b/HaWeb/wwwroot/js/marginals.mjs index 578ec26..14312ad 100644 --- a/HaWeb/wwwroot/js/marginals.mjs +++ b/HaWeb/wwwroot/js/marginals.mjs @@ -1,210 +1,221 @@ // Script for auto collapsing marginal boxes const startup_marginals = function () { - let debounce_resize; - let collapsedboxes = []; + let debounce_resize; + let collapsedboxes = []; - const getLineHeight = function (element) { - var temp = document.createElement(element.nodeName), - ret; - temp.setAttribute("class", element.className); - temp.innerHTML = "Üj"; - element.parentNode.appendChild(temp); - ret = temp.getBoundingClientRect().height; - temp.parentNode.removeChild(temp); - return ret; - }; + const getLineHeight = function (element) { + var temp = document.createElement(element.nodeName), + ret; + temp.setAttribute("class", element.className); + temp.innerHTML = "Üj"; + element.parentNode.appendChild(temp); + ret = temp.getBoundingClientRect().height; + temp.parentNode.removeChild(temp); + return ret; + }; - const collapsebox = function (element, height, lineheight) { - element.style.maxHeight = height + "px"; - element.classList.add("ha-collapsed-box"); - element.classList.remove("ha-expanded-box"); - setTimeout(function () { - element.classList.remove("transition-all"); - }, 130); - }; + const collapsebox = function (element, height, lineheight) { + element.style.maxHeight = height + "px"; + element.classList.add("ha-collapsed-box"); + element.classList.remove("ha-expanded-box"); + setTimeout(function () { + element.classList.remove("transition-all"); + }, 130); + element.style.bottom = "unset"; + }; - const uncollapsebox = function (element) { - element.classList.add("transition-all"); - element.classList.remove("ha-collapsed-box"); - element.classList.add("ha-expanded-box"); - }; + const uncollapsebox = function (element, topmove = false) { + element.classList.add("transition-all"); + element.classList.remove("ha-collapsed-box"); + element.classList.add("ha-expanded-box"); + if (topmove > 0) element.style.bottom = "5px"; + }; - const addbuttoncaollapsebox = function (element, height, hoverfunction, topmove) { - let btn = document.createElement("div"); - btn.classList.add("ha-btn-collapsed-box"); + const addbuttoncaollapsebox = function ( + element, + height, + hoverfunction, + topmove = false, + ) { + let btn = document.createElement("div"); + btn.classList.add("ha-btn-collapsed-box"); - if (element.classList.contains("ha-collapsed-box")) { - btn.classList.add("ha-open-btn-collapsed-box"); - } else { - btn.classList.add("ha-close-btn-collapsed-box"); - } + if (element.classList.contains("ha-collapsed-box")) { + btn.classList.add("ha-open-btn-collapsed-box"); + } else { + btn.classList.add("ha-close-btn-collapsed-box"); + } - btn.addEventListener("click", function (ev) { - ev.stopPropagation(); - if (element.classList.contains("ha-collapsed-box")) { - uncollapsebox(element); - if (topmove > 0) element.style.bottom = "5px"; - btn.classList.add("ha-close-btn-collapsed-box"); - btn.classList.add("ha-collapsed-box-manually-toggled"); - } else { - collapsebox(element, height, 0); - - btn.classList.remove("ha-close-btn-collapsed-box"); - btn.classList.remove("ha-collapsed-box-manually-toggled"); - } - }); - - if (hoverfunction) { - let timer = null; - - element.addEventListener("mouseenter", function (ev) { - ev.stopPropagation(); - timer = setTimeout(function () { - if (element.classList.contains("ha-collapsed-box")) { - uncollapsebox(element); - if (topmove > 0) element.style.bottom = "5px"; - btn.classList.add("ha-close-btn-collapsed-box"); - } - }, 80); - }); - - element.addEventListener("mouseleave", function (ev) { - ev.stopPropagation(); - if (timer != null) { - clearTimeout(timer); - } - if ( - element.classList.contains("ha-expanded-box") && - !btn.classList.contains("ha-collapsed-box-manually-toggled") - ) { - collapsebox(element, height, 0); - btn.classList.remove("ha-close-btn-collapsed-box"); - } - }); - } - element.parentNode.insertBefore(btn, element); - }; - - const overlappingcollapsebox = function (selector, hoverfunction, containerid) { - let container = document.getElementById(containerid); - if (!container) return; - container.classList.add("overflow-hidden"); - let containerrect = document.getElementById(containerid).getBoundingClientRect();; - let boxes = document.querySelectorAll(selector); - let lineheight = 1; - - if (boxes.length >= 1) { - lineheight = getLineHeight(boxes[0]); - } - - for (var i = 0; i < boxes.length; i++) { - let element = boxes[i]; - let thisrect = element.getBoundingClientRect(); - let overlap = -2; - let topmove = 0; - if (thisrect.bottom > containerrect.bottom) { - overlap = thisrect.bottom - containerrect.bottom; - topmove = thisrect.bottom - containerrect.bottom; - console.log("topmove", topmove); - } else if (i < boxes.length - 1) { - let nextrect = boxes[i + 1].getBoundingClientRect(); - overlap = thisrect.bottom - nextrect.top; - } - if ( - // -1 for catching lines that perfectly close up on each other - overlap >= -1 && - !(window.getComputedStyle(element).display === "none") - ) { - let newlength = 0; - if (overlap >= 0) - newlength = thisrect.height - overlap; - else - newlength = thisrect.height - lineheight; - if (newlength % (lineheight * 3) <= 2) - newlength -= lineheight; - let remainder = newlength % lineheight; - newlength = newlength - remainder; - - // Line clamping for Marginals - if (element.classList.contains("ha-marginalbox")) { - let marginals = element.querySelectorAll(".ha-marginal"); - let h = 0; - for (let m of marginals) { - let cr = m.getBoundingClientRect(); - let eh = cr.bottom - cr.top; - h += eh; - if (h >= newlength) { - let lines = Math.floor(eh / lineheight); - let cutoff = Math.floor((h - newlength) / lineheight); - m.style.cssText += "-webkit-line-clamp: " + (lines - cutoff) + ";"; - m.style.cssText += "line-clamp: " + (lines - cutoff) + ";"; - } - } - } - - requestAnimationFrame(() => { - collapsedboxes.push(element); - collapsebox(element, newlength, lineheight); - addbuttoncaollapsebox(element, newlength, hoverfunction, topmove); - }); - - } - } - }; - - const marginalboxwidthset = function (containerid, classes) { - let lt = document.getElementById(containerid); - if (lt !== null) { - let mg = lt.querySelectorAll(classes); - if (mg.length > 0) { - let ltbcr = lt.getBoundingClientRect(); - let mgbcr = mg[0].getBoundingClientRect(); - let nw = ltbcr.right - mgbcr.left - 20; - - for (let element of mg) { - element.style.width = nw + "px"; - } - } - } - }; - - const clearcollapsedboxes = function () { - let elements = document.querySelectorAll(".ha-marginalbox"); - elements.forEach(element => { - element.removeAttribute("style"); - }); - collapsedboxes.forEach(element => { - element.classList.remove("ha-expanded-box"); - element.classList.remove("ha-collapsed-box"); - element.outerHTML = element.outerHTML; - }); - collapsedboxes = []; - elements = document.querySelectorAll(".ha-btn-collapsed-box"); - elements.forEach(element => { - element.remove(); - }); - }; - - const resetall = function () { - clearcollapsedboxes(); - marginalboxwidthset("ha-letterbody", ".ha-marginalbox"); - marginalboxwidthset("ha-register-body", ".ha-letlinks"); - startup_marginals(); - }; - - const collapseboxes = function () { - overlappingcollapsebox(".ha-letlinks", true, "ha-register-body"); - overlappingcollapsebox(".ha-marginalbox", true, "ha-letterbody"); - }; - - window.addEventListener("resize", function () { - clearTimeout(debounce_resize); - debounce_resize = setTimeout(resetall, 17); + btn.addEventListener("click", function (ev) { + ev.stopPropagation(); + if (element.classList.contains("ha-collapsed-box")) { + uncollapsebox(element, topmove); + btn.classList.add("ha-close-btn-collapsed-box"); + btn.classList.add("ha-collapsed-box-manually-toggled"); + } else { + collapsebox(element, height, 0); + btn.classList.remove("ha-close-btn-collapsed-box"); + btn.classList.remove("ha-collapsed-box-manually-toggled"); + } }); + if (hoverfunction) { + let timer = null; + + element.addEventListener("mouseenter", function (ev) { + ev.stopPropagation(); + if (timer != null) { + clearTimeout(timer); + } + if (element.classList.contains("ha-collapsed-box")) { + uncollapsebox(element, topmove); + btn.classList.add("ha-close-btn-collapsed-box"); + } + }); + + element.addEventListener("mouseleave", function (ev) { + ev.stopPropagation(); + timer = setTimeout(function () { + if ( + element.classList.contains("ha-expanded-box") && + !btn.classList.contains("ha-collapsed-box-manually-toggled") + ) { + collapsebox(element, height, 0); + btn.classList.remove("ha-close-btn-collapsed-box"); + } + }, 200); + }); + } + element.parentNode.insertBefore(btn, element); + }; + + const overlappingcollapsebox = function ( + selector, + hoverfunction, + containerid, + ) { + let container = document.getElementById(containerid); + if (!container) return; + let containerrect = document + .getElementById(containerid) + .getBoundingClientRect(); + let boxes = document.querySelectorAll(selector); + let lineheight = 1; + + if (boxes.length >= 1) { + lineheight = getLineHeight(boxes[0]); + } + + for (var i = 0; i < boxes.length; i++) { + let element = boxes[i]; + let thisrect = element.getBoundingClientRect(); + let overlap = -2; + let topmove = false; + // Check if this works + + if (i < boxes.length - 1) { + let nextrect = boxes[i + 1].getBoundingClientRect(); + overlap = thisrect.bottom - nextrect.top; + } else if (thisrect.bottom > containerrect.bottom) { + overlap = thisrect.bottom - containerrect.bottom; + } + + if (thisrect.bottom > containerrect.bottom) { + topmove = true; + } + + if ( + // -1 for catching lines that perfectly close up on each other + overlap >= -1 && + !(window.getComputedStyle(element).display === "none") + ) { + let newlength = 0; + if (overlap >= 0) newlength = thisrect.height - overlap; + else newlength = thisrect.height - lineheight; + if (newlength % (lineheight * 3) <= 2) newlength -= lineheight; + let remainder = newlength % lineheight; + newlength = newlength - remainder; + + // Line clamping for Marginals + if (element.classList.contains("ha-marginalbox")) { + let marginals = element.querySelectorAll(".ha-marginal"); + let h = 0; + for (let m of marginals) { + let cr = m.getBoundingClientRect(); + let eh = cr.bottom - cr.top; + h += eh; + if (h >= newlength) { + let lines = Math.floor(eh / lineheight); + let cutoff = Math.floor((h - newlength) / lineheight); + m.style.cssText += + "-webkit-line-clamp: " + (lines - cutoff) + ";"; + m.style.cssText += "line-clamp: " + (lines - cutoff) + ";"; + } + } + } + + requestAnimationFrame(() => { + collapsedboxes.push(element); + collapsebox(element, newlength, lineheight); + addbuttoncaollapsebox(element, newlength, hoverfunction, topmove); + }); + } + } + }; + + const marginalboxwidthset = function (containerid, classes) { + let lt = document.getElementById(containerid); + if (lt !== null) { + let mg = lt.querySelectorAll(classes); + if (mg.length > 0) { + let ltbcr = lt.getBoundingClientRect(); + let mgbcr = mg[0].getBoundingClientRect(); + let nw = ltbcr.right - mgbcr.left - 20; + + for (let element of mg) { + element.style.width = nw + "px"; + } + } + } + }; + + const clearcollapsedboxes = function () { + let elements = document.querySelectorAll(".ha-marginalbox"); + elements.forEach((element) => { + element.removeAttribute("style"); + }); + collapsedboxes.forEach((element) => { + element.classList.remove("ha-expanded-box"); + element.classList.remove("ha-collapsed-box"); + element.outerHTML = element.outerHTML; + }); + collapsedboxes = []; + elements = document.querySelectorAll(".ha-btn-collapsed-box"); + elements.forEach((element) => { + element.remove(); + }); + }; + + const resetall = function () { + clearcollapsedboxes(); marginalboxwidthset("ha-letterbody", ".ha-marginalbox"); marginalboxwidthset("ha-register-body", ".ha-letlinks"); - collapseboxes(); + startup_marginals(); + }; + + const collapseboxes = function () { + overlappingcollapsebox(".ha-letlinks", true, "ha-register-body"); + overlappingcollapsebox(".ha-marginalbox", true, "ha-letterbody"); + }; + + window.addEventListener("resize", function () { + clearTimeout(debounce_resize); + debounce_resize = setTimeout(resetall, 17); + }); + + marginalboxwidthset("ha-letterbody", ".ha-marginalbox"); + marginalboxwidthset("ha-register-body", ".ha-letlinks"); + collapseboxes(); }; export { startup_marginals };