UNPKG

enketo-transformer

Version:

Library that transforms ODK-compliant XForms into a format that Enketo can consume

1 lines 41.2 kB
{"version":3,"file":"transformer.cjs","sources":["../../src/transformer.ts"],"sourcesContent":["import { DOMParser, XPathResult, XSLTProcessor } from 'enketo-transformer/dom';\nimport type LibXMLJS from 'libxmljs';\nimport type { DOM } from './dom/abstract';\nimport { NodeTypes } from './dom/shared';\nimport xslForm from './xsl/openrosa2html5form.xsl?raw';\nimport xslModel from './xsl/openrosa2xmlmodel.xsl?raw';\nimport { parseLanguage } from './language';\nimport { markdownToHTML } from './markdown';\nimport { NAMESPACES } from './shared';\nimport { escapeURLPath, getMediaPath } from './url';\n\nexport type TransformPreprocess = (\n this: typeof LibXMLJS,\n doc: LibXMLJS.Document\n) => LibXMLJS.Document;\n\ntype NodeOnly<T> = [typeof ENV] extends ['web'] ? undefined : T;\n\nexport interface Survey {\n xform: string;\n markdown?: boolean;\n media?: Record<string, string>;\n openclinica?: boolean | number;\n\n /**\n * @deprecated\n *\n * Only supported in Node environments.\n */\n preprocess?: NodeOnly<TransformPreprocess>;\n\n theme?: string;\n}\n\nexport type TransformedSurvey<T = unknown> = Omit<T, keyof Survey> & {\n form: string;\n languageMap: Record<string, string>;\n model: string;\n transformerVersion: string;\n};\n\nexport type Transform = <T extends Survey>(\n survey: T\n) => Promise<TransformedSurvey<T>>;\n\nconst getPreprocess = (\n survey: Survey\n): typeof ENV extends 'web' ? void : TransformPreprocess | void => {\n if (String(ENV) === 'node' && typeof survey.preprocess === 'function') {\n return survey.preprocess;\n }\n};\n\n/**\n * Performs XSLT transformation on XForm and process the result.\n */\nexport const transform: Transform = async (survey) => {\n const { xform, markdown, media, openclinica, theme } = survey;\n\n const xsltParams = openclinica\n ? {\n openclinica: 1,\n }\n : {};\n\n const mediaMap = Object.fromEntries(\n Object.entries(media || {}).map((entry) => entry.map(escapeURLPath))\n );\n const domParser = new DOMParser();\n const xslFormDoc = domParser.parseFromString(xslForm, 'text/xml');\n\n let xformDoc: DOM.Document = domParser.parseFromString(xform, 'text/xml');\n\n const preprocess = getPreprocess(survey);\n\n if (typeof preprocess === 'function') {\n const { libxmljs } = await import('libxslt');\n\n // This passes with the Node and test configs, but now fails with the\n // web config... where it is never used. Ignoring the error after\n // several futile, probably pointless, attempts to appease the compiler.\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ^\n // @ts-ignore\n xformDoc = preprocess.call(libxmljs, xformDoc as LibXMLJS.Document);\n }\n\n processBinaryDefaults(xformDoc, mediaMap);\n processItemsets(xformDoc);\n\n const htmlDoc = xslTransform(xslFormDoc, xformDoc, xsltParams);\n\n correctHTMLDocHierarchy(htmlDoc);\n correctAction(htmlDoc, 'setgeopoint');\n correctAction(htmlDoc, 'setvalue');\n transformAppearances(htmlDoc);\n replaceTheme(htmlDoc, theme);\n replaceMediaSources(htmlDoc, mediaMap);\n\n const languageMap = replaceLanguageTags(htmlDoc);\n const form =\n markdown !== false\n ? renderMarkdown(htmlDoc, mediaMap)\n : docToString(htmlDoc);\n const xslModelDoc = domParser.parseFromString(xslModel, 'text/xml');\n const xmlDoc = xslTransform(xslModelDoc, xformDoc);\n\n correctModelNamespaces(xslModelDoc, xformDoc, xmlDoc);\n replaceMediaSources(xmlDoc, mediaMap);\n addInstanceIdNodeIfMissing(xmlDoc);\n\n const model = docToString(xmlDoc);\n\n // @ts-expect-error - This fails because `xform` is not optional, but this is API-consistent behavior.\n delete survey.xform;\n delete survey.media;\n delete survey.preprocess;\n delete survey.markdown;\n delete survey.openclinica;\n\n return Object.assign(survey, {\n form,\n model,\n languageMap,\n transformerVersion: PACKAGE_VERSION,\n });\n};\n\ninterface XSLTParams {\n openclinica?: number;\n}\n\nconst xslTransform = (\n xslDoc: DOM.Document,\n xmlDoc: DOM.Document,\n xsltParams: XSLTParams = {} as XSLTParams\n) => {\n const xsltProcessor = new XSLTProcessor();\n\n xsltProcessor.importStylesheet(xslDoc);\n\n Object.entries(xsltParams).forEach(([key, value]) => {\n xsltProcessor.setParameter(null, key, value);\n });\n\n return xsltProcessor.transformToDocument(xmlDoc);\n};\n\nconst getNamespaceResolver = (namespaces: Record<string, string>) => ({\n lookupNamespaceURI: (prefix: string) => namespaces[prefix] ?? null,\n});\n\nconst isDocument = (node: DOM.Node | DOM.Document): node is DOM.Document =>\n node.nodeType === NodeTypes.DOCUMENT_NODE;\n\nconst evaluateXPathExpression = (\n context: DOM.Document | DOM.Element,\n expression: string,\n resultType: DOM.XPathResultType,\n namespaces?: Record<string, string>\n) => {\n const namespaceResolver =\n namespaces == null ? null : getNamespaceResolver(namespaces);\n const doc = isDocument(context) ? context : context.ownerDocument;\n\n if (doc == null) {\n throw new Error('Could not find owner document');\n }\n\n return doc.evaluate(expression, context, namespaceResolver, resultType);\n};\n\nconst getNodesByXPathExpression = <\n T extends DOM.Element | DOM.Attr = DOM.Element,\n>(\n context: DOM.Document | DOM.Element,\n expression: string,\n namespaces?: Record<string, string>\n) => {\n const results: T[] = [];\n const result = evaluateXPathExpression(\n context,\n expression,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n namespaces\n );\n\n for (let i = 0; i < (result.snapshotLength ?? 0); i += 1) {\n results.push(result.snapshotItem?.(i) as DOM.Node as T);\n }\n\n return results;\n};\n\nconst getNodeByXPathExpression = <\n T extends DOM.Element | DOM.Attr = DOM.Element,\n>(\n context: DOM.Document | DOM.Element,\n expression: string,\n namespaces?: Record<string, string>\n) => {\n const { singleNodeValue } = evaluateXPathExpression(\n context,\n expression,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n namespaces\n );\n\n return singleNodeValue as T | null;\n};\n\nconst processBinaryDefaults = (\n doc: DOM.Document,\n mediaMap: Record<string, string>\n) => {\n getNodesByXPathExpression(\n doc,\n '/h:html/h:head/xmlns:model/xmlns:bind[@type=\"binary\"]',\n NAMESPACES\n ).forEach((bind) => {\n const nodeset = bind.getAttribute('nodeset');\n\n if (nodeset) {\n const path = `/h:html/h:head/xmlns:model/xmlns:instance${nodeset.replace(\n /\\//g,\n '/xmlns:'\n )}`;\n const dataNode = getNodeByXPathExpression(doc, path, NAMESPACES);\n\n if (dataNode) {\n const text = dataNode.textContent ?? '';\n\n // Very crude URL checker which is fine for now,\n // because at this point we don't expect anything other than jr://\n if (/^[a-zA-Z]+:\\/\\//.test(text)) {\n const value = getMediaPath(mediaMap, text);\n const escapedText = escapeURLPath(text);\n\n dataNode.setAttribute('src', value);\n dataNode.textContent = escapedText;\n }\n }\n }\n });\n};\n\n/**\n * Correct some <setvalue>/<odk:setgeopoint> issues in the XSL stylesheets.\n * This is much easier to correct in javascript than in XSLT\n */\nconst correctAction = (\n doc: DOM.Document,\n localName: 'setvalue' | 'setgeopoint' = 'setvalue'\n) => {\n /*\n * See setvalue.xml (/data/person/age_changed). A <setvalue> inside a form control results\n * in one label.question with a nested label.setvalue which is weird syntax (and possibly invalid HTML).\n */\n getNodesByXPathExpression(\n doc,\n `//*[contains(@class, \"question\")]//label/input[@data-${localName}]`\n ).forEach((setValueEl) => {\n const { parentElement } = setValueEl;\n\n if (parentElement != null) {\n const clone = setValueEl.cloneNode(true);\n\n parentElement.replaceWith(clone);\n }\n });\n\n /*\n * See setvalue.xml (/data/person/age). A <setvalue> inside a repeat to set a default value that also has a question with the same name, results\n * in one .question and .setvalue with the same name, which will leads to all kinds of problems in enketo-core\n * as name is presumed to be unique.\n *\n * Note that a label.setvalue is always to set a default value (with odk-new-repeat, odk-instance-first-load), never\n * a value change directive (with xforms-value-changed)\n */\n getNodesByXPathExpression(\n doc,\n `//label[contains(@class, \"${localName}\")]/input[@data-${localName}]`\n ).forEach((setValueEl) => {\n const name = setValueEl.getAttribute('name');\n const questionSameName = getNodeByXPathExpression(\n doc,\n `//*[@name=\"${name}\" and ( contains(../@class, 'question') or contains(../../@class, 'option-wrapper')) and not(@type='hidden')]`\n );\n if (questionSameName) {\n // Note that if the question has radiobuttons or checkboxes only the first of those gets the setvalue attributes.\n [`data-${localName}`, 'data-event'].forEach((name) => {\n questionSameName.setAttribute(\n name,\n setValueEl.getAttribute(name) ?? name\n );\n });\n setValueEl.parentElement?.remove();\n }\n });\n};\n\nconst replaceTheme = (doc: DOM.Document, theme?: string) => {\n const HAS_THEME = /(theme-)[^\"'\\s]+/;\n\n if (!theme) {\n return;\n }\n\n const form = getNodeByXPathExpression(doc.documentElement, '/root/form');\n\n if (form == null) {\n throw new Error('Form is missing');\n }\n\n const formClass = form.getAttribute('class');\n\n if (formClass != null && HAS_THEME.test(formClass)) {\n form.setAttribute('class', formClass.replace(HAS_THEME, `$1${theme}`));\n } else {\n form.setAttribute('class', `${formClass ?? ''} theme-${theme}`);\n }\n};\n\nconst replaceMediaSources = (\n root: DOM.Document,\n mediaMap?: Record<string, string>\n) => {\n if (!mediaMap) {\n return;\n }\n\n // iterate through each element with a src attribute\n getNodesByXPathExpression(root, '//*[@src] | //a[@href]').forEach(\n (mediaEl) => {\n const attribute =\n mediaEl.nodeName.toLowerCase() === 'a' ? 'href' : 'src';\n const src = mediaEl.getAttribute(attribute);\n\n if (src == null) {\n return;\n }\n\n const replacement = getMediaPath(mediaMap, src);\n\n if (replacement) {\n mediaEl.setAttribute(attribute, replacement);\n }\n }\n );\n\n // add form logo <img> element if applicable\n const formLogo = mediaMap['form_logo.png'];\n const formLogoEl = getNodeByXPathExpression(\n root,\n '//*[@class=\"form-logo\"]'\n );\n if (formLogo && formLogoEl) {\n const formLogoImg = root.createElement('img');\n\n formLogoImg.setAttribute('src', formLogo);\n formLogoImg.setAttribute('alt', 'form logo');\n\n formLogoEl.append(formLogoImg);\n }\n};\n\n/**\n * Replaces all lang attributes to the valid IANA tag if found.\n * Also add the dir attribute to the languages in the language selector.\n *\n * @see http://www.w3.org/International/questions/qa-choosing-language-tags\n */\nconst replaceLanguageTags = (doc: DOM.Document) => {\n const languageMap: Record<string, string> = {};\n\n const languageElements = getNodesByXPathExpression(\n doc,\n '/root/form/select[@id=\"form-languages\"]/option'\n );\n\n // List of parsed language objects\n const languages = languageElements.map((el) => {\n const lang = el.textContent ?? '';\n\n return parseLanguage(lang, getLanguageSampleText(doc, lang));\n });\n\n // forms without itext and only one language, still need directionality info\n if (languages.length === 0) {\n languages.push(parseLanguage('', getLanguageSampleText(doc, '')));\n }\n\n // add or correct dir and value attributes, and amend textcontent of options in language selector\n languageElements.forEach((el, index) => {\n const val = el.getAttribute('value');\n\n if (val && val !== languages[index].tag) {\n languageMap[val] = languages[index].tag;\n }\n\n el.setAttribute('data-dir', languages[index].directionality);\n el.setAttribute('value', languages[index].tag);\n el.textContent = languages[index].description;\n });\n\n // correct lang attributes\n languages.forEach(({ sourceLanguage, tag }) => {\n if (sourceLanguage === tag) {\n return;\n }\n getNodesByXPathExpression(\n doc,\n `/root/form//*[@lang=\"${sourceLanguage}\"]`\n ).forEach((el) => {\n el.setAttribute('lang', tag);\n });\n });\n\n // correct default lang attribute\n const langSelectorElement = getNodeByXPathExpression(\n doc,\n '/root/form/*[@data-default-lang]'\n );\n if (langSelectorElement) {\n const defaultLang =\n langSelectorElement.getAttribute('data-default-lang');\n\n languages.some(({ sourceLanguage, tag }) => {\n if (sourceLanguage === defaultLang) {\n langSelectorElement.setAttribute('data-default-lang', tag);\n\n return true;\n }\n\n return false;\n });\n }\n\n return languageMap;\n};\n\n/**\n * Obtains a non-empty hint text or other text sample of a particular form language.\n */\nconst getLanguageSampleText = (doc: DOM.Document, language: string) => {\n // First find non-empty text content of a hint with that lang attribute.\n // If not found, find any span with that lang attribute.\n const langSampleEl =\n getNodeByXPathExpression(\n doc,\n `/root/form//span[contains(@class, \"or-hint\") and @lang=\"${language}\" and normalize-space() and not(./text() = '-')]`\n ) ||\n getNodeByXPathExpression(\n doc,\n `/root/form//span[@lang=\"${language}\" and normalize-space() and not(./text() = '-')]`\n );\n\n return langSampleEl?.textContent?.trim() || 'nothing';\n};\n\n/**\n * Temporary function to add a /meta/instanceID node if this is missing.\n * This used to be done in enketo-xslt but was removed when support for namespaces was added.\n */\nconst addInstanceIdNodeIfMissing = (doc: DOM.Document) => {\n const xformsPath =\n '/xmlns:root/xmlns:model/xmlns:instance/*/xmlns:meta/xmlns:instanceID';\n const openrosaPath =\n '/xmlns:root/xmlns:model/xmlns:instance/*/orx:meta/orx:instanceID';\n const instanceIdEl = getNodeByXPathExpression(\n doc,\n `${xformsPath} | ${openrosaPath}`,\n NAMESPACES\n );\n\n if (!instanceIdEl) {\n const rootEl = getNodeByXPathExpression(\n doc,\n '/xmlns:root/xmlns:model/xmlns:instance/*',\n NAMESPACES\n );\n const metaEl = getNodeByXPathExpression(\n doc,\n '/xmlns:root/xmlns:model/xmlns:instance/*/xmlns:meta',\n NAMESPACES\n );\n\n const instanceID = doc.createElementNS(NAMESPACES.xmlns, 'instanceID');\n\n if (metaEl) {\n metaEl.append(instanceID);\n } else if (rootEl) {\n const meta = doc.createElementNS(NAMESPACES.xmlns, 'meta');\n\n rootEl.append(meta);\n meta.append(instanceID);\n }\n }\n};\n\n/**\n * Converts a subset of Markdown in all textnode children of labels and hints into HTML\n */\nconst renderMarkdown = (\n htmlDoc: DOM.Document,\n mediaMap: Record<string, string>\n) => {\n const replacements: Record<string, string> = {};\n\n // First turn all outputs into text so *<span class=\"or-output></span>* can be detected\n getNodesByXPathExpression(\n htmlDoc,\n '/root/form//span[contains(@class, \"or-output\")]'\n ).forEach((el, index) => {\n const key = `---output-${index}`;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const textNode = el.firstChild!.cloneNode(true);\n replacements[key] = el.outerHTML;\n textNode.textContent = key;\n el.replaceWith(textNode);\n // Note that we end up in a situation where we likely have sibling text nodes...\n });\n\n const domParser = new DOMParser();\n\n // Now render markdown\n getNodesByXPathExpression(\n htmlDoc,\n '/root/form//span[contains(@class, \"question-label\") or contains(@class, \"or-hint\")]'\n ).forEach((el, index) => {\n let key;\n /**\n * Using text() is done because:\n * a) We are certain that these <span>s do not contain other elements, other than formatting/markdown <span>s.\n * b) This avoids the need to merge any sibling text nodes that could have been created in the previous step.\n *\n * Note that text() will convert &gt; to >\n */\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const original = el\n .textContent!.replace('<', '&lt;')\n .replace('>', '&gt;');\n let rendered = markdownToHTML(original);\n\n if (original !== rendered) {\n const tempDoc = domParser.parseFromString(\n `<root class=\"temporary-root\">${rendered}</root>`,\n 'text/html'\n );\n\n correctHTMLDocHierarchy(tempDoc);\n replaceMediaSources(tempDoc, mediaMap);\n\n rendered = docToString(tempDoc);\n key = `$$$${index}`;\n replacements[key] = rendered;\n el.textContent = key;\n }\n });\n\n let htmlStr = docToString(htmlDoc);\n\n // Now replace the placeholders with the rendered HTML\n // in reverse order so outputs are done last\n Object.keys(replacements)\n .reverse()\n .forEach((key) => {\n const replacement = replacements[key];\n if (replacement) {\n /**\n * The replacement is called as a function here so special string\n * replacement sequences are preserved if they appear within Markdown.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_the_replacement}\n */\n htmlStr = htmlStr.replace(key, () => replacement);\n }\n });\n\n return htmlStr;\n};\n\nconst docToString = (doc: DOM.Document) => {\n const { outerHTML } = doc.documentElement;\n\n // It would be tempting to use `innerHTML`, as this is semantically\n // equivalent. But removing the open and closing tags from `outerHTML`\n // produces consistent namespace declarations across environments.\n return outerHTML.replace(/^<[^>]+>/, '').replace(/<\\/[^>]+>(?!.|\\n)$/, '');\n};\n\nexport const version = VERSION;\n\nexport const sheets = {\n xslForm,\n xslModel,\n};\n\nexport { escapeURLPath, NAMESPACES };\n\n/**\n * Exported for backwards compatibility, prefer named imports from enketo-transformer's index module.\n */\nexport default {\n transform,\n version: VERSION,\n NAMESPACES,\n sheets,\n escapeURLPath,\n};\n\nconst correctHTMLDocHierarchy = (doc: DOM.Document) => {\n const { documentElement } = doc;\n\n if (documentElement.nodeName.toLowerCase() === 'html') {\n const root = getNodeByXPathExpression(doc, '/html/body/root');\n\n if (root == null) {\n throw new Error('Missing root node.');\n }\n\n documentElement.replaceWith(root);\n }\n};\n\n/** @see {@link processItemsets} */\nconst substringBefore = (haystack: string, needle: string) =>\n haystack.split(needle, 1)[0];\n\n/** @see {@link processItemsets} */\nconst substringAfter = (haystack: string, needle: string) =>\n haystack.substring(haystack.indexOf(needle) + needle.length);\n\n/**\n * This is a replacement for the XSL template named `strip-filter`. The original\n * XSL commentary follows:\n *\n * ----------------------------------------------------------------------------\n * turns: /path/to/node[value=/some/other/node] into: /path/to/node this\n * function is probably way too aggressive but will work for xls-form generated\n * forms to do this properly a regexp:replace is required, but not supported in\n * libXML kept the recursion in, even though it is not being used right now\n * ----------------------------------------------------------------------------\n *\n * Contrary to the JSDoc comment for @see {@link processItemsets},\n * this implementation deviates from the original XSL logic, which uses\n * substrings and recursion, because it's (hopefully) much easier to understand\n * this way.\n *\n * It is *also* worth noting that in the process of migrating to browser DOM,\n * the original commentary was found to be wrong! The recursion was certainly in\n * use, and without it some snapshot tests failed.\n */\nconst stripFilter = (expression: string) => expression.replace(/\\[.*?\\]/g, '');\n\n/**\n * This is a replacement for the dynamic logic in the XSL template\n * `match=\"xf:itemset\" mode=\"labels\"`. Moving it to DOM code allows us to drop\n * our reliance on the unsupported `dyn` extension. The intent is to match the\n * naming and semantics of the original XSL logic to ensure there is no\n * potential for regressions.\n */\nconst processItemsets = (xformDoc: DOM.Document) => {\n const itemsets = getNodesByXPathExpression(\n xformDoc,\n '//xmlns:itemset',\n NAMESPACES\n );\n\n itemsets.forEach((itemset) => {\n const valueEl = getNodeByXPathExpression(\n itemset,\n './xmlns:value',\n NAMESPACES\n );\n const valueRef = valueEl?.getAttribute('ref') ?? '';\n const labelEl = getNodeByXPathExpression(\n itemset,\n './xmlns:label',\n NAMESPACES\n );\n const labelRef = labelEl?.getAttribute('ref') ?? '';\n const nodeset = itemset.getAttribute('nodeset') ?? '';\n const iwq = substringBefore(substringAfter(nodeset, 'instance('), ')');\n\n // TODO (2023-01-15): The following comment is directly from the\n // previous XSL implementation. But because we *do* now implement this\n // in a general purpose language, we can address these limitations.\n\n // Needs to also deal with randomize(instance(\"id\")/path/to/node), randomize(instance(\"id\")/path/to/node, 3)\n // Super inelegant and not robust without regexp:match\n let instancePathTemp: string;\n\n const nodesetIncludesRandomize = nodeset.includes('randomize(');\n\n if (nodesetIncludesRandomize && nodeset.includes(',')) {\n instancePathTemp = substringBefore(\n substringAfter(nodeset, ')'),\n ','\n );\n } else if (nodesetIncludesRandomize) {\n instancePathTemp = substringBefore(\n substringAfter(nodeset, ')'),\n ')'\n );\n } else {\n instancePathTemp = substringAfter(nodeset, ')');\n }\n\n const instancePath = instancePathTemp.replace(/\\//g, '/xf:');\n const instancePathNoFilter = stripFilter(instancePath);\n const instanceId = iwq.substring(1, iwq.length - 1);\n const itextPath = `/h:html/h:head/xf:model/xf:instance[@id=\"${instanceId}\"]${instancePathNoFilter}`;\n\n itemset.setAttribute('valueRef', valueRef);\n itemset.setAttribute('labelRef', labelRef);\n itemset.setAttribute('itextPath', `${itextPath}`);\n\n const [, labelNodeName] = labelRef.match(/itext\\((.*)\\)/) ?? [];\n\n if (labelNodeName != null) {\n const labelPath = `${itextPath.replace(\n /\\/xf:/g,\n '/xmlns:'\n )}/*[name() = \"${labelNodeName}\"]`;\n const items = getNodesByXPathExpression(\n xformDoc,\n labelPath,\n NAMESPACES\n );\n\n itemset.append(...items.map((item) => item.cloneNode(true)));\n }\n });\n};\n\n/**\n * This is a replacement for the logic previously implemented in the\n * `appearance` template in `openrosa2html5form.xsl`. That template now\n * transforms existing appearance values directly to a `data-appearance`\n * attribute, and the previous logic is applied here based on those values.\n */\nconst transformAppearances = (doc: DOM.Document) => {\n const appearanceElements = getNodesByXPathExpression(\n doc,\n '//*[@data-appearances]'\n );\n\n appearanceElements.forEach((element) => {\n const selectType = element.hasAttribute('data-appearances-select-type');\n\n if (selectType) {\n element.removeAttribute('data-appearances-select-type');\n }\n\n const appearances =\n element\n .getAttribute('data-appearances')\n ?.trim()\n .toLowerCase()\n .split(/\\s+/) ?? [];\n\n const appearanceClasses = appearances.flatMap((appearance) => {\n const results = [`or-appearance-${appearance}`];\n\n // Convert deprecated appearances, but leave the deprecated ones.\n if (selectType) {\n if (appearance === 'horizontal') {\n results.push('or-appearance-columns');\n }\n\n if (appearance === 'horizontal-compact') {\n results.push('or-appearance-columns-pack');\n }\n\n if (appearance === 'compact') {\n results.push(\n 'or-appearance-columns-pack',\n 'or-appearance-no-buttons'\n );\n }\n\n if (appearance.startsWith('compact-')) {\n results.push(\n appearance.replace('compact-', ''),\n 'or-appearance-no-buttons'\n );\n }\n }\n\n return results;\n });\n\n const classes =\n element.getAttribute('class')?.trim()?.split(/\\s+/) ?? [];\n\n const className = classes\n .flatMap((className) =>\n className === 'or-appearances-placeholder'\n ? appearanceClasses\n : className\n )\n .join(' ');\n\n element.setAttribute('class', className);\n element.removeAttribute('data-appearances');\n });\n};\n\nconst XMLNS_URI = 'http://www.w3.org/2000/xmlns/';\n\ndeclare const document: DOM.Document | undefined;\n\n/**\n * This addresses a difference in how Firefox treats XML namespace declarations\n * versus other browsers, as well as `libxslt`. In `openrosa2xmlmodel.xsl`, it's\n * expected that `xmlns` and `xmlns:*` are treated as attributes, but evidently\n * this is not the case in Firefox. So for consistency we copy the namespace\n * declarations in the browser DOM.\n *\n * An important note: understanding what the actual behavior originally was\n * turned out to be rather difficult, so this is the current behavior (which may\n * not exactly be *expected* e.g. by Enketo Core):\n *\n * For each namespace declaration defined on the XForm's root `html` element,\n * assign them to the transformed model's primary `instance` element, **except if**:\n *\n * - The namespace is also defined on the `model`.\n * - The namespace prefix is used on any of the `model`'s attributes.\n * - The namespace is also defined in `openrosa2xmlmodel.xsl`.\n *\n * TODO (2023-01-16):\n *\n * - All of the exceptions described above are either known bugs or likely to be.\n *\n * - This leaves very little remaining responsibilities of\n * `openrosa2xmlmodel.xsl`. We should seriously consider moving the remainder\n * to DOM code.\n */\nconst correctModelNamespaces = (\n xslDoc: DOM.Document,\n xformDoc: DOM.Document,\n modelDoc: DOM.Document\n) => {\n if (typeof document === 'undefined') {\n return;\n }\n\n const { documentElement: xslRoot } = xslDoc;\n const instanceRoots = getNodesByXPathExpression(\n modelDoc,\n '/xmlns:root/xmlns:model/xmlns:instance/*',\n NAMESPACES\n );\n const model = instanceRoots[0].parentElement?.parentElement;\n const xformModel = getNodeByXPathExpression(\n xformDoc,\n '/h:html/h:head/xmlns:model',\n NAMESPACES\n );\n\n if (model == null || xformModel == null) {\n throw new Error('XForm is missing a model element.');\n }\n\n instanceRoots.forEach((instanceRoot) => {\n const xformModelAttrNamespaces = [...xformModel.attributes]\n .filter(\n ({ name }) => name !== 'xmlns' && !name.startsWith('xmlns:')\n )\n .map(({ namespaceURI }) => namespaceURI);\n\n const missingNamespaceAttrs = [\n ...xformDoc.documentElement.attributes,\n ].filter(\n ({ name, value }) =>\n (name === 'xmlns' || name.startsWith('xmlns:')) &&\n !xslRoot.hasAttribute(name) &&\n !xformModelAttrNamespaces.includes(value) &&\n !instanceRoot.hasAttribute(name)\n );\n\n missingNamespaceAttrs.forEach(({ name, value }) => {\n instanceRoot.setAttributeNS(XMLNS_URI, name, value);\n });\n });\n};\n"],"names":["markdown","escapeURLPath","DOMParser","xslForm","xslModel","XSLTProcessor","NodeTypes","XPathResult","NAMESPACES","getMediaPath","name","parseLanguage","language","markdownToHTML","className"],"mappings":"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"}