You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							128 lines
						
					
					
						
							3.6 KiB
						
					
					
				
			
		
		
	
	
							128 lines
						
					
					
						
							3.6 KiB
						
					
					
				"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(/&/g, "&") | 
						|
    .replace(/"/g, """) | 
						|
    .replace(/</g, "<") | 
						|
    .replace(/>/g, ">") | 
						|
    .replace(/\t/g, "	") | 
						|
    .replace(/\n/g, "
") | 
						|
    .replace(/\r/g, "
"); | 
						|
} | 
						|
 | 
						|
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;
 | 
						|
 |