aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/w3c-xmlserializer/lib/attributes.js
blob: 5012e5bde7eb61c28b8b1260b70b0c239b415174 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"use strict";

const xnv = require("xml-name-validator");

const { NAMESPACES } = require("./constants");

function generatePrefix(map, newNamespace, prefixIndex) {
  const generatedPrefix = `ns${prefixIndex}`;
  map[newNamespace] = [generatedPrefix];
  return generatedPrefix;
}

function preferredPrefixString(map, ns, preferredPrefix) {
  const candidateList = map[ns];
  if (!candidateList) {
    return null;
  }
  if (candidateList.includes(preferredPrefix)) {
    return preferredPrefix;
  }
  return candidateList[candidateList.length - 1];
}

function serializeAttributeValue(value/* , requireWellFormed*/) {
  if (value === null) {
    return "";
  }
  // TODO: Check well-formedness
  return value
    .replace(/&/ug, "&")
    .replace(/"/ug, """)
    .replace(/</ug, "&lt;")
    .replace(/>/ug, "&gt;")
    .replace(/\t/ug, "&#x9;")
    .replace(/\n/ug, "&#xA;")
    .replace(/\r/ug, "&#xD;");
}

function serializeAttributes(
  element,
  map,
  localPrefixes,
  ignoreNamespaceDefAttr,
  requireWellFormed,
  refs
) {
  let result = "";
  const namespaceLocalnames = Object.create(null);
  for (const attr of element.attributes) {
    if (
      requireWellFormed &&
      namespaceLocalnames[attr.namespaceURI] &&
      namespaceLocalnames[attr.namespaceURI].has(attr.localName)
    ) {
      throw new Error("Found duplicated attribute");
    }
    if (!namespaceLocalnames[attr.namespaceURI]) {
      namespaceLocalnames[attr.namespaceURI] = new Set();
    }
    namespaceLocalnames[attr.namespaceURI].add(attr.localName);
    const attributeNamespace = attr.namespaceURI;
    let candidatePrefix = null;
    if (attributeNamespace !== null) {
      candidatePrefix = preferredPrefixString(
        map,
        attributeNamespace,
        attr.prefix
      );
      if (attributeNamespace === NAMESPACES.XMLNS) {
        if (
          attr.value === NAMESPACES.XML ||
          (attr.prefix === null && ignoreNamespaceDefAttr) ||
          (attr.prefix !== null &&
            localPrefixes[attr.localName] !== attr.value &&
            map[attr.value].includes(attr.localName))
        ) {
          continue;
        }
        if (requireWellFormed && attr.value === NAMESPACES.XMLNS) {
          throw new Error(
            "The XMLNS namespace is reserved and cannot be applied as an element's namespace via XML parsing"
          );
        }
        if (requireWellFormed && attr.value === "") {
          throw new Error(
            "Namespace prefix declarations cannot be used to undeclare a namespace"
          );
        }
        if (attr.prefix === "xmlns") {
          candidatePrefix = "xmlns";
        }
      } else if (candidatePrefix === null) {
        candidatePrefix = generatePrefix(
          map,
          attributeNamespace,
          refs.prefixIndex++
        );
        result += ` xmlns:${candidatePrefix}="${serializeAttributeValue(
          attributeNamespace,
          requireWellFormed
        )}"`;
      }
    }

    result += " ";
    if (candidatePrefix !== null) {
      result += `${candidatePrefix}:`;
    }
    if (
      requireWellFormed &&
      (attr.localName.includes(":") ||
        !xnv.name(attr.localName) ||
        (attr.localName === "xmlns" && attributeNamespace === null))
    ) {
      throw new Error("Invalid attribute localName value");
    }
    result += `${attr.localName}="${serializeAttributeValue(attr.value, requireWellFormed)}"`;
  }
  return result;
}

module.exports.preferredPrefixString = preferredPrefixString;
module.exports.generatePrefix = generatePrefix;
module.exports.serializeAttributeValue = serializeAttributeValue;
module.exports.serializeAttributes = serializeAttributes;