diff --git a/src/main/java/nl/imvertor/OfficeCompiler/OfficeCompiler.java b/src/main/java/nl/imvertor/OfficeCompiler/OfficeCompiler.java
index 7ff35188b..ecc2ec28e 100644
--- a/src/main/java/nl/imvertor/OfficeCompiler/OfficeCompiler.java
+++ b/src/main/java/nl/imvertor/OfficeCompiler/OfficeCompiler.java
@@ -25,7 +25,6 @@
import java.util.Iterator;
import java.util.Vector;
-import org.apache.commons.exec.CommandLine;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.jgit.transport.PushResult;
@@ -37,10 +36,12 @@
import nl.imvertor.common.file.AnyFile;
import nl.imvertor.common.file.AnyFolder;
import nl.imvertor.common.file.FtpFolder;
+import nl.imvertor.common.file.WordFile;
+import nl.imvertor.common.file.XmlFile;
import nl.imvertor.common.file.ZipFile;
import nl.imvertor.common.git.ResourcePusher;
-import nl.imvertor.common.helper.OsExecutor;
-import nl.imvertor.common.helper.OsExecutor.OsExecutorResultHandler;
+import nl.imvertor.common.xsl.extensions.ImvertorCalculateHashlabel;
+import nl.imvertor.common.xsl.extensions.expath.ImvertorExpathWriteBinary;
public class OfficeCompiler extends Step {
@@ -49,6 +50,9 @@ public class OfficeCompiler extends Step {
public static final String STEP_NAME = "OfficeCompiler";
public static final String VC_IDENTIFIER = "$Id: OfficeCompiler.java 7457 2016-03-05 08:43:43Z arjan $";
+ private AnyFolder workFolder;
+ private AnyFolder moduleFolder;
+
public boolean run() throws Exception{
// set up the configuration for this step
@@ -79,7 +83,9 @@ public void generateOfficeReport() throws Exception {
if (op.equals("html")) {
runner.info(logger,"Creating documentation");
Transformer transformer = new Transformer();
-
+ transformer.setExtensionFunction(new ImvertorCalculateHashlabel());
+ transformer.setExtensionFunction(new ImvertorExpathWriteBinary());
+
boolean succeeds = true;
// append codelists and reference lists to imvertor file when referenced and when required.
@@ -93,10 +99,12 @@ public void generateOfficeReport() throws Exception {
succeeds = succeeds ? transformer.transformStep("properties/WORK_LISTS_FILE","properties/WORK_MODELDOC_FILE", "properties/IMVERTOR_METAMODEL_" + dr + "_MODELDOC_XSLPATH") : false;
*/
int i = 1;
+ String lastModeldocFile = "";
while (true) {
String xslname = "IMVERTOR_METAMODEL_" + dr + "_MODELDOC_XSLPATH" + ((i == 1) ? "" : ("_" + i));
String outname = "WORK_MODELDOC_FILE" + ((i == 1) ? "" : ("_" + i));
if (configurator.getParm("properties", xslname, false) != null) {
+ lastModeldocFile = configurator.getXParm("properties/" + outname); // onthoud welk file als laatste in de reeks is gegenereerd
succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/" + outname, "properties/" + xslname, "system/cur-imvertor-filepath") : false ;
i += 1;
} else if (i == 0) {
@@ -107,13 +115,14 @@ public void generateOfficeReport() throws Exception {
break;
}
- // variants may be "office" or "respec"
Vector vr = Configurator.split(configurator.getXParm("cli/createofficevariant"),"\\s+");
- if (vr.contains("msword") || vr.contains("respec")) {
+ if (vr.contains("msword") || vr.contains("respec") || vr.contains("documentor")) {
String template = configurator.getXParm("cli/officename"); // e.g. resolved [project-name]-[application-name]-[phase]-[release]
String fn = configurator.mergeParms(template);
+ configurator.setXParm("system/officename-resolved", fn); // resolved, dus bijv. CM-Testmodel-1-20231114
+
if (vr.contains("msword")) {
succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/WORK_MSWORD_FILE", "properties/IMVERTOR_METAMODEL_" + dr + "_MODELDOC_MSWORD_XSLPATH") : false;
if (succeeds) processDoc(fn,"msword.html","appinfo/msword-documentation-filename","properties/WORK_MSWORD_FILE","none");
@@ -124,45 +133,109 @@ public void generateOfficeReport() throws Exception {
if (mswordFolder.isDirectory())
mswordFolder.copy(new AnyFolder(configurator.getXParm("system/work-cat-folder-path") + "/msword"));
}
- }
- if (vr.contains("respec")) {
- if (configurator.isTrue("cli","fullrespec", false)) {
- // process complete report
- transformer.setXslParm("catalog-only", "false");
- succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/WORK_RESPEC_FILE", "properties/IMVERTOR_METAMODEL_" + dr + "_MODELDOC_RESPEC_XSLPATH") : false;
- if (succeeds) processDoc(fn,"respec.full.html","appinfo/full-respec-documentation-filename","properties/WORK_RESPEC_FILE","none");
- }
- // process catalog only, save as HTML
- transformer.setXslParm("catalog-only", "true");
- succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/WORK_RESPEC_FILE", "properties/IMVERTOR_METAMODEL_" + dr + "_MODELDOC_RESPEC_XSLPATH") : false;
- if (succeeds) processDoc(fn,"respec.html","appinfo/respec-documentation-filename","properties/WORK_RESPEC_FILE",configurator.getXParm("cli/passoffice",false));
+ }//msword
+ if (vr.contains("respec") || vr.contains("documentor")) {
+
// process catalog only, save as XHTML
succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/WORK_RESPEC_FILE", "properties/IMVERTOR_MODELDOC_RESPEC_XSLPATH") : false;
- if (succeeds) processDoc(fn,"respec.catalog.xhtml","appinfo/respec-documentation-filename","properties/WORK_RESPEC_FILE","none");
+ if (succeeds) processDoc(fn,"respec.catalog.xhtml","appinfo/catalog-documentation-filename","properties/WORK_RESPEC_FILE","none");
- // De laatste output is de XHTML catalogus; die staat centraal in documentor.
- /*
// als documentor info beschikbaar is, dan uitpakken en omzetten naar xhtml met Pandoc
- AnyFile docFile = new AnyFile(configurator.getXParm("cli/modeldocfile",false));
- if (docFile != null) {
- boolean isFolder = docFile.isDirectory();
+ String mdf = configurator.getXParm("cli/documentorfile",false);
+
+ if (mdf == null && vr.contains("documentor")) {
+ runner.warn(logger, "Documentor processing requested but no modeldoc folder passed");
+ succeeds = false;
+ }
+
+ if (succeeds && vr.contains("documentor")) {
+
+ // Er is documentor input in de vorm van modeldocs meegeleverd.
+
+ // Maak de workfolder en de module folder aan
+ workFolder = new AnyFolder(configurator.getWorkFolder("documentor"));
+ if (workFolder.isDirectory()) workFolder.deleteDirectory();
+ workFolder.mkdir();
+ configurator.setXParm("documentor/modeldoc-workfolder",workFolder.getCanonicalPath());
+
+ moduleFolder = new AnyFolder(workFolder,"module");
+ if (moduleFolder.isDirectory()) moduleFolder.deleteDirectory();
+ moduleFolder.mkdir();
+ configurator.setXParm("documentor/modeldoc-modulefolder",moduleFolder.getCanonicalPath());
+
+ // check het type modeldoc. In development: folder, in productie: zipfile
+ AnyFile docFile = new AnyFile(mdf);
boolean isZip = docFile.getExtension().equals("zip");
if (isZip) {
+ runner.debug(logger,"CHAIN","Extracting documentor files");
// alles uitpakken naar de workfolder
- AnyFolder docFolder = new AnyFolder(configurator.getWorkFolder("modeldoc"));
ZipFile zipFile = new ZipFile(docFile);
- zipFile.decompress(docFolder);
- // ga door deze files heen en zet ze om naar XHTML
- Iterator it = docFolder.listFilesToVector(false).iterator();
- while (it.hasNext()) {
- AnyFile f = new AnyFile(it.next());
- if (f.getExtension().equals("docx"))
- succeeds = succeeds ? transformDocx(f) : false ;
- }
+ zipFile.decompress(workFolder);
+ } else {
+ runner.debug(logger,"CHAIN","Copying documentor files");
+ // alles kopieren naar de workfolder
+ (new AnyFolder(docFile)).copy(workFolder);
+ }
+
+ // maak een kopie van alle *relevante* files in de workfolder en verzamel deze in de modulefolder.
+ String modelName = configurator.getXParm("appinfo/original-application-name");
+ succeeds = succeeds ? copyFilesToModulefolder(workFolder + "/modeldoc/" + modelName, modelName, true, true) : false;
+ succeeds = succeeds ? copyFilesToModulefolder(workFolder + "/sections", modelName, true, false) : false;
+
+ // de files zijn uitgelezen en omgezet naar XHTML
+ // nu de bestanden integreren, start bij het masterdoc, als dat er is -- masterdoc wordt bepaald bij het scannen van de files..
+ String masterdocPath = configurator.getXParm("documentor/masterdoc-path",false);
+ if (masterdocPath == null) {
+ runner.warn(logger, "Documentor processing requested but no modeldoc file \"" + modelName + "/" + modelName + ".docx\" found");
+ succeeds = false;
+ }
+ // kopieer het masterdoc naar de imvertor workfolder
+ if (succeeds) {
+ (new AnyFile(masterdocPath)).copyFile(configurator.getXParm("properties/IMVERTOR_DOCUMENTOR_CORESCANNER_FILE"));
+ succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/IMVERTOR_DOCUMENTOR_CORESCANNER_FILE", "properties/IMVERTOR_DOCUMENTOR_CORESCANNER_XSLPATH","system/cur-imvertor-filepath") : false;
+ succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/IMVERTOR_DOCUMENTOR_COREMODES_FILE", "properties/IMVERTOR_DOCUMENTOR_COREMODES_XSLPATH","system/cur-imvertor-filepath") : false;
+ succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/IMVERTOR_DOCUMENTOR_XHTMLTORESPEC_FILE", "properties/IMVERTOR_DOCUMENTOR_XHTMLTORESPEC_XSLPATH","system/cur-imvertor-filepath") : false;
+ }
+ if (succeeds) {
+
+ // kopieer documentor configuratie naar de cat folder. Eerst de standaard "Imvertor" files, en daaroverheen de owner files.
+ AnyFolder target = new AnyFolder(configurator.getWorkFolder() + "/app/cat/documentor");
+ target.mkdirs();
+ AnyFolder imvertorFolder = new AnyFolder(configurator.getBaseFolder(), "input" + File.separator + "Imvertor/cfg/docrules/documentor"); // waaronder default.css en default.js
+ imvertorFolder.copy(target);
+ AnyFolder ownerFolder = new AnyFolder(configurator.getInputFolder() + "/cfg/docrules/documentor");
+ if (ownerFolder.isDirectory())
+ ownerFolder.copy(target);
+ else
+ runner.warn(logger, "Documentor has not been configured for \""+ configurator.getXParm("cli/owner") +"\". Please contact your system administrator.");
+
+ // kopieer de gecachte versie van de respec config javascript naar de js folder
+ AnyFolder cacheFolder = new AnyFolder(configurator.getBaseFolder() + "/etc/respec/cache/" + configurator.getXParm("documentor/respec-config"));
+ AnyFolder jsFolder = new AnyFolder(target + "/js");
+ cacheFolder.copy(jsFolder);
}
}
- */
- }
+ configurator.setXParm("system/cur-imvertor-filepath", lastModeldocFile);
+
+ if (succeeds) {
+ // we hebben nu het hele document in respec format, met daarin de catalogus. Plaats dit document als body van het Respec resultaat document.
+ if (configurator.isTrue("cli","fullrespec", false)) {
+ // process complete report
+ transformer.setXslParm("catalog-only", "false");
+ succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/WORK_RESPEC_FILE", "properties/IMVERTOR_METAMODEL_" + dr + "_MODELDOC_RESPEC_XSLPATH") : false;
+
+ // als de fn is "index", vervang dan de extensie door (index.)html
+ String fullExt = (fn.equals("index")) ? "html" : "respec.full.html";
+ if (succeeds) processDoc(fn,fullExt,"appinfo/full-respec-documentation-filename","properties/WORK_RESPEC_FILE","none");
+ }
+
+ // process catalog only, save as HTML
+ transformer.setXslParm("catalog-only", "true");
+ succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/WORK_RESPEC_FILE", "properties/IMVERTOR_METAMODEL_" + dr + "_MODELDOC_RESPEC_XSLPATH") : false;
+ if (succeeds) processDoc(fn,"respec.html","appinfo/respec-documentation-filename","properties/WORK_RESPEC_FILE",configurator.getXParm("cli/passoffice",false));
+ }
+
+ }//respec
} else {
runner.error(logger,"No (valid) office variant specified: " + vr.toString());
succeeds = false;
@@ -276,8 +349,76 @@ private void passGIThub(File officeFile) throws Exception {
}
private boolean transformDocx(AnyFile mswordFile) throws Exception {
- //TODO
+ WordFile infile = new WordFile(mswordFile);
+ XmlFile outfile = new XmlFile(mswordFile.getCanonicalPath() + ".xhtml");
- return true;
+ runner.info(logger,"Processing " + infile.getName());
+
+ Transformer transformer = new Transformer();
+
+ boolean succeeds = true;
+
+ succeeds = succeeds ? infile.correctCodeSpaces() : false;
+
+ succeeds = succeeds? infile.toXhtmlFile(outfile) : false;
+
+ if (succeeds) {
+ // transformeer die XHTML naar iets bruikbaars, extraheer ook meteen respec properties
+ transformer.setXslParm("msword-file-path", outfile.getCanonicalPath());
+ transformer.setXslParm("msword-file-name", outfile.getNameNoExtension());
+ configurator.setXParm("system/cur-imvertor-filepath", outfile.getCanonicalPath());
+ succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/IMVERTOR_DOCUMENTOR_FILEPREPARE_FILE", "properties/IMVERTOR_DOCUMENTOR_FILEPREPARE_XSLPATH","system/cur-imvertor-filepath") : false;
+ succeeds = succeeds ? transformer.transformStep("system/cur-imvertor-filepath","properties/IMVERTOR_DOCUMENTOR_FILEFINALIZE_FILE", "properties/IMVERTOR_DOCUMENTOR_FILEFINALIZE_XSLPATH","system/cur-imvertor-filepath") : false;
+ // vervang het file met de aangepaste XHTML file
+ (new AnyFile(configurator.getXParm("properties/IMVERTOR_DOCUMENTOR_FILEFINALIZE_FILE"))).copyFile(outfile);
+ }
+ return succeeds;
}
+
+ private boolean copyFilesToModulefolder(String workSubFolderPath, String modelName, boolean recurse, boolean mustExist) throws Exception {
+
+ // workfolder is gemaakt; alle MsWord bestanden omzetten naar XHTML
+ AnyFolder workSubFolder = new AnyFolder(workSubFolderPath);
+ workSubFolderPath = workSubFolder.getCanonicalPath(); // forward slash correctie
+ if (workSubFolder.isDirectory()) {
+ // eerste slag: alle docx files transformeren naar standaard XHTML vorm.
+ Iterator it1 = workSubFolder.listFilesToVector(recurse).iterator();
+ boolean succeeds = true;
+ while (it1.hasNext()) {
+ String path = it1.next();
+ AnyFile f = new AnyFile(path);
+ Boolean isMasterDoc = f.getParent().equals(workSubFolderPath) && f.getName().equals(modelName + ".docx");
+ String fileUri = f.toURI().toString();
+ // Kies de te verwerken bestanden: het moet een docx file zijn, het is de masterdoc of het is een lokaal subdocument. Sla alle msword werkbestanden over.
+ if (f.getExtension().equals("docx") && (isMasterDoc || StringUtils.contains(fileUri,"/sections/")) && !StringUtils.startsWith(f.getName(),"~"))
+ succeeds = succeeds ? transformDocx(f) : false ;
+ }
+ // tweede slag: alles kopieren naar module folder
+ Iterator it2 = workSubFolder.listFilesToVector(recurse).iterator();
+ while (it2.hasNext()) {
+ AnyFile f = new AnyFile(it2.next());
+ String fileName = f.getName();
+ String fileUri = f.toURI().toString();
+
+ // Kopieer de msword resultaten en de include files naar de module folder.
+ if (f.isFile()) {
+ Boolean isWordFile = StringUtils.endsWith(fileName,".docx.xhtml");
+ Boolean isIncludeFile = StringUtils.contains(fileUri,"/include/");
+ if (isWordFile || isIncludeFile) {
+ String filepath = (isWordFile) ? moduleFolder + "/" + fileName : configurator.getWorkFolder() + "/app/cat/inc/" + fileName;
+ AnyFile moduleFile = new AnyFile(filepath);
+ if (moduleFile.isFile()) {
+ runner.error(logger, "Duplicate file name in documentor input: " + fileName);
+ succeeds = false;
+ } else
+ f.copyFile(moduleFile);
+ }
+ }
+ }
+
+ return succeeds;
+ } else
+ return !mustExist;
+ }
+
}
diff --git a/src/main/java/nl/imvertor/common/Configurator.java b/src/main/java/nl/imvertor/common/Configurator.java
index 63120f260..18ee6f288 100644
--- a/src/main/java/nl/imvertor/common/Configurator.java
+++ b/src/main/java/nl/imvertor/common/Configurator.java
@@ -250,6 +250,10 @@ public AnyFolder getBaseFolder() {
public AnyFolder getWorkFolder() {
return workFolder;
}
+
+ public AnyFolder getInputFolder() {
+ return inputFolder;
+ }
public AnyFolder getWorkFolder(String subfolderName) {
return new AnyFolder(workFolder,subfolderName);
@@ -1040,7 +1044,7 @@ private void loadFromPropertyFile(String filePath) throws Exception {
String optionName = e.nextElement().toString();
String value = properties.getProperty(optionName);
// process file properties in context of the current file
- if (optionName.equals("umlfile") | optionName.equals("zipfile") | optionName.equals("hisfile")) {
+ if (optionName.equals("umlfile") | optionName.equals("zipfile") | optionName.equals("hisfile") | optionName.equals("documentorfile")) {
File parent = (new File(filePath)).getParentFile();
if (AnyFile.isAbsolutePath(value))
value = (new File(value)).getCanonicalPath();
diff --git a/src/main/java/nl/imvertor/common/file/HttpFile.java b/src/main/java/nl/imvertor/common/file/HttpFile.java
index 9e1bde6f7..e1ac7755d 100644
--- a/src/main/java/nl/imvertor/common/file/HttpFile.java
+++ b/src/main/java/nl/imvertor/common/file/HttpFile.java
@@ -5,6 +5,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -12,6 +13,7 @@
import java.util.Map;
import java.util.Map.Entry;
+import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
@@ -220,13 +222,7 @@ public int getStatus() {
return status;
}
public String getResponseBody(HttpResponse response) throws Exception, IOException {
- BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
- String inputLine;
- StringBuffer responseText = new StringBuffer();
- while ((inputLine = in.readLine()) != null)
- responseText.append(inputLine);
- in.close();
- return responseText.toString();
+ return IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
}
}
diff --git a/src/main/java/nl/imvertor/common/file/WordFile.java b/src/main/java/nl/imvertor/common/file/WordFile.java
new file mode 100644
index 000000000..33469b9de
--- /dev/null
+++ b/src/main/java/nl/imvertor/common/file/WordFile.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 Dienst voor het kadaster en de openbare registers
+ *
+ * This file is part of Imvertor.
+ *
+ * Imvertor is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Imvertor is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Imvertor. If not, see .
+ *
+ */
+
+package nl.imvertor.common.file;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Base64;
+import java.util.HashMap;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+
+import nl.imvertor.common.Configurator;
+import nl.imvertor.common.Runner;
+import nl.imvertor.common.Transformer;
+import nl.imvertor.common.helper.OsExecutor;
+import nl.imvertor.common.helper.OsExecutor.OsExecutorResultHandler;
+
+public class WordFile extends AnyFile {
+
+ public static final Logger logger = Logger.getLogger(ExecFile.class);
+ private static final long serialVersionUID = 2409879811971148189L;
+
+ public WordFile(String filepath) {
+ super(filepath);
+ }
+ public WordFile(File file) {
+ super(file.getAbsolutePath());
+ }
+ public WordFile(File file, String subpath) {
+ super(file, subpath);
+ }
+
+ /**
+ * Transform the Doc file to XHTML structure.
+ * This uses Pandoc.
+ *
+ * @return XmlFile
+ * @param filePath
+ * @throws Exception
+ */
+ public boolean toXhtmlFile(XmlFile outFile) throws Exception {
+
+ Configurator configurator = Configurator.getInstance();
+ Runner runner = configurator.getRunner();
+ boolean debugging = runner.getDebug("DOCUMENTOR");
+
+ String pandocServerUrl = configurator.getServerProperty("pandoc.server", false);
+ if (pandocServerUrl != null) {
+ // gebruik de pandoc server: https://pandoc.org/pandoc-server.html
+
+ HttpFile localFile = new HttpFile(this);
+
+ HashMap headerMap = new HashMap();
+ //headerMap.put(HttpHeaders.AUTHORIZATION,"token " + OAUTH);
+ headerMap.put(HttpHeaders.ACCEPT, "application/octet-stream");
+ headerMap.put(HttpHeaders.CONTENT_TYPE, "application/json");
+ headerMap.put(HttpHeaders.CONTENT_ENCODING, getEncoding());
+
+ String payloadBase64 = Base64.getEncoder().encodeToString(this.getBinaryContent());
+ String payload = "{"
+ + "\"from\": \"docx+styles\","
+ + "\"to\": \"html\","
+ + "\"indented-code-classes\": ["
+ + "\"Programmacode\""
+ + "],"
+ + "\"section-divs\": true,"
+ + "\"embed-resources\": true,"
+ //+ "\"ipynb-output\": \"all\","
+ + "\"standalone\": true,"
+ + "\"variables\": {"
+ + "\"lang\": \"nl-NL\","
+ + "\"title-meta\": \"NOTITLE\""
+ + "},"
+ + "\"text\": \"" + payloadBase64 + "\""
+ + "}";
+ try {
+ String result = localFile.post(HttpFile.METHOD_POST_CONTENT, URI.create(pandocServerUrl), headerMap, null, new String[] {payload});
+ if (StringUtils.startsWith(result,"<")) {
+ outFile.setContent(result);
+ return true;
+ } else {
+ runner.error(logger, "Documentor processing error: \"" + result + "\"");
+ return false;
+ }
+ } catch (Exception e) {
+ runner.error(logger, "Documentor server error: \"" + e.getMessage() + "\"");
+ }
+ return false;
+ } else {
+ // Implementatie van Pandoc omzetting naar XHTML.
+
+ OsExecutor osExecutor = new OsExecutor();
+
+ String toolloc = (new AnyFile(configurator.getServerProperty("documentor.msword.transformer"))).getCanonicalPath(); // location of the tool
+ long osExecutorJobTimeout = Long.parseLong(configurator.getServerProperty("documentor.msword.transformer.timeout")); // location of the tool
+ boolean osExecutorInBackground = false;
+
+ OsExecutorResultHandler osExecutorResult = null;
+
+ /*
+ * Dit batch file doet het volgende
+ * - Bereid MsWord voor door roep o.a. pandoc aan en corrigeert allerlei
+ */
+ CommandLine commandLine = new CommandLine(toolloc + "\\documentor.bat"); // TODO: *nix
+ commandLine.addArgument(this.getName()); // the docx file
+ commandLine.addArgument(toolloc); // the tool folder
+ commandLine.addArgument(this.getParent()); // The work folder
+ commandLine.addArgument(debugging ? "true" : "false"); // debugging?
+
+ try {
+ osExecutorResult = osExecutor.osexec(commandLine, osExecutorJobTimeout, osExecutorInBackground);
+ osExecutorResult.waitFor();
+ // assume the msword file * is transformed to *.xhtml
+ configurator.setXParm("appinfo/documentor-transformation-result", outFile.getName(),false);
+ return true;
+
+ } catch (Exception e) {
+ if (osExecutorResult != null)
+ runner.error(logger, "Documentor exit value " + osExecutorResult.getExitValue() + ". " + osExecutorResult.getException().getMessage());
+ else
+ runner.error(logger, e.getMessage());
+ }
+ }
+ return false;
+
+ }
+
+ /*
+ * Uitlezen van msword gaat niet altijd goed; in preserveSpace secties worden leading blanks niet goed verwerkt. Vervang deze door harde spaties.
+ */
+ public boolean correctCodeSpaces() throws Exception{
+ Configurator configurator = Configurator.getInstance();
+ try {
+ ZipFile thisFile = new ZipFile(this);
+ AnyFolder tempFolder = configurator.getWorkFolder("documentor/msword");
+ thisFile.decompress(tempFolder);
+ // run stylesheet om de spaties in code vast te zetten
+ XmlFile wordFile = new XmlFile(tempFolder,"/word/document.xml");
+ XmlFile outFile = new XmlFile(tempFolder,"/word/document.xml.transformed");
+ XslFile xslFile = new XslFile(configurator.getBaseFolder() + "/xsl/OfficeCompiler/Imvert2documentor-msword-fixes.xsl");
+ Transformer transformer = new Transformer();
+ transformer.transform(wordFile, outFile, xslFile, "filefix");
+ // zet het resultaat van de transformatie op de plek van het input document
+ outFile.copyFile(wordFile);
+ outFile.delete();
+ // Maak het MdWord bestand opnieuw aan door de tijdelijke folder weer te zippen.
+ thisFile.compress(tempFolder);
+ return true;
+ } catch (Exception e) {
+ configurator.getRunner().error(logger,"Cannot fix MsWord program code fragment(s)",e);
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/nl/imvertor/common/file/ZipFile.java b/src/main/java/nl/imvertor/common/file/ZipFile.java
index b368ced63..40b33caf3 100644
--- a/src/main/java/nl/imvertor/common/file/ZipFile.java
+++ b/src/main/java/nl/imvertor/common/file/ZipFile.java
@@ -21,19 +21,12 @@
package nl.imvertor.common.file;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
@@ -63,6 +56,7 @@ public ZipFile(String zipFilePath) throws IOException {
}
public ZipFile(File file) throws IOException {
super(file);
+ fileList = new ArrayList();
}
public ZipFile(File folder, String file) throws IOException {
super(folder, file);
@@ -79,9 +73,7 @@ public ZipFile(File folder, String file) throws IOException {
*/
public void compress(File folderToCompress) throws Exception {
if (folderToCompress.isDirectory()) {
- sourceFolder = folderToCompress.getAbsolutePath();
- generateFileList(folderToCompress);
- zipIt();
+ ZipUtils.zipDirectory(folderToCompress, this);
} else
throw new Exception("Source folder for zip is not a folder: " + folderToCompress);
}
@@ -94,113 +86,9 @@ public void compress(File folderToCompress) throws Exception {
* @throws Exception
*/
public void decompress(AnyFolder targetFolder) throws Exception {
- unZipIt(this.getAbsolutePath(),targetFolder.getAbsolutePath(),null);
- }
- public void decompress(AnyFolder targetFolder,Pattern requestedFilePattern) throws Exception {
- if (targetFolder.isFile())
- throw new Exception("Cannot decompress to a file: " + targetFolder);
- if (!targetFolder.exists() )
- targetFolder.mkdirs();
- unZipIt(this.getAbsolutePath(),targetFolder.getAbsolutePath(),requestedFilePattern);
+ ZipUtils.unzipFile(this, targetFolder);
}
-
- /**
- * Traverse a folder and get all files,
- * and add the file into fileList
- * @param node file or folder
- */
- // adapted from http://www.mkyong.com/java/how-to-compress-files-in-zip-format/
- private void generateFileList(File node){
-
- //add file only
- if(node.isFile()) {
- fileList.add(generateZipEntry(node.getAbsoluteFile().toString()));
- }
- if(node.isDirectory()){
- String[] subNote = node.list();
- for(String filename : subNote){
- generateFileList(new File(node, filename));
- }
- }
- }
-
- /**
- * Format the file path for zip
- * @param file file path
- * @return Formatted file path
- */
- private String generateZipEntry(String file){
- return file.substring(sourceFolder.length()+1, file.length());
- }
-
- /**
- * Zip it
- * @throws IOException
- */
- // adapted from http://www.mkyong.com/java/how-to-compress-files-in-zip-format/
- private void zipIt() throws IOException {
- byte[] buffer = new byte[1024];
- FileOutputStream fos = new FileOutputStream(this);
- ZipOutputStream zos = new ZipOutputStream(fos);
- for(String file : this.fileList){
- ZipEntry ze = new ZipEntry(file);
- zos.putNextEntry(ze);
- FileInputStream in = new FileInputStream(sourceFolder + File.separator + file);
- int len;
- while ((len = in.read(buffer)) > 0) {
- zos.write(buffer, 0, len);
- }
- in.close();
- }
- zos.closeEntry();
- zos.close();
- }
-
- /**
- * Unzip it
- * @param zipFile input zip file
- * @param outputFolder zip file output folder
- * @param requestedFilePattern Pattern to match the file name.
- *
- * @throws Exception
- */
- private void unZipIt(String zipFile, String outputFolder, Pattern requestedFilePattern) throws Exception{
-
- byte[] buffer = new byte[1024];
-
- //create output folder is not exists
- AnyFolder folder = new AnyFolder(outputFolder);
- folder.mkdir();
-
- //get the zip file content
- ZipInputStream zis =
- new ZipInputStream(new FileInputStream(zipFile));
- //get the zipped file list entry
- ZipEntry ze = zis.getNextEntry();
-
- while(ze!=null){
- if (!ze.isDirectory()) {
- String fileName = ze.getName();
- // if the pattern specified, use a matcher. Otherwise accept any file.
- Matcher m = (requestedFilePattern != null) ? requestedFilePattern.matcher(fileName) : null;
- if (requestedFilePattern == null || m.find()) {
- File newFile = new File(outputFolder + File.separator + fileName);
- //create all non exists folders
- //else you will hit FileNotFoundException for compressed folder
- new File(newFile.getParent()).mkdirs();
- FileOutputStream fos = new FileOutputStream(newFile);
- int len;
- while ((len = zis.read(buffer)) > 0) {
- fos.write(buffer, 0, len);
- }
- fos.close();
- }
- }
- ze = zis.getNextEntry();
- }
- zis.closeEntry();
- zis.close();
- }
+
/**
* Create an XML serialization in a folder that will hold
* 1/ a single xml file content.xml
diff --git a/src/main/java/nl/imvertor/common/file/ZipUtils.java b/src/main/java/nl/imvertor/common/file/ZipUtils.java
new file mode 100644
index 000000000..6490b4641
--- /dev/null
+++ b/src/main/java/nl/imvertor/common/file/ZipUtils.java
@@ -0,0 +1,135 @@
+package nl.imvertor.common.file;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+public class ZipUtils {
+
+ public static void zip(File directory, File base, ZipOutputStream zos) throws IOException {
+ File[] files = directory.listFiles();
+ byte[] buffer = new byte[8192];
+ int read = 0;
+ for (int i=0, n=files.length; i e = archive.entries();
+ while (e.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) e.nextElement();
+ File file = new File(extractTo, entry.getName());
+ if (entry.isDirectory()) {
+ if (!file.exists()) {
+ file.mkdirs();
+ } // else nothing to do
+ } else {
+ if (!file.getParentFile().exists()) {
+ file.getParentFile().mkdirs();
+ }
+ InputStream in = archive.getInputStream(entry);
+ BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
+ try {
+ byte[] buffer = new byte[8192];
+ int read;
+ while (-1 != (read = in.read(buffer))) {
+ out.write(buffer, 0, read);
+ }
+ } finally {
+ in.close();
+ out.close();
+ }
+ }
+ }
+ } finally {
+ archive.close();
+ }
+ }
+
+ public static void unzipStream(InputStream is, File extractTo) throws IOException {
+ byte[] buffer = new byte[8192];
+ int size;
+ ZipInputStream zis = new ZipInputStream(is);
+ ZipEntry entry;
+ try {
+ while ((entry = zis.getNextEntry()) != null) {
+ File file = new File(extractTo, entry.getName());
+ if (entry.isDirectory()) {
+ if (!file.exists()) {
+ file.mkdirs();
+ }
+ } else {
+ if (!file.getParentFile().exists()) {
+ file.getParentFile().mkdirs();
+ }
+ BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
+ try {
+ while ((size = zis.read(buffer, 0, buffer.length)) != -1) {
+ bos.write(buffer, 0, size);
+ }
+ bos.flush();
+ } finally {
+ zis.closeEntry();
+ bos.close();
+ }
+ }
+ }
+ } finally {
+ zis.close();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/nl/imvertor/common/xsl/extensions/ImvertorCalculateHashlabel.java b/src/main/java/nl/imvertor/common/xsl/extensions/ImvertorCalculateHashlabel.java
new file mode 100644
index 000000000..c98f75468
--- /dev/null
+++ b/src/main/java/nl/imvertor/common/xsl/extensions/ImvertorCalculateHashlabel.java
@@ -0,0 +1,90 @@
+/*
+ * $HeadURL$
+ * $LastChangedRevision$
+ * $LastChangedDate$
+ * $LastChangedBy$
+ */
+
+package nl.imvertor.common.xsl.extensions;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ExtensionFunctionCall;
+import net.sf.saxon.lib.ExtensionFunctionDefinition;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.StringValue;
+import nl.imvertor.common.Configurator;
+
+public class ImvertorCalculateHashlabel extends ExtensionFunctionDefinition {
+
+ private static final StructuredQName qName =
+ new StructuredQName("", Configurator.NAMESPACE_EXTENSION_FUNCTIONS, "imvertorCalculateHashLabel");
+
+ public StructuredQName getFunctionQName() {
+ return qName;
+ }
+
+ public int getMinimumNumberOfArguments() {
+ return 2;
+ }
+
+ public int getMaximumNumberOfArguments() {
+ return 2;
+ }
+
+ public SequenceType[] getArgumentTypes() {
+ return new SequenceType[] { SequenceType.SINGLE_STRING, SequenceType.SINGLE_STRING };
+ }
+
+ public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
+ return SequenceType.SINGLE_STRING;
+ }
+
+ public ExtensionFunctionCall makeCallExpression() {
+ return new HashlabelCall();
+ }
+
+ private static class HashlabelCall extends ExtensionFunctionCall {
+
+ /**
+ * Extension function.
+ * Create Hash on the string passed. Return a label (characters, digits,... based on the supplied pool of characters) or a full hash (digits) for the supplied string.
+ *
+ * @param string to create a hash label for
+ * @return string hash
+ * @throws Exception
+ */
+
+ /**
+ * Calculate Hashlabel on input data
+ */
+ public static String calculateHashlabel(String s,String characters) {
+ int hash = s.hashCode() & 0xfffffff; // Maak het positief, gegenereerde hashes kunnen negatief zijn. - https://stackoverflow.com/questions/33219638/how-to-make-a-hashcodeinteger-value-positive
+ if (characters.length() > 0)
+ return subtract(hash,"",characters);
+ else
+ return String.valueOf(hash);
+ }
+
+ private static String subtract(int seed, String result, String pool) {
+ int l = pool.length();
+ int m = seed % l;
+ int r = seed / l;
+ String c = pool.substring(m,m + 1);
+ if (r > 0)
+ return result + c + subtract(r,result,pool);
+ else
+ return result + c;
+ }
+
+ @Override
+ public Sequence call(XPathContext arg0, Sequence[] arguments) throws XPathException {
+ String s1 = arguments[0].head().getStringValue();
+ String s2 = arguments[1].head().getStringValue();
+ return StringValue.makeStringValue(calculateHashlabel(s1,s2));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/cfg/ConfigCompiler/parms.xml b/src/main/resources/cfg/ConfigCompiler/parms.xml
index c4fc9fab1..59c2df74d 100644
--- a/src/main/resources/cfg/ConfigCompiler/parms.xml
+++ b/src/main/resources/cfg/ConfigCompiler/parms.xml
@@ -27,7 +27,7 @@
owner
name
- Name of the project owner
+ The owner of the configurations that are applied in a single Imvertor OS run. Typically an organization name.
true
diff --git a/src/main/resources/cfg/OfficeCompiler/parms.xml b/src/main/resources/cfg/OfficeCompiler/parms.xml
index 031244a82..e6b7cb2ee 100644
--- a/src/main/resources/cfg/OfficeCompiler/parms.xml
+++ b/src/main/resources/cfg/OfficeCompiler/parms.xml
@@ -25,13 +25,13 @@
createoffice
- html|doc|none
+ html | doc | none
Create a documentation file in plain HTML, MsWord, or Respec format. By default, create none.
false
createofficevariant
- msword/respec
+ msword | respec | documentor
Specify the type of office file. Multiple formats are allowed, separate list by whitespace.
When createoffice is set to html
@@ -133,6 +133,14 @@
Yes if codelists and reference lists that have a location must be read dynamically from that location and included in the model documentation.
false
+
+
+ modeldocfile
+ filepath
+ Path to the folder or zipfile holding the modeldoc files, i.e. profile and MsWord files and images used to create a full Respec.
+ false
+ file
+
@@ -148,6 +156,21 @@
Imvert2modeldoc-xhtml-respec.xsl
+ Imvert2documentor-file-prepare.xsl
+ ${system/work-imvert-folder-path}/imvertor.26.1.documentor-file-prepare.xhtml
+
+ Imvert2documentor-file-finalize.xsl
+ ${system/work-imvert-folder-path}/imvertor.26.2.documentor-file-finalize.xhtml
+
+ Imvert2documentor-core-scanner.xsl
+ ${system/work-imvert-folder-path}/imvertor.26.3.documentor-core-scanner.xhtml
+
+ Imvert2documentor-core-modes.xsl
+ ${system/work-imvert-folder-path}/imvertor.26.4.documentor-core-modes.xhtml
+
+ Imvert2documentor-respec-xhtml-to-respec.xsl
+ ${system/work-imvert-folder-path}/imvertor.26.5.documentor-respec-xhtml-to-respec.xhtml
+
Imvert2modeldoc-KING-MIM-11-SIM.xsl
Imvert2modeldoc-KING-MIM-11-SIM-html-msword.xsl
@@ -252,6 +275,9 @@
Imvert2modeldoc-Kadaster-MIM-11-html-msword.xsl
Imvert2modeldoc-Kadaster-MIM-11-html-respec.xsl
+ Imvert2modeldoc-Armatiek-MIM-12-CONCEPTUAL.xsl
+ Imvert2modeldoc-Armatiek-MIM-12-CONCEPTUAL-html-respec.xsl
+
Imvert2modeldoc-EIGENAAR-MIM-12-CONCEPTUAL.xsl
Imvert2modeldoc-EIGENAAR-MIM-12-CONCEPTUAL-html-respec.xsl
Imvert2modeldoc-EIGENAAR-MIM-12-CONCEPTUAL-html-msword.xsl
diff --git a/src/main/resources/cfg/Validator/parms.xml b/src/main/resources/cfg/Validator/parms.xml
index 592733a9d..c2503ebeb 100644
--- a/src/main/resources/cfg/Validator/parms.xml
+++ b/src/main/resources/cfg/Validator/parms.xml
@@ -47,6 +47,12 @@
Yes if the notes fields that are required must be checked (for content)
true
+
+ allowemptymodel
+ yes|no
+ Yes if the model or any model domain may be empty; this is feasible in development phases
+ false
+
stub_requiresalias
yes|no
@@ -371,6 +377,22 @@
Imvert2validation-MIM-11.xsl
Imvert2validation-BRO-MIM-1-LOGICAL.xsl
+
+
+ Imvert2canonical.xsl
+ Imvert2canonical-MIM-10.xsl
+ Imvert2canonical-MIM-11.xsl
+ Imvert2canonical-MIM-12.xsl
+
+ Imvert2validation.xsl
+ Imvert2validation-MIM-10.xsl
+ Imvert2validation-MIM-11.xsl
+ Imvert2validation-MIM-12.xsl
+
- Imvert2canonical.xsl
- Imvert2canonical-MIM-10.xsl
- Imvert2canonical-MIM-11.xsl
- Imvert2canonical-MIM-12.xsl
-
- Imvert2validation.xsl
- Imvert2validation-MIM-10.xsl
- Imvert2validation-MIM-11.xsl
- Imvert2validation-MIM-12.xsl
-
|$))/,he=X("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))","i").replace("comment",fe).replace("tag",pe).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),me=X(ce).replace("hr",ie).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",pe).getRegex(),ge={blockquote:X(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",me).getRegex(),code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,def:ue,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:ie,html:he,lheading:ae,list:de,newline:/^(?: *(?:\n|$))+/,paragraph:me,table:ee,text:/^[^\n]+/},be=X("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",ie).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",pe).getRegex(),we={...ge,table:be,paragraph:X(ce).replace("hr",ie).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",be).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",pe).getRegex()},ye={...ge,html:X("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)| \\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",fe).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:ee,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:X(ce).replace("hr",ie).replace("heading"," *#{1,6} *[^\n]").replace("lheading",ae).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},ve=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,$e=/^( {2,}|\\)\n(?!\s*$)/,xe="\\p{P}\\p{S}",ke=X(/^((?![*_])[\spunctuation])/,"u").replace(/punctuation/g,xe).getRegex(),_e=X(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,"u").replace(/punct/g,xe).getRegex(),Ee=X("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])","gu").replace(/punct/g,xe).getRegex(),Ce=X("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])","gu").replace(/punct/g,xe).getRegex(),Se=X(/\\([punct])/,"gu").replace(/punct/g,xe).getRegex(),Le=X(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Re=X(fe).replace("(?:--\x3e|$)","--\x3e").getRegex(),Ae=X("^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",Re).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),Te=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Pe=X(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",Te).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),Ie=X(/^!?\[(label)\]\[(ref)\]/).replace("label",Te).replace("ref",le).getRegex(),De=X(/^!?\[(ref)\](?:\[\])?/).replace("ref",le).getRegex(),Ne={_backpedal:ee,anyPunctuation:Se,autolink:Le,blockSkip:/\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g,br:$e,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:ee,emStrongLDelim:_e,emStrongRDelimAst:Ee,emStrongRDelimUnd:Ce,escape:ve,link:Pe,nolink:De,punctuation:ke,reflink:Ie,reflinkSearch:X("reflink|nolink(?!\\()","g").replace("reflink",Ie).replace("nolink",De).getRegex(),tag:Ae,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\t+" ".repeat(n.length)));e;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some((r=>!!(n=r.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.space(e))e=e.substring(n.raw.length),1===n.raw.length&&t.length>0?t[t.length-1].raw+="\n":t.push(n);else if(n=this.tokenizer.code(e))e=e.substring(n.raw.length),r=t[t.length-1],!r||"paragraph"!==r.type&&"text"!==r.type?t.push(n):(r.raw+="\n"+n.raw,r.text+="\n"+n.text,this.inlineQueue[this.inlineQueue.length-1].src=r.text);else if(n=this.tokenizer.fences(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.heading(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.hr(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.blockquote(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.list(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.html(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.def(e))e=e.substring(n.raw.length),r=t[t.length-1],!r||"paragraph"!==r.type&&"text"!==r.type?this.tokens.links[n.tag]||(this.tokens.links[n.tag]={href:n.href,title:n.title}):(r.raw+="\n"+n.raw,r.text+="\n"+n.raw,this.inlineQueue[this.inlineQueue.length-1].src=r.text);else if(n=this.tokenizer.table(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.lheading(e))e=e.substring(n.raw.length),t.push(n);else{if(o=e,this.options.extensions&&this.options.extensions.startBlock){let t=1/0;const n=e.slice(1);let r;this.options.extensions.startBlock.forEach((e=>{r=e.call({lexer:this},n),"number"==typeof r&&r>=0&&(t=Math.min(t,r))})),t<1/0&&t>=0&&(o=e.substring(0,t+1))}if(this.state.top&&(n=this.tokenizer.paragraph(o)))r=t[t.length-1],i&&"paragraph"===r.type?(r.raw+="\n"+n.raw,r.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=r.text):t.push(n),i=o.length!==e.length,e=e.substring(n.raw.length);else if(n=this.tokenizer.text(e))e=e.substring(n.raw.length),r=t[t.length-1],r&&"text"===r.type?(r.raw+="\n"+n.raw,r.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=r.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n,r,o,i,s,a,c=e;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(i=this.tokenizer.rules.inline.reflinkSearch.exec(c));)e.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(c=c.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+c.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(i=this.tokenizer.rules.inline.blockSkip.exec(c));)c=c.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+c.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(i=this.tokenizer.rules.inline.anyPunctuation.exec(c));)c=c.slice(0,i.index)+"++"+c.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;e;)if(s||(a=""),s=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some((r=>!!(n=r.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.escape(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.tag(e))e=e.substring(n.raw.length),r=t[t.length-1],r&&"text"===n.type&&"text"===r.type?(r.raw+=n.raw,r.text+=n.text):t.push(n);else if(n=this.tokenizer.link(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.reflink(e,this.tokens.links))e=e.substring(n.raw.length),r=t[t.length-1],r&&"text"===n.type&&"text"===r.type?(r.raw+=n.raw,r.text+=n.text):t.push(n);else if(n=this.tokenizer.emStrong(e,c,a))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.codespan(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.br(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.del(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.autolink(e))e=e.substring(n.raw.length),t.push(n);else if(this.state.inLink||!(n=this.tokenizer.url(e))){if(o=e,this.options.extensions&&this.options.extensions.startInline){let t=1/0;const n=e.slice(1);let r;this.options.extensions.startInline.forEach((e=>{r=e.call({lexer:this},n),"number"==typeof r&&r>=0&&(t=Math.min(t,r))})),t<1/0&&t>=0&&(o=e.substring(0,t+1))}if(n=this.tokenizer.inlineText(o))e=e.substring(n.raw.length),"_"!==n.raw.slice(-1)&&(a=n.raw.slice(-1)),s=!0,r=t[t.length-1],r&&"text"===r.type?(r.raw+=n.raw,r.text+=n.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}else e=e.substring(n.raw.length),t.push(n);return t}}class Fe{options;constructor(e){this.options=e||q}code(e,t,n){const r=(t||"").match(/^\S*/)?.[0];return e=e.replace(/\n$/,"")+"\n",r?''+(n?e:Y(e,!0))+"
\n":""+(n?e:Y(e,!0))+"
\n"}blockquote(e){return`\n${e} \n`}html(e,t){return e}heading(e,t,n){return`${e} \n`}hr(){return" \n"}list(e,t,n){const r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"}listitem(e,t,n){return`${e} \n`}checkbox(e){return" '}paragraph(e){return`${e}
\n`}table(e,t){return t&&(t=`${t} `),"\n"}tablerow(e){return`\n${e} \n`}tablecell(e,t){const n=t.header?"th":"td";return(t.align?`<${n} align="${t.align}">`:`<${n}>`)+e+`${n}>\n`}strong(e){return`${e} `}em(e){return`${e} `}codespan(e){return`${e}
`}br(){return" "}del(e){return`${e}`}link(e,t,n){const r=J(e);if(null===r)return n;let o='"+n+" ",o}image(e,t,n){const r=J(e);if(null===r)return n;let o=` ",o}text(e){return e}}class Be{strong(e){return e}em(e){return e}codespan(e){return e}del(e){return e}html(e){return e}text(e){return e}link(e,t,n){return""+n}image(e,t,n){return""+n}br(){return""}}class He{options;renderer;textRenderer;constructor(e){this.options=e||q,this.options.renderer=this.options.renderer||new Fe,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new Be}static parse(e,t){return new He(t).parse(e)}static parseInline(e,t){return new He(t).parseInline(e)}parse(e,t=!0){let n="";for(let r=0;r0&&"paragraph"===n.tokens[0].type?(n.tokens[0].text=e+" "+n.tokens[0].text,n.tokens[0].tokens&&n.tokens[0].tokens.length>0&&"text"===n.tokens[0].tokens[0].type&&(n.tokens[0].tokens[0].text=e+" "+n.tokens[0].tokens[0].text)):n.tokens.unshift({type:"text",text:e+" "}):a+=e+" "}a+=this.parse(n.tokens,i),s+=this.renderer.listitem(a,o,!!r)}n+=this.renderer.list(s,t,r);continue}case"html":{const e=o;n+=this.renderer.html(e.text,e.block);continue}case"paragraph":{const e=o;n+=this.renderer.paragraph(this.parseInline(e.tokens));continue}case"text":{let i=o,s=i.tokens?this.parseInline(i.tokens):i.text;for(;r+1{const o=e[r].flat(1/0);n=n.concat(this.walkTokens(o,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let r=e.renderer.apply(this,t);return!1===r&&(r=n.apply(this,t)),r}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new Fe(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if("options"===n)continue;const r=n,o=e.renderer[r],i=t[r];t[r]=(...e)=>{let n=o.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new oe(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const r=n,o=e.tokenizer[r],i=t[r];t[r]=(...e)=>{let n=o.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new We;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if("options"===n)continue;const r=n,o=e.hooks[r],i=t[r];We.passThroughHooks.has(n)?t[r]=e=>{if(this.defaults.async)return Promise.resolve(o.call(t,e)).then((e=>i.call(t,e)));const n=o.call(t,e);return i.call(t,n)}:t[r]=(...e)=>{let n=o.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,r=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(r.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return Ue.lex(e,t??this.defaults)}parser(e,t){return He.parse(e,t??this.defaults)}#e(e,t){return(n,r)=>{const o={...r},i={...this.defaults,...o};!0===this.defaults.async&&!1===o.async&&(i.silent||console.warn("marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored."),i.async=!0);const s=this.#t(!!i.silent,!!i.async);if(null==n)return s(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof n)return s(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(i.hooks&&(i.hooks.options=i),i.async)return Promise.resolve(i.hooks?i.hooks.preprocess(n):n).then((t=>e(t,i))).then((e=>i.hooks?i.hooks.processAllTokens(e):e)).then((e=>i.walkTokens?Promise.all(this.walkTokens(e,i.walkTokens)).then((()=>e)):e)).then((e=>t(e,i))).then((e=>i.hooks?i.hooks.postprocess(e):e)).catch(s);try{i.hooks&&(n=i.hooks.preprocess(n));let r=e(n,i);i.hooks&&(r=i.hooks.processAllTokens(r)),i.walkTokens&&this.walkTokens(r,i.walkTokens);let o=t(r,i);return i.hooks&&(o=i.hooks.postprocess(o)),o}catch(e){return s(e)}}}#t(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="An error occurred:
"+Y(n.message+"",!0)+" ";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}};function Ze(e,t){return Ve.parse(e,t)}Ze.options=Ze.setOptions=function(e){return Ve.setOptions(e),Ze.defaults=Ve.defaults,U(Ze.defaults),Ze},Ze.getDefaults=M,Ze.defaults=q,Ze.use=function(...e){return Ve.use(...e),Ze.defaults=Ve.defaults,U(Ze.defaults),Ze},Ze.walkTokens=function(e,t){return Ve.walkTokens(e,t)},Ze.parseInline=Ve.parseInline,Ze.Parser=He,Ze.parser=He.parse,Ze.Renderer=Fe,Ze.TextRenderer=Be,Ze.Lexer=Ue,Ze.lexer=Ue.lex,Ze.Tokenizer=oe,Ze.Hooks=We,Ze.parse=Ze,Ze.options,Ze.setOptions,Ze.use,Ze.walkTokens,Ze.parseInline,He.parse,Ue.lex;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function Ye(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Ke,Ge={exports:{}};Ke=Ge,function(e,t){Ke.exports=t()}(0,(function(){var e=[],t=[],n={},r={},o={};function i(e){return"string"==typeof e?new RegExp("^"+e+"$","i"):e}function s(e,t){return e===t?t:e===e.toLowerCase()?t.toLowerCase():e===e.toUpperCase()?t.toUpperCase():e[0]===e[0].toUpperCase()?t.charAt(0).toUpperCase()+t.substr(1).toLowerCase():t.toLowerCase()}function a(e,t){return e.replace(t[0],(function(n,r){var o,i,a=(o=t[1],i=arguments,o.replace(/\$(\d{1,2})/g,(function(e,t){return i[t]||""})));return s(""===n?e[r-1]:n,a)}))}function c(e,t,r){if(!e.length||n.hasOwnProperty(e))return t;for(var o=r.length;o--;){var i=r[o];if(i[0].test(t))return a(t,i)}return t}function l(e,t,n){return function(r){var o=r.toLowerCase();return t.hasOwnProperty(o)?s(r,o):e.hasOwnProperty(o)?s(r,e[o]):c(o,r,n)}}function u(e,t,n,r){return function(r){var o=r.toLowerCase();return!!t.hasOwnProperty(o)||!e.hasOwnProperty(o)&&c(o,o,n)===o}}function d(e,t,n){return(n?t+" ":"")+(1===t?d.singular(e):d.plural(e))}return d.plural=l(o,r,e),d.isPlural=u(o,r,e),d.singular=l(r,o,t),d.isSingular=u(r,o,t),d.addPluralRule=function(t,n){e.push([i(t),n])},d.addSingularRule=function(e,n){t.push([i(e),n])},d.addUncountableRule=function(e){"string"!=typeof e?(d.addPluralRule(e,"$0"),d.addSingularRule(e,"$0")):n[e.toLowerCase()]=!0},d.addIrregularRule=function(e,t){t=t.toLowerCase(),e=e.toLowerCase(),o[e]=t,r[t]=e},[["I","we"],["me","us"],["he","they"],["she","they"],["them","them"],["myself","ourselves"],["yourself","yourselves"],["itself","themselves"],["herself","themselves"],["himself","themselves"],["themself","themselves"],["is","are"],["was","were"],["has","have"],["this","these"],["that","those"],["echo","echoes"],["dingo","dingoes"],["volcano","volcanoes"],["tornado","tornadoes"],["torpedo","torpedoes"],["genus","genera"],["viscus","viscera"],["stigma","stigmata"],["stoma","stomata"],["dogma","dogmata"],["lemma","lemmata"],["schema","schemata"],["anathema","anathemata"],["ox","oxen"],["axe","axes"],["die","dice"],["yes","yeses"],["foot","feet"],["eave","eaves"],["goose","geese"],["tooth","teeth"],["quiz","quizzes"],["human","humans"],["proof","proofs"],["carve","carves"],["valve","valves"],["looey","looies"],["thief","thieves"],["groove","grooves"],["pickaxe","pickaxes"],["passerby","passersby"]].forEach((function(e){return d.addIrregularRule(e[0],e[1])})),[[/s?$/i,"s"],[/[^\u0000-\u007F]$/i,"$0"],[/([^aeiou]ese)$/i,"$1"],[/(ax|test)is$/i,"$1es"],[/(alias|[^aou]us|t[lm]as|gas|ris)$/i,"$1es"],[/(e[mn]u)s?$/i,"$1s"],[/([^l]ias|[aeiou]las|[ejzr]as|[iu]am)$/i,"$1"],[/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,"$1i"],[/(alumn|alg|vertebr)(?:a|ae)$/i,"$1ae"],[/(seraph|cherub)(?:im)?$/i,"$1im"],[/(her|at|gr)o$/i,"$1oes"],[/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor)(?:a|um)$/i,"$1a"],[/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)(?:a|on)$/i,"$1a"],[/sis$/i,"ses"],[/(?:(kni|wi|li)fe|(ar|l|ea|eo|oa|hoo)f)$/i,"$1$2ves"],[/([^aeiouy]|qu)y$/i,"$1ies"],[/([^ch][ieo][ln])ey$/i,"$1ies"],[/(x|ch|ss|sh|zz)$/i,"$1es"],[/(matr|cod|mur|sil|vert|ind|append)(?:ix|ex)$/i,"$1ices"],[/\b((?:tit)?m|l)(?:ice|ouse)$/i,"$1ice"],[/(pe)(?:rson|ople)$/i,"$1ople"],[/(child)(?:ren)?$/i,"$1ren"],[/eaux$/i,"$0"],[/m[ae]n$/i,"men"],["thou","you"]].forEach((function(e){return d.addPluralRule(e[0],e[1])})),[[/s$/i,""],[/(ss)$/i,"$1"],[/(wi|kni|(?:after|half|high|low|mid|non|night|[^\w]|^)li)ves$/i,"$1fe"],[/(ar|(?:wo|[ae])l|[eo][ao])ves$/i,"$1f"],[/ies$/i,"y"],[/\b([pl]|zomb|(?:neck|cross)?t|coll|faer|food|gen|goon|group|lass|talk|goal|cut)ies$/i,"$1ie"],[/\b(mon|smil)ies$/i,"$1ey"],[/\b((?:tit)?m|l)ice$/i,"$1ouse"],[/(seraph|cherub)im$/i,"$1"],[/(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i,"$1"],[/(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i,"$1sis"],[/(movie|twelve|abuse|e[mn]u)s$/i,"$1"],[/(test)(?:is|es)$/i,"$1is"],[/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,"$1us"],[/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i,"$1um"],[/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i,"$1on"],[/(alumn|alg|vertebr)ae$/i,"$1a"],[/(cod|mur|sil|vert|ind)ices$/i,"$1ex"],[/(matr|append)ices$/i,"$1ix"],[/(pe)(rson|ople)$/i,"$1rson"],[/(child)ren$/i,"$1"],[/(eau)x?$/i,"$1"],[/men$/i,"man"]].forEach((function(e){return d.addSingularRule(e[0],e[1])})),["adulthood","advice","agenda","aid","aircraft","alcohol","ammo","analytics","anime","athletics","audio","bison","blood","bream","buffalo","butter","carp","cash","chassis","chess","clothing","cod","commerce","cooperation","corps","debris","diabetes","digestion","elk","energy","equipment","excretion","expertise","firmware","flounder","fun","gallows","garbage","graffiti","hardware","headquarters","health","herpes","highjinks","homework","housework","information","jeans","justice","kudos","labour","literature","machinery","mackerel","mail","media","mews","moose","music","mud","manga","news","only","personnel","pike","plankton","pliers","police","pollution","premises","rain","research","rice","salmon","scissors","series","sewage","shambles","shrimp","software","species","staff","swine","tennis","traffic","transportation","trout","tuna","wealth","welfare","whiting","wildebeest","wildlife","you",/pok[eé]mon$/i,/[^aeiou]ese$/i,/deer$/i,/fish$/i,/measles$/i,/o[iu]s$/i,/pox$/i,/sheep$/i].forEach(d.addUncountableRule),d}));var Qe=Ye(Ge.exports),Xe=function(e){var t={};try{t.WeakMap=WeakMap}catch(u){t.WeakMap=function(e,t){var n=t.defineProperty,r=t.hasOwnProperty,o=i.prototype;return o.delete=function(e){return this.has(e)&&delete e[this._]},o.get=function(e){return this.has(e)?e[this._]:void 0},o.has=function(e){return r.call(e,this._)},o.set=function(e,t){return n(e,this._,{configurable:!0,value:t}),this},i;function i(t){n(this,"_",{value:"_@ungap/weakmap"+e++}),t&&t.forEach(s,this)}function s(e){this.set(e[0],e[1])}}(Math.random(),Object)}var n=t.WeakMap,r={};try{r.WeakSet=WeakSet}catch(u){!function(e,t){var n=o.prototype;function o(){t(this,"_",{value:"_@ungap/weakmap"+e++})}n.add=function(e){return this.has(e)||t(e,this._,{value:!0,configurable:!0}),this},n.has=function(e){return this.hasOwnProperty.call(e,this._)},n.delete=function(e){return this.has(e)&&delete e[this._]},r.WeakSet=o}(Math.random(),Object.defineProperty)}function o(e,t,n,r,o,i){for(var s=("selectedIndex"in t),a=s;r>>0;ns;)--c;l=a+r-c;var w=Array(l),y=u[c];for(--n;y;){for(var v=y.newi,$=y.oldi;v"+e+"",r.querySelectorAll(t)):(r.innerHTML=e,r.childNodes)),n},function(e,t){return("svg"===t?function(e){var t=z(C),n=z("div");return n.innerHTML=''+e+" ",O(t,n.firstChild.childNodes),t}:L)(e)});function O(e,t){for(var n=t.length;n--;)e.appendChild(t[0])}function z(e){return e===C?E.createDocumentFragment():E.createElementNS("http://www.w3.org/1999/xhtml",e)}var M,q,U,F,B,H,W,V,Z,Y=(q="appendChild",U="cloneNode",F="createTextNode",H=(B="importNode")in(M=e),(W=M.createDocumentFragment())[q](M[F]("g")),W[q](M[F]("")),(H?M[B](W,!0):W[U](!0)).childNodes.length<2?function e(t,n){for(var r=t[U](),o=t.childNodes||[],i=o.length,s=0;n&&s
',V.content.childNodes[0].getAttribute(Z)==G)||(G="_dt: "+G.slice(1,-1)+";",Q=!0)}catch(u){}var X="\x3c!--"+G+"--\x3e",J=8,ee=1,te=3,ne=/^(?:style|textarea)$/i,re=/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i,oe=" \\f\\n\\r\\t",ie="[^"+oe+"\\/>\"'=]+",se="["+oe+"]+"+ie,ae="<([A-Za-z]+[A-Za-z0-9:._-]*)((?:",ce="(?:\\s*=\\s*(?:'[^']*?'|\"[^\"]*?\"|<[^>]*?>|"+ie.replace("\\/","")+"))?)",le=new RegExp(ae+se+ce+"+)(["+oe+"]*/?>)","g"),ue=new RegExp(ae+se+ce+"*)(["+oe+"]*/>)","g"),de=new RegExp("("+se+"\\s*=\\s*)(['\"]?)"+X+"\\2","gi");function pe(e,t,n,r){return"<"+t+n.replace(de,fe)+r}function fe(e,t,n){return t+(n||'"')+G+(n||'"')}function he(e,t,n){return re.test(t)?e:"<"+t+n+">"+t+">"}var me=Q?function(e,t){var n=t.join(" ");return t.slice.call(e,0).sort((function(e,t){return n.indexOf(e.name)<=n.indexOf(t.name)?-1:1}))}:function(e,t){return t.slice.call(e,0)};function ge(t,n,r,o){for(var i=t.childNodes,s=i.length,a=0;a{e.remove()}))}function dt(e,t="long"){const n=new Intl.ListFormat(l,{style:t,type:e});return(e,t)=>{let r=0;return n.formatToParts(e).map((({type:n,value:o})=>"element"===n&&t?t(o,r++,e):o))}}const pt=dt("conjunction"),ft=dt("disjunction");function ht(e,t){return pt(e,t).join("")}function mt(e,t){return ft(e,t).join("")}function gt(e){return e.replace(/&/g,"&").replace(/>/g,">").replace(/"/g,""").replace(/new Date)return i}catch(e){console.error("Failed to use Cache API.",e)}const s=await fetch(n);if(!s.ok&&i)return console.warn(`Returning a stale cached response for ${r}`),i;if(o&&s.ok){const e=s.clone(),r=new Headers(s.headers),i=new Date(Date.now()+t);r.set("Expires",i.toISOString());const a=new Response(await e.blob(),{headers:r});await o.put(n,a).catch(console.error)}return s}function xt(e,t=(e=>e)){const n=e.map(t),r=n.slice(0,-1).map((e=>Je`${e}, `));return Je`${r}${n[n.length-1]}`}function kt(e,t){return[].concat(pt(e,t)).map((e=>"string"==typeof e?Je`${e}`:e))}function _t(e,t="",n="",r=!1){if(e.id)return e.id;n||(n=(e.title?e.title:e.textContent).trim());let o=r?n:n.toLowerCase();if(o=o.trim().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/\W+/gim,"-").replace(/^-+/,"").replace(/-+$/,""),o?!/\.$/.test(o)&&/^[a-z]/i.test(t||o)||(o=`x${o}`):o="generatedID",t&&(o=`${t}-${o}`),e.ownerDocument.getElementById(o)){let t=0,n=`${o}-${t}`;for(;e.ownerDocument.getElementById(n);)t+=1,n=`${o}-${t}`;o=n}return e.id=o,o}function Et(e){const t=new Set,n="ltNodefault"in e.dataset?"":bt(e.textContent),r=e.children[0];if(e.dataset.lt?e.dataset.lt.split("|").map((e=>bt(e))).forEach((e=>t.add(e))):1===e.childNodes.length&&1===e.getElementsByTagName("abbr").length&&r.title?t.add(r.title):'""'===e.textContent&&t.add("the-empty-string"),t.add(n),t.delete(""),e.dataset.localLt){e.dataset.localLt.split("|").forEach((e=>t.add(bt(e))))}return[...t]}function Ct(e,t,n={copyAttributes:!0}){if(e.localName===t)return e;const r=e.ownerDocument.createElement(t);if(n.copyAttributes)for(const{name:t,value:n}of e.attributes)r.setAttribute(t,n);return r.append(...e.childNodes),e.replaceWith(r),r}function St(e,t){const n=t.closest(ct);let r=!1;if(n&&(r=!t.closest(".normative")||!n.querySelector(".normative")),e.startsWith("!")){if(r)return{type:"informative",illegal:!0};r=!1}else e.startsWith("?")&&(r=!0);return{type:r?"informative":"normative",illegal:!1}}function Lt(e,t){return t.append(...e.childNodes),e.appendChild(t),e}function Rt(e,t){const n=[];let r=e.parentElement;for(;r;){const e=r.closest(t);if(!e)break;n.push(e),r=e.parentElement}return n}function At(e){const{previousSibling:t}=e;if(!t||t.nodeType!==Node.TEXT_NODE)return"";const n=t.textContent.lastIndexOf("\n");if(-1===n)return"";const r=t.textContent.slice(n+1);return/\S/.test(r)?"":r}class Tt extends Set{constructor(e=[]){super();for(const t of e)this.add(t)}add(e){return this.has(e)||this.getCanonicalKey(e)?this:super.add(e)}has(e){return super.has(e)||[...this.keys()].some((t=>t.toLowerCase()===e.toLowerCase()))}delete(e){return super.has(e)?super.delete(e):super.delete(this.getCanonicalKey(e))}getCanonicalKey(e){return super.has(e)?e:[...this.keys()].find((t=>t.toLowerCase()===e.toLowerCase()))}}function Pt(e){const t=e.cloneNode(!0);return t.querySelectorAll("[id]").forEach((e=>e.removeAttribute("id"))),t.querySelectorAll("dfn").forEach((e=>{Ct(e,"span",{copyAttributes:!1})})),t.hasAttribute("id")&&t.removeAttribute("id"),It(t),t}function It(e){const t=document.createTreeWalker(e,NodeFilter.SHOW_COMMENT);for(const e of[...Dt(t)])e.remove()}function*Dt(e){for(;e.nextNode();)yield e.currentNode}class Nt extends Map{constructor(e=[]){return super(),e.forEach((([e,t])=>{this.set(e,t)})),this}set(e,t){return super.set(e.toLowerCase(),t),this}get(e){return super.get(e.toLowerCase())}has(e){return super.has(e.toLowerCase())}delete(e){return super.delete(e.toLowerCase())}}class jt extends Error{constructor(e,t,n){super(e);const r=n.isWarning?"ReSpecWarning":"ReSpecError";Object.assign(this,{message:e,plugin:t,name:r,...n}),n.elements&&n.elements.forEach((t=>function(e,t,n){e.classList.add("respec-offending-element"),e.hasAttribute("title")||e.setAttribute("title",n||t),e.id||_t(e,"respec-offender")}(t,e,n.title)))}toJSON(){const{message:e,name:t,stack:n}=this,{plugin:r,hint:o,elements:i,title:s,details:a}=this;return{message:e,name:t,plugin:r,hint:o,elements:i,title:s,details:a,stack:n}}}function Ot(e,t,n={}){const r={...n,isWarning:!1};o("error",new jt(e,t,r))}function zt(e,t,n={}){const r={...n,isWarning:!0};o("warn",new jt(e,t,r))}function Mt(e){return{showError:(t,n)=>Ot(t,e,n),showWarning:(t,n)=>zt(t,e,n)}}function qt(e){return e?`\`${e}\``:""}function Ut(e,{quotes:t}={quotes:!1}){return mt(e,t?e=>{return qt((t=e,String(t)?`"${t}"`:""));var t}:qt)}function Ft(e,...t){return Bt(e.map(((e,n)=>{const r=t[n];if(!r)return e;if(!r.startsWith("[")&&!r.endsWith("]"))return e+r;const[o,i]=r.slice(1,-1).split("|");if(i){return`${e}[${o}](${new URL(i,"https://respec.org/docs/")})`}return`${e}[\`${o}\`](https://respec.org/docs/#${o})`})).join(""))}function Bt(e){if(!e)return e;const t=e.trimEnd().split("\n");for(;t.length&&!t[0].trim();)t.shift();const n=t.filter((e=>e.trim())).map((e=>e.search(/[^\s]/))),r=Math.min(...n);return t.map((e=>e.slice(r))).join("\n")}const Ht=new Map([["text/html","html"],["application/xml","xml"]]);function Wt(e,t=document){const n=Ht.get(e);if(!n){const t=[...Ht.values()].join(", ");throw new TypeError(`Invalid format: ${e}. Expected one of: ${t}.`)}const r=Vt(n,t);return`data:${e};charset=utf-8,${encodeURIComponent(r)}`}function Vt(e,t){const n=t.cloneNode(!0);!function(e){const{head:t,body:n,documentElement:r}=e;It(e),e.querySelectorAll(".removeOnSave, #toc-nav").forEach((e=>e.remove())),n.classList.remove("toc-sidebar"),ut(r);const i=e.createDocumentFragment(),s=e.querySelector("meta[name='viewport']");s&&t.firstChild!==s&&i.appendChild(s);let a=e.querySelector("meta[charset], meta[content*='charset=']");a||(a=Je` `);i.appendChild(a);const c=`ReSpec ${window.respecVersion||"Developer Channel"}`,l=Je`
+
+ `;i.appendChild(l),t.prepend(i),o("beforesave",r)}(n);let r="";if("xml"===e)r=(new XMLSerializer).serializeToString(n);else!function(e){e.querySelectorAll("style").forEach((e=>{e.innerHTML=`\n${e.innerHTML}\n`})),e.querySelectorAll("head > *").forEach((e=>{e.outerHTML=`\n${e.outerHTML}`}))}(n),n.doctype&&(r+=(new XMLSerializer).serializeToString(n.doctype)),r+=n.documentElement.outerHTML;return r}n("core/exporter",{rsDocToDataURL:Wt});class Zt{constructor(){this._respecDonePromise=new Promise((e=>{i("end-all",(()=>e()),{once:!0})})),this.errors=[],this.warnings=[],i("error",(e=>{console.error(e,e.toJSON()),this.errors.push(e)})),i("warn",(e=>{console.warn(e,e.toJSON()),this.warnings.push(e)}))}get version(){return window.respecVersion}get ready(){return this._respecDonePromise}async toHTML(){return Vt("html",document)}}const Yt="core/post-process";const Kt="core/pre-process";const Gt="core/base-runner";async function Qt(e){!function(){const e=new Zt;Object.defineProperty(document,"respec",{value:e})}(),o("start-all",respecConfig),function(e){const t={},n=e=>Object.assign(t,e);n(e),i("amend-user-config",n),i("end-all",(()=>{const e=document.createElement("script");e.id="initialUserConfig",e.type="application/json";for(const e of s)e in t&&delete t[e];e.innerHTML=JSON.stringify(t,null,2),document.head.appendChild(e)}))}(respecConfig),function(e){const t=new URLSearchParams(document.location.search),n=Array.from(t).filter((([e,t])=>!!e&&!!t)).map((([e,t])=>{const n=decodeURIComponent(e),r=decodeURIComponent(t.replace(/%3D/g,"="));let o;try{o=JSON.parse(r)}catch{o=r}return[n,o]})),r=Object.fromEntries(n);Object.assign(e,r),o("amend-user-config",r)}(respecConfig),performance.mark(`${Gt}-start`),await async function(e){if(Array.isArray(e.preProcess)){const t=e.preProcess.filter((e=>{const t="function"==typeof e;return t||Ot("Every item in `preProcess` must be a JS function.",Kt),t})).map((async(t,n)=>{const r=Mt(`${Kt}/${t.name||`[${n}]`}`);try{return await t(e,document,r)}catch(e){Ot(`Function ${t.name} threw an error during \`preProcess\`.`,Kt,{hint:"See developer console."}),console.error(e)}}));await Promise.all(t)}}(respecConfig);const t=e.filter((e=>{return(t=e)&&(t.run||t.Plugin);var t}));t.forEach((e=>!e.name&&console.warn("Plugin lacks name:",e))),await async function(e,t){for(const n of e.filter((e=>e.prepare)))try{await n.prepare(t)}catch(e){console.error(e)}}(t,respecConfig),await async function(e,t){for(const n of e){const e=n.name||"";try{await new Promise((async(r,o)=>{const i=setTimeout((()=>{const t=`Plugin ${e} took too long.`;console.error(t,n),o(new Error(t))}),15e3);performance.mark(`${e}-start`);try{n.Plugin?(await new n.Plugin(t).run(),r()):n.run&&(await n.run(t),r())}catch(e){o(e)}finally{clearTimeout(i),performance.mark(`${e}-end`),performance.measure(e,`${e}-start`,`${e}-end`)}}))}catch(e){console.error(e)}}}(t,respecConfig),o("plugins-done",respecConfig),await async function(e){if(Array.isArray(e.postProcess)){const t=e.postProcess.filter((e=>{const t="function"==typeof e;return t||Ot("Every item in `postProcess` must be a JS function.",Yt),t})).map((async(t,n)=>{const r=Mt(`${Yt}/${t.name||`[${n}]`}`);try{return await t(e,document,r)}catch(e){Ot(`Function ${t.name} threw an error during \`postProcess\`.`,Yt,{hint:"See developer console."}),console.error(e)}}));await Promise.all(t)}"function"==typeof e.afterEnd&&await e.afterEnd(e,document)}(respecConfig),o("end-all"),ut(document),performance.mark(`${Gt}-end`),performance.measure(Gt,`${Gt}-start`,`${Gt}-end`)}var Xt=Object.freeze({__proto__:null,name:Gt,runAll:Qt});var Jt=String.raw`.respec-modal .close-button{position:absolute;z-index:inherit;padding:.2em;font-weight:700;cursor:pointer;margin-left:5px;border:none;background:0 0}
+#respec-ui{position:fixed;display:flex;flex-direction:row-reverse;top:20px;right:20px;width:202px;text-align:right;z-index:9000}
+#respec-pill,.respec-info-button{height:2.4em;background:#fff;background:var(--bg,#fff);color:#787878;color:var(--tocnav-normal-text,#787878);border:1px solid #ccc;box-shadow:1px 1px 8px 0 rgba(100,100,100,.5);box-shadow:1px 1px 8px 0 var(--tocsidebar-shadow,rgba(100,100,100,.5));padding:.2em 0}
+.respec-info-button{border:none;opacity:.75;border-radius:2em;margin-right:1em;min-width:3.5em;will-change:opacity}
+.respec-info-button:focus,.respec-info-button:hover{opacity:1;transition:opacity .2s}
+#respec-pill{width:4.8em}
+#respec-pill:not(:disabled){animation:respec-fadein .6s ease-in-out}
+@keyframes respec-fadein{
+from{margin-top:-1.2em;border-radius:50%;border:.2em solid rgba(100,100,100,.5);box-shadow:none;height:4.8em}
+to{margin-top:0;border:1px solid #ccc;border-radius:0;box-shadow:1px 1px 8px 0 rgba(100,100,100,.5);height:2.4em}
+}
+#respec-pill:disabled{margin-top:-1.2em;position:relative;border:none;box-shadow:none;border-radius:50%;width:4.8em;height:4.8em;padding:0}
+#respec-pill:disabled::after{position:absolute;content:'';inset:-.2em;border-radius:50%;border:.2em solid rgba(100,100,100,.5);border-left:.2em solid transparent;animation:respec-spin .5s infinite linear}
+@media (prefers-reduced-motion){
+#respec-pill:not(:disabled){animation:none}
+#respec-pill:disabled::after{animation:none;border-left:.2em solid rgba(100,100,100,.5)}
+}
+@keyframes respec-spin{
+0%{transform:rotate(0)}
+100%{transform:rotate(360deg)}
+}
+.respec-hidden{visibility:hidden;opacity:0;transition:visibility 0s .2s,opacity .2s linear}
+.respec-visible{visibility:visible;opacity:1;transition:opacity .2s linear}
+#respec-pill:focus,#respec-pill:hover{color:#000;background-color:#f5f5f5;transition:color .2s}
+#respec-menu{position:absolute;margin:0;padding:0;font-family:sans-serif;background:var(--bg,#fff);color:var(--text,#000);box-shadow:1px 1px 8px 0 rgba(100,100,100,.5);width:200px;display:none;text-align:left;margin-top:32px;font-size:.8em}
+#respec-menu:not([hidden]){display:block}
+#respec-menu li{list-style-type:none;margin:0;padding:0}
+.respec-save-buttons{display:grid;grid-template-columns:repeat(auto-fill,minmax(47%,2fr));grid-gap:.5cm;padding:.5cm}
+.respec-save-button:link{padding-top:16px;color:var(--def-text,#fff);background:var(--def-bg,#2a5aa8);justify-self:stretch;height:1cm;text-decoration:none;text-align:center;font-size:inherit;border:none;border-radius:.2cm}
+.respec-save-button:link:hover{color:var(--def-text,#fff);background:var(--defrow-border,#2a5aa8);padding:0;margin:0;border:0;padding-top:16px}
+.respec-save-button:link:focus{background:var(--tocnav-active-bg,#193766);color:var(--tocnav-active-text,#000)}
+#respec-pill:focus,#respec-ui button:focus,.respec-option:focus{outline:0;outline-style:none}
+#respec-pill-error{background-color:red;color:#fff}
+#respec-pill-warning{background-color:orange;color:#fff}
+.respec-error-list,.respec-warning-list{margin:0;padding:0;font-family:sans-serif;font-size:.85em}
+.respec-warning-list{background-color:#fffbe6}
+:is(.respec-warning-list,.respec-error-list)>li{list-style-type:none;margin:0;padding:.5em 0;padding-left:2em;padding-right:.5em}
+:is(.respec-warning-list,.respec-error-list)>li+li{margin-top:.5rem}
+:is(.respec-warning-list,.respec-error-list)>li:before{position:absolute;left:.4em}
+:is(.respec-warning-list,.respec-error-list) p{padding:0;margin:0}
+.respec-warning-list>li{color:#5c3b00;border-bottom:thin solid #fff5c2}
+.respec-error-list,.respec-error-list li{background-color:#fff0f0}
+.respec-warning-list>li::before{content:"⚠️"}
+.respec-error-list>li::before{content:"💥"}
+.respec-error-list>li{color:#5c3b00;border-bottom:thin solid #ffd7d7}
+:is(.respec-warning-list,.respec-error-list)>li li{list-style:disc}
+#respec-overlay{display:block;position:fixed;z-index:10000;top:0;left:0;height:100%;width:100%;background:#000}
+.respec-show-overlay{transition:opacity .2s linear;opacity:.5}
+.respec-hide-overlay{transition:opacity .2s linear;opacity:0}
+.respec-modal{display:block;position:fixed;z-index:11000;top:10%;background:var(--bg,#fff);color:var(--text,#000);border:5px solid #666;border-color:var(--tocsidebar-shadow,#666);min-width:20%;padding:0;max-height:80%;overflow-y:auto;margin:0 -.5cm;left:20%;max-width:75%;min-width:60%}
+.respec-modal h3{margin:0;padding:.2em;left:0!important;text-align:center;background:var(--tocsidebar-shadow,#ddd);color:var(--text,#000);font-size:1em}
+#respec-menu button.respec-option{background:var(--bg,#fff);color:var(--text,#000);border:none;width:100%;text-align:left;font-size:inherit;padding:1.2em 1.2em}
+#respec-menu button.respec-option:hover{background-color:var(--tocnav-hover-bg,#eee);color:var(--tocnav-hover-text,#000)}
+.respec-cmd-icon{padding-right:.5em}
+#respec-ui button.respec-option:first-child{margin-top:0}
+#respec-ui button.respec-option:last-child{border:none;border-radius:inherit;margin-bottom:0}
+.respec-button-copy-paste{position:absolute;height:28px;width:40px;cursor:pointer;background-image:linear-gradient(#fcfcfc,#eee);border:1px solid #90b8de;border-left:0;border-radius:0 0 3px 0;-webkit-user-select:none;user-select:none;-webkit-appearance:none;top:0;left:127px}
+@media print{
+#respec-ui{display:none}
+}
+.respec-iframe{width:100%;min-height:550px;height:100%;overflow:hidden;padding:0;margin:0;border:0}
+.respec-iframe:not(.ready){background:url(https://respec.org/xref/loader.gif) no-repeat center}
+.respec-iframe+a[href]{font-size:.9rem;float:right;margin:0 .5em .5em;border-bottom-width:1px}
+p:is(.respec-hint,.respec-occurrences){display:block;margin-top:.5em}
+.respec-plugin{text-align:right;color:rgb(120,120,120,.5);font-size:.6em}`;const en=/>/gm,tn=/&/gm;class nn extends tt.Renderer{code(e,t,n){const{language:r,...o}=nn.parseInfoString(t);if(/(^webidl$)/i.test(r))return`${e} `;const i=super.code(e,r,n).replace('class="language-','class="'),{example:s,illegalExample:a}=o;if(!s&&!a)return i;const c=s||a,l=`${r} ${s?"example":"illegal-example"}`;return i.replace("",``)}image(e,t,n){if(!t)return super.image(e,t,n);return String.raw`
+
+
+ ${t}
+
+ `}static parseInfoString(e){const t=e.search(/\s/);if(-1===t)return{language:e};const n=e.slice(0,t),r=e.slice(t+1);let o;if(r)try{o=JSON.parse(`{ ${r} }`)}catch(e){console.error(e)}return{language:n,...o}}heading(e,t,n){const r=/(.+)\s+{#([\w-]+)}$/;if(r.test(e)){const[,n,o]=e.match(r);return`${n} `}return super.heading(e,t,n)}}const rn={gfm:!0,renderer:new nn};function on(e,t={inline:!1}){const n=Bt(e).replace(en,">").replace(tn,"&");return t.inline?tt.parseInline(n,rn):tt.parse(n,rn)}function sn(e){for(const t of e.getElementsByTagName("pre"))t.prepend("\n");e.innerHTML=on(e.innerHTML)}const an=(cn="[data-format='markdown']:not(body)",e=>{const t=e.querySelectorAll(cn);return t.forEach(sn),Array.from(t)});var cn;var ln=Object.freeze({__proto__:null,markdownToHtml:on,name:"core/markdown",run:function(e){const t=!!document.querySelector("[data-format=markdown]:not(body)"),n="markdown"===e.format;if(!n&&!t)return;if(!n)return void an(document.body);const r=document.getElementById("respec-ui");r.remove();const o=document.body.cloneNode(!0);!function(e,t){const n=e.querySelectorAll(t);for(const e of n){const{innerHTML:t}=e;if(/^<\w/.test(t.trimStart()))continue;const n=t.split("\n"),r=n.slice(0,2).join("\n"),o=n.slice(-2).join("\n");if(r.trim()&&e.prepend("\n\n"),o.trim()){const t=At(e);e.append(`\n\n${t}`)}}}(o,"[data-format=markdown], section, div, address, article, aside, figure, header, main"),sn(o),function(e){Array.from(e).forEach((e=>{e.replaceWith(e.textContent)}))}(o.querySelectorAll(".nolinks a[href]")),o.append(r),document.body.replaceWith(o)}});function un(e,t){e&&Array.from(t).forEach((([t,n])=>{e.setAttribute(`aria-${t}`,n)}))}!function(){const e=document.createElement("style");e.id="respec-ui-styles",e.textContent=Jt,e.classList.add("removeOnSave"),document.head.appendChild(e)}();const dn=Je`
`,pn=Je` `,fn=Je`_n.closeModal()}
+ title="Close"
+>
+ ❌
+ `;let hn,mn;window.addEventListener("load",(()=>$n(pn)));const gn=[],bn=[],wn={};i("start-all",(()=>document.body.prepend(dn)),{once:!0}),i("end-all",(()=>document.body.prepend(dn)),{once:!0});const yn=Je`ReSpec `;function vn(){pn.classList.toggle("respec-hidden"),pn.classList.toggle("respec-visible"),pn.hidden=!pn.hidden}function $n(e){const t=e.querySelectorAll("a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled])"),n=t[0],r=t[t.length-1];n&&n.focus(),e.addEventListener("keydown",(e=>{"Tab"===e.key&&(e.shiftKey?document.activeElement===n&&(r.focus(),e.preventDefault()):document.activeElement===r&&(n.focus(),e.preventDefault()))}))}dn.appendChild(yn),yn.addEventListener("click",(e=>{e.stopPropagation(),yn.setAttribute("aria-expanded",String(pn.hidden)),vn(),pn.querySelector("li:first-child button").focus()})),document.documentElement.addEventListener("click",(()=>{pn.hidden||vn()})),dn.appendChild(pn),pn.addEventListener("keydown",(e=>{"Escape"!==e.key||pn.hidden||(yn.setAttribute("aria-expanded",String(pn.hidden)),vn(),yn.focus())}));const xn=new Map([["controls","respec-menu"],["expanded","false"],["haspopup","true"],["label","ReSpec Menu"]]);function kn(e,t,n,r){t.push(e),wn.hasOwnProperty(n)||(wn[n]=function(e,t,n){const r=`respec-pill-${e}`,o=Je` `;o.addEventListener("click",(()=>{o.setAttribute("aria-expanded","true");const r=Je` `;for(const e of t){const t=document.createRange().createContextualFragment(En(e)),n=document.createElement("li");t.firstElementChild===t.lastElementChild?n.append(...t.firstElementChild.childNodes):n.appendChild(t),r.appendChild(n)}_n.freshModal(n,r,o)}));const i=new Map([["expanded","false"],["haspopup","true"],["controls",`respec-pill-${e}-modal`]]);return un(o,i),o}(n,t,r),dn.appendChild(wn[n]));const o=wn[n];o.textContent=t.length;const i=1===t.length?rt.singular(r):r;un(o,new Map([["label",`${t.length} ${i}`]]))}un(yn,xn);const _n={show(){try{dn.hidden=!1}catch(e){console.error(e)}},hide(){dn.hidden=!0},enable(){yn.removeAttribute("disabled")},addCommand(e,t,n,r){r=r||"";const o=`respec-button-${e.toLowerCase().replace(/\s+/,"-")}`,i=Je`
+ ${r} ${e}…
+ `,s=Je`${i} `;return s.addEventListener("click",t),pn.appendChild(s),i},error(e){kn(e,gn,"error","ReSpec Errors")},warning(e){kn(e,bn,"warning","ReSpec Warnings")},closeModal(e){mn&&(mn.classList.remove("respec-show-overlay"),mn.classList.add("respec-hide-overlay"),mn.addEventListener("transitionend",(()=>{mn.remove(),mn=null}))),e&&e.setAttribute("aria-expanded","false"),hn&&(hn.remove(),hn=null,yn.focus())},freshModal(e,t,n){hn&&hn.remove(),mn&&mn.remove(),mn=Je`
`;const r=`${n.id}-modal`,o=`${r}-heading`;hn=Je``;const i=new Map([["labelledby",o]]);un(hn,i),document.body.append(mn,hn),mn.addEventListener("click",(()=>this.closeModal(n))),mn.classList.toggle("respec-show-overlay"),hn.hidden=!1,$n(hn)}};function En(e){if("string"==typeof e)return e;const t=e.plugin?`(plugin: "${e.plugin}")
`:"",n=e.hint?`\n${on(`How to fix: ${Bt(e.hint)}`,{inline:!e.hint.includes("\n")})}\n`:"",r=Array.isArray(e.elements)?`
Occurred ${e.elements.length} times at:
\n ${on(e.elements.map(Cn).join("\n"))}`:"",o=e.details?`\n\n\n${e.details}\n \n`:"";return`${on(`**${gt(e.message)}**`,{inline:!0})}${n}${r}${o}${t}`}function Cn(e){return`* [\`<${e.localName}>\`](#${e.id}) element`}document.addEventListener("keydown",(e=>{"Escape"===e.key&&_n.closeModal()})),window.respecUI=_n,i("error",(e=>_n.error(e))),i("warn",(e=>_n.warning(e)));var Sn=Object.freeze({__proto__:null,name:"core/ui",ui:_n});async function Ln(e){try{_n.show(),await async function(){"loading"===document.readyState&&await new Promise((e=>document.addEventListener("DOMContentLoaded",e)))}(),await Qt(e)}finally{_n.enable()}}window.addEventListener("error",(e=>{console.error(e.error,e.message,e)}));const Rn=[Promise.resolve().then((function(){return Xt})),Promise.resolve().then((function(){return Sn})),Promise.resolve().then((function(){return An})),Promise.resolve().then((function(){return u})),Promise.resolve().then((function(){return In})),Promise.resolve().then((function(){return Mn})),Promise.resolve().then((function(){return Wn})),Promise.resolve().then((function(){return Qn})),Promise.resolve().then((function(){return ln})),Promise.resolve().then((function(){return Xn})),Promise.resolve().then((function(){return Jn})),Promise.resolve().then((function(){return nr})),Promise.resolve().then((function(){return ir})),Promise.resolve().then((function(){return lr})),Promise.resolve().then((function(){return fr})),Promise.resolve().then((function(){return Ir})),Promise.resolve().then((function(){return jr})),Promise.resolve().then((function(){return Or})),Promise.resolve().then((function(){return qr})),Promise.resolve().then((function(){return Go})),Promise.resolve().then((function(){return Jo})),Promise.resolve().then((function(){return li})),Promise.resolve().then((function(){return ui})),Promise.resolve().then((function(){return hi})),Promise.resolve().then((function(){return yi})),Promise.resolve().then((function(){return _i})),Promise.resolve().then((function(){return Si})),Promise.resolve().then((function(){return bo})),Promise.resolve().then((function(){return Zi})),Promise.resolve().then((function(){return cs})),Promise.resolve().then((function(){return Oi})),Promise.resolve().then((function(){return Lo})),Promise.resolve().then((function(){return $s})),Promise.resolve().then((function(){return ks})),Promise.resolve().then((function(){return _s})),Promise.resolve().then((function(){return Ps})),Promise.resolve().then((function(){return Ds})),Promise.resolve().then((function(){return Os})),Promise.resolve().then((function(){return Us})),Promise.resolve().then((function(){return Ws})),Promise.resolve().then((function(){return Ks})),Promise.resolve().then((function(){return ta})),Promise.resolve().then((function(){return na})),Promise.resolve().then((function(){return ca})),Promise.resolve().then((function(){return ma})),Promise.resolve().then((function(){return va})),Promise.resolve().then((function(){return Ca})),Promise.resolve().then((function(){return Ra})),Promise.resolve().then((function(){return Ta})),Promise.resolve().then((function(){return Ia})),Promise.resolve().then((function(){return Ua})),Promise.resolve().then((function(){return Va})),Promise.resolve().then((function(){return Za})),Promise.resolve().then((function(){return Ya})),Promise.resolve().then((function(){return Qa})),Promise.resolve().then((function(){return tc})),Promise.resolve().then((function(){return oc})),Promise.resolve().then((function(){return cc})),Promise.resolve().then((function(){return dc})),Promise.resolve().then((function(){return hc})),Promise.resolve().then((function(){return bc})),Promise.resolve().then((function(){return vc})),Promise.resolve().then((function(){return kc})),Promise.resolve().then((function(){return Sc}))];Promise.all(Rn).then((e=>Ln(e))).catch((e=>console.error(e)));var An=Object.freeze({__proto__:null,name:"core/location-hash",run:function(){window.location.hash&&document.respec.ready.then((()=>{let e=decodeURIComponent(window.location.hash).slice(1);const t=document.getElementById(e),n=/\W/.test(e);if(!t&&n){const t=e.replace(/[\W]+/gim,"-").replace(/^-+/,"").replace(/-+$/,"");document.getElementById(t)&&(e=t)}window.location.hash=`#${e}`}))}});var Tn=String.raw`@keyframes pop{
+0%{transform:scale(1,1)}
+25%{transform:scale(1.25,1.25);opacity:.75}
+100%{transform:scale(1,1)}
+}
+a.internalDFN{color:inherit;border-bottom:1px solid #99c;text-decoration:none}
+a.externalDFN{color:inherit;border-bottom:1px dotted #ccc;text-decoration:none}
+a.bibref{text-decoration:none}
+.respec-offending-element:target{animation:pop .25s ease-in-out 0s 1}
+.respec-offending-element,a[href].respec-offending-element{text-decoration:red wavy underline}
+@supports not (text-decoration:red wavy underline){
+.respec-offending-element:not(pre){display:inline-block}
+.respec-offending-element{background:url(data:image/gif;base64,R0lGODdhBAADAPEAANv///8AAP///wAAACwAAAAABAADAEACBZQjmIAFADs=) bottom repeat-x}
+}
+#references :target{background:#eaf3ff;animation:pop .4s ease-in-out 0s 1}
+cite .bibref{font-style:normal}
+a[href].orcid{padding-left:4px;padding-right:4px}
+a[href].orcid>svg{margin-bottom:-2px}
+ol.tof,ul.tof{list-style:none outside none}
+.caption{margin-top:.5em;font-style:italic}
+#issue-summary>ul{column-count:2}
+#issue-summary li{list-style:none;display:inline-block}
+details.respec-tests-details{margin-left:1em;display:inline-block;vertical-align:top}
+details.respec-tests-details>*{padding-right:2em}
+details.respec-tests-details[open]{z-index:999999;position:absolute;border:thin solid #cad3e2;border-radius:.3em;background-color:#fff;padding-bottom:.5em}
+details.respec-tests-details[open]>summary{border-bottom:thin solid #cad3e2;padding-left:1em;margin-bottom:1em;line-height:2em}
+details.respec-tests-details>ul{width:100%;margin-top:-.3em}
+details.respec-tests-details>li{padding-left:1em}
+.self-link:hover{opacity:1;text-decoration:none;background-color:transparent}
+aside.example .marker>a.self-link{color:inherit}
+.header-wrapper{display:flex;align-items:baseline;position:relative;left:-.5em}
+:is(h2,h3,h4,h5,h6):not(#toch2)+a.self-link{color:inherit;order:-1;position:relative;left:-.7em;font-size:1rem;opacity:.5}
+:is(h2,h3,h4,h5,h6)+a.self-link::before{content:"§";text-decoration:none;color:var(--heading-text)}
+:is(h2,h3)+a.self-link{top:-.2em}
+:is(h4,h5,h6)+a.self-link::before{color:#000}
+@media (max-width:767px){
+dd{margin-left:0}
+}
+@media print{
+.removeOnSave{display:none}
+}`;const Pn=function(){const e=document.createElement("style");return e.id="respec-mainstyle",e.textContent=Tn,document.head.appendChild(e),e}();var In=Object.freeze({__proto__:null,name:"core/style",run:function(e){e.noReSpecCSS&&Pn.remove()}});var Dn=String.raw`img.license{float:left;padding-right:5px}`;const Nn="logius/style";function jn(){const e=Je`
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Abbreviations
+ AA: Alcoholics Anonymous
+ BB: BoerenBond
+
+
+ Document config
+ EIGENAAR
+
+
+ Document owner
+ EIGENAAR
+
+
+ Document store
+ https://eigenaar.store
+
+
+ Document type
+ IM
+
+
+ Draft URI
+ URL: https://eigenaar.nl/BakstenenCM.html
+
+
+ Editor
+ Name: Persoon, Kadaster
+ URL: https://eigenaar.nl/
+
+
+ Hypothesis
+ False
+
+
+ License
+ cc-by-nd
+
+
+ Lint
+ False
+
+
+ Modification date
+ 2022-05-28
+
+
+ Previous publish date
+ 2022-05-01
+
+
+ Publish date
+ 2022-06-01
+
+
+ Section links
+ True
+
+
+ Short name
+ BakstenenCM
+
+
+ Spec status
+ GN-CV
+
+
+ TOC levels
+ 3
+
+
+ Version
+ 1.0
+
+
+
+
+ Samenvatting
+
+ Dit is de samenvatting.
+
+
+ Over dit document
+
+ Dit is de status van het document.
+
+
+ Introductie
+
+
+
Dit is een introductie hoofdstuk.
+
+
+ Met een plaatje van een gebouw.
+
+
+
+
+
+ De catalogus
+ De catalogus wordt hieronder ingevoegd.
+
+
+
+ Bijlage
+
+ Dit is een bijlage.
+
+
+