var xml = xml || {}; var text = text || require('./text'); // https://www.w3.org/TR/xml xml.TextReader = class { static open(data, callback) { const decoder = text.Decoder.open(data); for (;;) { const c = decoder.decode(); if (c === '<') { break; } if (c === ' ' || c === '\n' || c === '\r' || c === '\t') { continue; } return null; } return new xml.TextReader(data, callback); } constructor(data, callback) { this._data = data; this._callback = callback; this._entities = new Map([ [ 'quot', '"' ], [ 'amp', '&' ], [ 'apos', "'" ], [ 'lt', '<' ], [ 'gt', '>' ] ]); this._nameStartCharRegExp = /[:A-Z_a-z\xC0-\xD6\xD8-\xF6\xF8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/; this._nameCharRegExp = new RegExp("[-.0-9\\xB7" + this._nameStartCharRegExp.source.slice(1, -1) + "]"); xml.Utility.nameStartCharRegExp = this._nameStartCharRegExp; } peek() { this._peek = true; const value = this.read(); delete this._peek; return value; } read() { this._stack = []; this._context = []; this._pushBuffer(this._data, '', '', false); this._version = 0; /* eslint-disable */ this._charRegExp = /[\x09\x0a\x0d\x20-\uD7FF\uE000-\uFFFD]/; /* eslint-enable */ this._parameterEntities = false; this._characterData = true; this._push(new xml.Document()); const document = this._document(); for (;;) { this._start = this._position; switch (this._char) { case '<': { this._next(); switch (this._char) { case '?': { this._processingInstruction(); break; } case '!': { this._next(); if (this._match('--')) { this._comment(); } else if (this._match('[CDATA')) { this._assert(this._stack.length > 1); this._characterData = true; this._expect('['); const data = this._terminal(']]>'); const node = document.createCDATASection(data); this._appendChild(node); } else if (this._match('DOCTYPE')) { this._assert(this._stack.length > 1 || !document.documentElement || !document.documentType); this._whitespace(1); const name = this._name(); this._assert(name !== null); let systemId = ''; let publicId = ''; let whitespace = this._whitespace(0); if (whitespace && this._match('SYSTEM')) { this._whitespace(1); systemId = this._systemLiteral(); this._whitespace(0); whitespace = true; } else if (whitespace && this._match('PUBLIC')) { this._whitespace(1); publicId = this._pubidLiteral(); this._whitespace(1); systemId = this._systemLiteral(); this._whitespace(0); whitespace = true; } const node = document.createDocumentType(name, publicId, systemId); this._appendChild(node); this._push(node); node.parameterEntities = new xml.NamedNodeMap(); node.elements = new xml.NamedNodeMap(); this._parameterEntities = true; this._characterData = false; const internalSubset = whitespace && this._match('['); if (internalSubset) { this._internalSubset(']'); } if (systemId && !this._standalone) { this._pushResource(systemId, '', true); this._internalSubset(undefined); this._popContext(); } this._characterData = true; this._parameterEntities = false; const values = node.entities.filter((entity) => entity.value).map((entity) => entity.value); for (const entity of node.entities.filter((entity) => entity.notationName)) { const reference = '&' + entity.localName + ';'; if (values.some((value) => value.indexOf(reference) >= 0)) { this._error("Entity references unparsed entity '" + entity.localName + "'"); } } if (internalSubset) { this._expect(']'); this._whitespace(0); } this._expect('>'); this._assert(this._pop().nodeType === xml.NodeType.DocumentType); } else { this._unexpected(); } break; } case '/': { this._next(); const name = this._name(); this._assert(name !== null); this._whitespace(0); this._expect('>'); const node = this._pop(); const nodeName = node.prefix ? node.prefix + ':' + node.localName : node.localName; if (name !== nodeName) { this._error("Opening tag <" + nodeName + "> and ending tag mismatch", this._start); } break; } default: { this._assert(this._stack.length > 1 || !this._document.documentElement); const name = this._name(); this._assert(name !== null); this._assert(!name.startsWith('xmlns:')); const attributes = []; let whitespace = this._whitespace(0); if (whitespace) { while (this._char !== '/' && this._char !== '>') { if (!whitespace) { this._unexpected(); } const position = this._position; const name = this._name(); if (!name) { this._unexpected(); } this._whitespace(0); this._expect('='); this._whitespace(0); const valuePosition = this._valuePosition; const value = this._attributeValue(); attributes.push({ qualifiedName: name, value: value, position: position, valuePosition: valuePosition }); whitespace = this._whitespace(0); if (name === 'xmlns' && (!this._validateNamespace(value) || value === 'http://www.w3.org/2000/xmlns/' || value === 'http://www.w3.org/XML/1998/namespace')) { this._error("Invalid namespace '" + value + "'", valuePosition); } if (name === 'xml:space' && value !== 'preserve' && value !== 'default') { this._error("Unexpected xml:space attribute value '" + value + "'", position); } } } const namespaces = new Map(); for (const entry of attributes.reverse()) { const name = entry.qualifiedName; const value = entry.value; const pair = xml.Utility.split(name); this._assert(name !== 'xmlns:'); entry.prefix = pair[0]; entry.localName = pair[1]; if (entry.prefix !== null) { this._assert(entry.localName !== ''); if (entry.prefix === 'xmlns' && entry.localName) { if (!this._validateNamespace(value) || value === 'http://www.w3.org/2000/xmlns/') { this._error("Invalid namespace '" + value + "'", entry.valuePosition); } if (entry.localName === 'xmlns' || (entry.localName === 'xml' && value !== 'http://www.w3.org/XML/1998/namespace') || (entry.localName !== 'xml' && value === 'http://www.w3.org/XML/1998/namespace')) { this._error("Invalid namespace prefix '" + entry.localName + "'", entry.position); } if (this._version === 0 && value.length === 0) { this._error("Invalid namespace declaration'", entry.position); } namespaces.set(entry.localName, value); } } else { if (entry.localName === 'xmlns') { namespaces.set('', value); } } } const pair = xml.Utility.split(name); const prefix = pair[0] || ''; const namespaceURI = namespaces.has(prefix) ? namespaces.get(prefix) : this._lookupNamespaceURI(prefix); let element = null; const documentType = document.documentType; const elementType = documentType ? documentType.elements.getNamedItem(name) : null; if (namespaceURI !== null) { this._assert(name === ':' || (!name.endsWith(':') && !name.startsWith(':'))); if (prefix && (namespaceURI === '' || namespaceURI === null)) { this._error("Invalid namespace prefix '" + prefix + "'", this._start); } element = document.createElementNS(namespaceURI, name); } else { this._assert((pair[0] === null && !name.endsWith(':')) || name === ':' || elementType !== null); element = document.createElement(name); } const parent = this._node(); if (parent.nodeType === xml.NodeType.Document && parent.documentElement !== null) { this._error('Duplicate document element', this._start); } this._appendChild(element); const keys = new Set(); for (const attr of attributes) { const name = attr.qualifiedName; const prefix = attr.prefix || ''; const namespaceURI = namespaces.has(prefix) ? namespaces.get(prefix) : this._lookupNamespaceURI(prefix); let attribute = null; if (namespaceURI) { attribute = document.createAttributeNS(namespaceURI, name); } else { const attributeType = elementType ? elementType.attributes.getNamedItem(name) : null; this._assert(name.indexOf(':') === -1 || attributeType); attribute = document.createAttribute(name); } const key = (attribute.namespaceURI || '') + '|' + attribute.localName; this._assert(!keys.has(key)); keys.add(key); attribute.value = attr.value; attribute.ownerElement = element; element.setAttributeNode(attribute); } const close = this._match('/'); this._expect('>'); if (this._peek && this._stack.length === 1 && this._nodeType() === xml.NodeType.Document) { return this._pop(); } if (!close) { this._push(element); } break; } } break; } default: { while (this._char === undefined && this._context.length > 0) { this._popContext(); } if (this._char === undefined) { if (this._stack.length === 1 && this._nodeType() === xml.NodeType.Document) { this._assert(document.documentElement); const documentType = document.documentType; if (documentType) { delete documentType.parameterEntities; delete documentType.elements; } const value = this._pop(); for (const key of Object.keys(this)) { if (key !== '_data' && key !== '_callback' && key !== '_entities' && !key.startsWith('_name')) { delete this[key]; } } return value; } this._unexpected(); } const node = this._node(); if (node.nodeType === xml.NodeType.Element) { const documentType = document.documentType; const name = node.prefix ? node.prefix + ':' + node.localName : node.localName; const elementType = documentType ? documentType.elements.getNamedItem(name) : null; this._characterData = elementType ? elementType.characterData : false; this._seek(this._position); const data = []; while (this._char !== '<' && this._char !== undefined) { if (this._char === ']' && this._match(']]>')) { this._unexpected(); } data.push(this._content()); if (data.length > 65536) { this._error('Invalid character data buffer size.'); } } if (data.length > 0) { const content = data.splice(0, data.length).join(''); if (content.trim().length > 0) { const node = document.createTextNode(content); this._appendChild(node); } } continue; } if (!this._whitespace(0)) { this._unexpected(); } break; } } } } _internalSubset(terminal) { for (;;) { this._start = this._position; switch (this._char) { case '<': { this._next(); switch (this._char) { case '?': { this._processingInstruction(); break; } case '!': { this._next(); if (this._match('--')) { this._parameterEntities = false; this._characterData = true; this._comment(); this._parameterEntities = true; } else if (this._match('ENTITY')) { const documentType = this._node(); this._assert(documentType.nodeType === xml.NodeType.DocumentType); this._parameterEntities = false; this._whitespace(1); const parameter = this._char === '%'; if (parameter) { this._next(); this._whitespace(1); } this._parameterEntities = true; const name = this._entityName(); const node = documentType.createEntity(name); let whitespace = this._whitespace(0); if (whitespace && (this._char === '"' || this._char === "'")) { node.value = this._entityValue(); whitespace = this._whitespace(0); } else { if (whitespace && this._match('SYSTEM')) { this._whitespace(1); node.systemId = this._systemLiteral(); whitespace = this._whitespace(0); } else if (whitespace && this._match('PUBLIC')) { this._whitespace(1); node.publicId = this._pubidLiteral(); this._whitespace(1); node.systemId = this._systemLiteral(); whitespace = this._whitespace(0); } else { this._unexpected(); } if (whitespace && !parameter) { if (this._match('NDATA')) { this._whitespace(1); const name = this._name(); this._assert(name !== null); node.notationName = name; this._whitespace(0); } } } this._expect('>'); if (parameter) { documentType.parameterEntities.setNamedItem(node); } else { this._appendChild(node); } } else if (this._match('ELEMENT')) { const documentType = this._node(); this._assert(documentType.nodeType === xml.NodeType.DocumentType); this._whitespace(1); const name = this._name(); this._assert(name !== null); this._whitespace(1); const elementType = this._elementType(name); if (this._match('EMPTY')) { this._whitespace(0); } else if (this._match('ANY')) { this._whitespace(0); } else { this._expect('('); this._whitespace(0); if (this._match('#PCDATA')) { elementType.characterData = true; this._whitespace(0); if (this._match(')')) { this._match('*'); } else { this._whitespace(0); while (this._match('|')) { this._whitespace(0); const name = this._name(); this._assert(name); this._whitespace(0); } this._expect(')*'); } } else { this._elementChildren(); } } this._whitespace(0); this._expect('>'); } else if (this._match('ATTLIST')) { const documentType = this._node(); this._assert(documentType.nodeType === xml.NodeType.DocumentType); this._whitespace(1); const name = this._name(); this._assert(name !== null); const elementType = this._elementType(name); while (this._whitespace(0)) { const attributeType = this._attributeDefinition(); if (!attributeType) { break; } elementType.attributes.setNamedItem(attributeType); } this._whitespace(0); this._expect('>'); } else if (this._match('NOTATION')) { this._assert(this._nodeType() === xml.NodeType.DocumentType); const notation = { systemId: null, publicId: null }; this._whitespace(1); notation.name = this._entityName(); let whitespace = this._whitespace(0); if (whitespace && this._match('SYSTEM')) { this._whitespace(1); notation.systemId = this._systemLiteral(); whitespace = this._whitespace(0); } if (whitespace && this._match('PUBLIC')) { this._whitespace(1); notation.publicId = this._pubidLiteral(); if (this._whitespace(0) && (this._char === '"') || this._char === "'") { notation.systemId = this._systemLiteral(); whitespace = this._whitespace(0); } } this._assert(notation.systemId || notation.publicId); this._expect('>'); } else if (this._match('[')) { this._whitespace(0); if (this._match('INCLUDE')) { this._assert(this._context.length > 0); this._whitespace(0); this._expect('['); this._internalSubset(']'); this._expect(']]>'); } else if (this._match('IGNORE')) { this._whitespace(0); this._expect('['); this._ignoreSectContents(); } } else { this._unexpected(); } } } break; } case '%': { this._resolveParameterEntityReference(); break; } default: { if (this._char === terminal) { return; } if (!this._whitespace(0)) { this._unexpected(); } break; } } } } _ignoreSectContents() { while (!this._match(']]>')) { if (this._match('= 0x10000 && c <= 0xEFFFF)) { name.push(this._char); this._next(); if (this._char !== undefined) { let c = this._char.codePointAt(0); while (this._nameCharRegExp.test(this._char) || (c >= 0x300 && c <= 0x36f) || (c >= 0x203F && c <= 0x2040)) { name.push(this._char); this._next(); if (this._char === undefined || this._implicitSpace) { break; } c = this._char.codePointAt(0); } } } if (name.length > 0) { return name.join(''); } this._seek(position); return null; } _nmtoken() { const position = this._position; const name = []; let c = this._char.codePointAt(0); while (this._nameCharRegExp.test(this._char) || (c >= 0x300 && c <= 0x36f) || (c >= 0x203F && c <= 0x2040)) { name.push(this._char); this._next(); if (this._char === undefined) { break; } c = this._char.codePointAt(0); } if (name.length > 0) { return name.join(''); } this._seek(position); return null; } _entityName() { const position = this._position; const name = this._name(); if (name === null) { this._error('Expected entity name', position); } if (!name.endsWith(':') && name.indexOf(':') !== -1) { this._error('Invalid colon in entity name', position); } return name; } _entityValue() { const quote = this._char; this._parameterEntities = false; this._characterData = true; const decoder = this._decoder; const position = this._position; this._next(); while (this._char !== quote) { if (this._char === undefined) { this._unexpected(); } this._next(); } const end = this._position; this._parameterEntities = true; this._seek(position); this._next(); const data = []; while (this._position !== end || this._decoder !== decoder) { if (this._char === undefined) { this._unexpected(); } if (this._char === '%') { if (this._context.length === 0) { this._error('Invalid parameter entity reference in internal subset'); } this._assert(); this._resolveParameterEntityReference(); continue; } if (this._char === '&') { data.push(this._entityReference()); this._expect(';'); continue; } data.push(this._char); this._next(); } this._next(); this._parameterEntities = true; this._characterData = false; return data.join(''); } _elementType(name) { const documentType = this._document().documentType; let elementType = documentType.elements.getNamedItem(name); if (!elementType) { elementType = { localName: name, characterData: false, attributes: new xml.NamedNodeMap() }; documentType.elements.setNamedItem(elementType); } return elementType; } _elementChildren() { let separator = undefined; const choice = new Set(); for (;;) { const name = this._name(); if (name) { this._assert(separator !== '|' || !choice.has(name)); choice.add(name); this._match('?') || this._match('*') || this._match('+'); this._whitespace(0); } else if (this._match('(')) { this._elementChildren(); this._whitespace(0); } else { this._unexpected(); } if (this._match(')')) { break; } if (separator && separator !== this._char) { this._unexpected(); } if (this._char !== '|' && this._char !== ',') { this._unexpected(); } separator = this._char; this._next(); this._whitespace(0); } this._match('?') || this._match('*') || this._match('+'); } _attributeDefinition() { this._whitespace(0); const name = this._name(); if (name) { this._whitespace(1); if (this._match('CDATA') || this._match('IDREFS') || this._match('IDREF') || this._match('ID') || this._match('ENTITIES') || this._match('ENTITY') || this._match('NMTOKENS') || this._match('NMTOKEN') || this._enumeratedType()) { this._whitespace(1); if (!this._match('#REQUIRED') && !this._match('#IMPLIED')) { if (this._match('#FIXED')) { this._whitespace(1); } this._parameterEntities = false; this._attributeValue(); this._parameterEntities = true; } return { localName: name }; } this._assert(false); } return null; } _enumeratedType() { if (this._match('NOTATION')) { this._whitespace(1); this._expect('('); do { this._whitespace(0); const name = this._name(); this._assert(name); this._whitespace(0); } while (this._match('|')); this._expect(')'); return true; } if (this._match('(')) { do { this._whitespace(0); const name = this._nmtoken(); this._assert(name); this._whitespace(0); } while (this._match('|')); this._expect(')'); return true; } return false; } _content() { const c = this._char !== '&' ? this._char : this._resolveEntityReference(); if (c === undefined) { return ''; } const code = c.codePointAt(0); if ((!this._charRegExp.test(c) && (code < 0x10000 || c > 0x10FFFF))) { this._unexpected(); } this._next(); return c; } _attributeValue() { const quote = this._char; if (quote !== '"' && quote !== "'") { this._unexpected(); } this._characterData = true; const decoder = this._decoder; const position = this._position; this._next(); while (this._char !== quote) { if (this._char === undefined || this._char === '<') { this._unexpected(); } this._next(); } const end = this._position; this._characterData = false; this._seek(position); this._next(); const data = []; while (this._position !== end || this._decoder !== decoder) { if (this._char === undefined && this._context.length > 0) { this._popContext(); continue; } if (this._char === '<') { this._unexpected(); } data.push(this._content()); if (data.length > 65536) { this._error('Invalid character data buffer size.'); } } this._characterData = true; this._next(); return data.join(''); } _validateNamespace(value) { if (value && (value.startsWith('#') || value.indexOf(':') === -1)) { return false; } if (this._version > 0) { return true; } return /^[A-Za-z0-9-._~:/?#[\]@!$&'()*+,;%=]*$/.exec(value) !== null; } _pubidLiteral() { const quote = this._char; if (quote !== '"' && quote !== "'") { this._unexpected(); } this._next(); const data = []; while (this._char !== quote) { if (/[a-zA-Z0-9-'()+,./:=?;!*#@$_%]/.test(this._char) || this._char === ' ' || this._char === '\r' || this._char === '\n') { data.push(this._char); this._next(); if (this._char === undefined) { this._unexpected(); } continue; } this._unexpected(); } this._next(); return data.join(''); } _systemLiteral() { const quote = this._char; if (quote !== '"' && quote !== "'") { this._unexpected(); } this._next(); const data = []; while (this._char !== quote) { data.push(this._char); this._next(); if (this._char === undefined) { this._unexpected(); } } this._next(); const value = data.join(''); if (value.indexOf('#') >= 0) { this._unexpected(); } const match = /(.*\/)[^/]*/.exec(this._base); return (match ? match[1] : '') + value; } _terminal(terminal) { const data = []; while (!this._match(terminal)) { if (this._char === undefined) { this._unexpected(); } const c = this._char.codePointAt(0); if (c !== 0x09 && c !== 0x0A && c !== 0x0D && (c < 0x20 || c > 0xD7FF) && (c < 0xE000 || c > 0xFFFD) && (c < 0x10000 || c > 0x10FFFF)) { this._unexpected(); } data.push(this._char); this._next(); } return data.join(''); } _resolveParameterEntityReference() { const position = this._position; this._next(); const name = this._name(); this._assert(name !== null); if (this._char === ';') { const entity = this._document().documentType.parameterEntities.getNamedItem(name); if (entity) { const implicitSpace = !this._entity && !this._context.some((context) => context.entity); if (entity.systemId) { this._pushResource(entity.systemId, name, false); } else { this._pushString(entity.value, name, false); } if (implicitSpace) { this._implicitSpace = true; } return; } this._error("Undefined ENTITY '" + name + "'", position); } this._unexpected(); } _resolveEntityReference() { const position = this._position; const entity = this._entityReference(); const name = entity.substring(1, entity.length - 1); if (name.startsWith('#x')) { const value = parseInt(name.substring(2), 16); return String.fromCodePoint(value); } else if (name.startsWith('#')) { const value = parseInt(name.substring(1), 10); return String.fromCodePoint(value); } else if (this._entities.has(name)) { return this._entities.get(name); } else { const documentType = this._document().documentType; const entity = documentType ? documentType.entities.getNamedItem(name) : null; if (entity) { if (entity.systemId) { this._pushResource(entity.systemId, name, true); } else { this._pushString(entity.value, name, true); } } else { if (this._context.length !== 0 || !documentType || documentType.parameterEntities.length === 0) { this._error("Undefined ENTITY '" + name + "'", position); } } return undefined; } } _entityReference() { if (this._char === '&') { const position = this._position; this._next(); if (this._match('#x')) { const data = []; while (/[0-9a-fA-F]/.test(this._char)) { data.push(this._char); this._next(); if (this._char === undefined) { this._unexpected(); } } this._assert(this._char === ';'); if (data.length > 0) { const text = data.join(''); const value = parseInt(text, 16); this._assert(value <= 0x10FFFF, "Invalid value '&#x" + text + ";'", position); return '&#x' + text + ';'; } } else if (this._match('#')) { const data = []; while (/[0-9]/.test(this._char)) { data.push(this._char); this._next(); if (this._char === undefined) { this._unexpected(); } } this._assert(this._char === ';'); if (data.length > 0) { const text = data.join(''); const value = parseInt(text, 10); this._assert(value <= 0x10FFFF, "Invalid value '&#" + text + ";'", position); return '&#' + text + ';'; } } else { const name = this._name(); this._assert(name !== null); this._assert(this._char === ';'); return '&' + name + ';'; } } this._unexpected(); } _comment() { const data = this._terminal('--'); const node = this._document().createComment(data); this._appendChild(node); this._expect('>'); } _processingInstruction() { this._next(); const name = this._entityName(); let whitespace = this._char === '?' ? false : this._whitespace(1); const position = this._position; const data = this._terminal('?>'); if (name.toLowerCase() === 'xml') { this._seek(position); this._assert(name === 'xml', "'" + name + "' must be lower case"); this._assert(this._start === this._prolog, "Prolog must start with XML declaration", this._start); this._assert(typeof this._data !== 'string', 'Invalid text declaration', this._start); const obj = { version: '', encoding: '', standalone: 'no' }; for (const name of Object.keys(obj)) { const expect = (name == 'version' && this._context.length === 0) || (name == 'encoding' && this._context.length > 0); if ((whitespace || expect) && (expect ? this._expect(name) : this._match(name))) { this._whitespace(0); this._expect('='); this._whitespace(0); obj[name] = this._attributeValue(); whitespace = this._whitespace(0); } } this._expect('?>'); obj.encoding = obj.encoding.toLowerCase(); if (this._decoder.encoding && obj.encoding !== this._decoder.encoding) { const position = this._position; this._decoder = text.Decoder.open(this._data, obj.encoding); this._seek(position); } if (obj.version.length > 0) { const match = /^(\d)\.(\d)$/.exec(obj.version); this._assert(match && match[1] === '1', "Invalid XML version '" + obj.version + "'"); const version = Number.parseInt(match[2], 10); if (version > this._version) { /* eslint-disable */ this._charRegExp = /[\x01-\uD7FF\uE000-\uFFFD]/; /* eslint-enable */ this._version = version; } this._assert(this._context.length === 0 || this._context.some((context) => context.version >= this._version)); } this._assert(obj.standalone === 'no' || (obj.standalone === 'yes' && !this._entity && this._context.length === 0)); this._standalone = obj.standalone === 'yes'; } const node = this._document().createProcessingInstruction(name, data); this._appendChild(node); } _whitespace(count) { const position = this._position; let index = 0; if (this._implicitSpace) { index++; this._implicitSpace = false; } while (this._char === ' ' || this._char === '\n' || this._char === '\r' || this._char === '\t' || (this._version > 0 && this._char === '\x85')) { index++; this._next(); } if (index < count) { this._seek(position); this._unexpected(); } return index > 0; } _pushResource(identifier, entity, stop) { const content = this._callback(identifier); this._pushBuffer(content, identifier, entity, stop); } _pushBuffer(data, base, entity, stop) { const signature = text.Decoder.open(data); const decoder = signature.encoding === 'utf-8' ? text.Decoder.open(data, 'utf-8') : signature; this._pushContext(decoder, data, base, entity, stop, false); this._data = data; } _pushString(value, entity, stop) { const decoder = text.Decoder.open(value); this._pushContext(decoder, value, this._base, entity, stop); } _pushContext(decoder, data, base, entity, stop) { if (this._context.some((context) => context && context.base === base && context.entity === entity)) { this._assert(!entity, "Recursive entity '" + entity + "'"); this._assert(!base, "Recursive base '" + base + "'"); } if (base.length !== 0 || entity.length !== 0) { this._context.push(this._state); } this._stop = stop; this._entity = entity; this._base = base; this._data = data; this._decoder = decoder; this._prolog = this._decoder.position; this._char = ''; this._next(); } _popContext() { const entity = this._entity; this._state = this._context.pop(); if (entity) { this._expect(';'); this._implicitSpace = !this._context.some((context) => context.entity); } } get _state() { return { base: this._base, data: this._data, decoder: this._decoder, position: this._position, version: this._version, entity: this._entity, prolog: this._prolog, stop: this._stop, }; } set _state(value) { this._stop = value.stop; this._base = value.base; this._data = value.data; this._decoder = value.decoder; this._seek(value.position); this._version = value.version; this._entity = value.entity; this._prolog = value.prolog; } _next() { if (this._char === undefined) { this._unexpected(); } this._position = this._decoder.position; this._char = this._decoder.decode(); this._implicitSpace = false; if (this._parameterEntities && this._char === '%' && (this._entity || this._base)) { this._resolveParameterEntityReference(); } if (!this._characterData) { if (this._char === '&' && (this._entity || this._base)) { const c = this._resolveEntityReference(); if (c !== undefined) { this._char = c; } } } if (this._char === '\uffff' || this._char === '\ufffe' || (this._version > 0 && this._char >= '\x7f' && this._char <= '\x9f' && this._char != '\x85')) { this._unexpected(); } if (this._char === undefined) { if (!this._stop && this._context.length > 0) { this._popContext(); } } } _seek(position) { this._decoder.position = position; this._char = ''; this._next(); } _expect(value) { if (!this._match(value)) { this._unexpected(); } return true; } _match(value) { if (this._char !== value[0]) { return false; } if (value.length === 1) { this._next(); return true; } if (this._context.length === 0) { const position = this._position; for (let i = 0; i < value.length; i++) { if (this._char !== value[i]) { this._seek(position); return false; } this._next(); } return true; } const context = Array.from(this._context); const state = this._state; for (let i = 0; i < value.length; i++) { if (this._char !== value[i]) { this._context = context; this._state = state; return false; } this._next(); } return true; } _assert(value, message, position) { if (value === false || value === undefined || value === null) { this._error(message, position); } } _error(message, position) { if (position) { this._parameterEntities = false; this._characterData = true; this._seek(position); } if (message) { throw new xml.Error(message + this._location()); } this._unexpected(); } _unexpected() { let c = this._char; if (c === undefined) { throw new xml.Error('Unexpected end of XML input.'); } else if (c === '"') { c = 'string'; } else if ((c >= '0' && c <= '9') || c === '-') { c = 'number'; } else { if (c < ' ' || c > '\x7F') { c = c.codePointAt(0); if (c < 0x0100) { c = '\\x' + ('0' + c.toString(16)).slice(-2); } else if (c < 0x010000) { c = '\\u' + ('000' + c.toString(16)).slice(-4); } else { c = '\\u' + ('00000' + c.toString(16)).slice(-6); } } c = "token '" + c + "'"; } this._error('Unexpected ' + c); } _location() { while (typeof this._data === 'string') { this._popContext(); } this._parameterEntities = false; this._characterData = true; let line = 1; let column = 1; this._decoder.position = 0; let c; do { if (this._decoder.position === this._position) { break; } c = this._decoder.decode(); if (c === '\n') { line++; column = 1; } else { column++; } } while (c !== undefined); return ' at ' + (this._base ? this._base + ':' : '') + line.toString() + ':' + column.toString() + '.'; } }; xml.NodeList = class extends Array { constructor() { super(); } item(index) { return this[index] || null; } }; xml.Node = class { constructor(document, nodeType) { this._ownerDocument = document; this._nodeType = nodeType; this._childNodes = new xml.NodeList(); } get ownerDocument() { return this._ownerDocument; } get nodeType() { return this._nodeType; } get localName() { throw new xml.Error('Not implemented.'); } get namespaceURI() { return null; } get childNodes() { return this._childNodes; } get parentNode() { return this._parentNode; } set parentNode(value) { this._parentNode = value; } get firstChild() { return this._firstChild; } set firstChild(value) { this._firstChild = value; } get lastChild() { return this._lastChild || null; } set lastChild(value) { this._lastChild = value; } get previousSibling() { return this._previousSibling; } set previousSibling(value) { this._previousSibling = value; } get nextSibling() { return this._nextSibling; } set nextSibling(value) { this._nextSibling = value; } appendChild(newChild) { this.firstChild = this.firstChild || newChild; newChild.previousSibling = this.lastChild; if (newChild.previousSibling) { newChild.previousSibling.nextSibling = newChild; } this.lastChild = newChild; this.childNodes[this.childNodes.length] = newChild; newChild.parentNode = this; } lookupNamespaceURI(prefix) { switch (prefix) { case 'xml': return 'http://www.w3.org/XML/1998/namespace'; case 'xmlns': return 'http://www.w3.org/2000/xmlns/'; } return null; } }; xml.Element = class extends xml.Node { constructor(document, namespaceURI, qualifiedName) { super(document, xml.NodeType.Element); this._namespaces = new Map(); this._attributes = new xml.NamedNodeMap(); this._namespaceURI = namespaceURI; if (namespaceURI === null) { this._prefix = null; this._localName = qualifiedName; } else { const pair = xml.Utility.split(qualifiedName); this._prefix = pair[0]; this._localName = pair[1]; } } get localName() { return this._localName; } get prefix() { return this._prefix; } get namespaceURI() { return this._namespaceURI; } get attributes() { return this._attributes; } get textContent() { return this.childNodes.map((node) => node.nodeType === xml.NodeType.ProcessingInstruction || node.nodeType === xml.NodeType.Comment ? '' : node.textContent).join(''); } getElementsByTagName(tagName) { const list = new xml.NodeList(); let node = this.firstChild; while (node) { if (node.nodeType === xml.NodeType.Element && (tagName === '*' || tagName === (node.prefix ? node.prefix + ':' + node.localName : node.localName))) { list.push(node); } node = node.nextSibling; } return list; } getAttribute(name) { const node = this.getAttributeNode(name); return node ? node.value || '' : ''; } getAttributeNode(name) { return this.attributes.getNamedItem(name); } setAttributeNode(node) { const oldNode = this.attributes.setNamedItem(node); if (node.namespaceURI === 'http://www.w3.org/2000/xmlns/') { const prefix = node.prefix ? node.localName : ''; this._namespaces.set(prefix, node.value); } return oldNode; } lookupNamespaceURI(prefix) { if (this._namespaces.has(prefix)) { return this._namespaces.get(prefix); } if (this.parentNode) { return this.parentNode.lookupNamespaceURI(prefix); } return super.lookupNamespaceURI(prefix); } }; xml.Attribute = class extends xml.Node { constructor(document, namespaceURI, qualifiedName) { super(document, xml.NodeType.Attribute); this._namespaceURI = namespaceURI; if (namespaceURI === null) { this._prefix = null; this._localName = qualifiedName; } else { const pair = xml.Utility.split(qualifiedName); this._prefix = pair[0]; this._localName = pair[1]; } } get ownerElement() { return this._ownerElement; } set ownerElement(value) { this._ownerElement = value; } get localName() { return this._localName; } get prefix() { return this._prefix; } get namespaceURI() { return this._namespaceURI; } get value() { return this._value; } set value(value) { this._value = value; } }; xml.CharacterData = class extends xml.Node { constructor(document, nodeType, data) { super(document, nodeType); this._data = data; } get data() { return this._data; } get textContent() { return this._data; } }; xml.Text = class extends xml.CharacterData { constructor(document, data) { super(document, xml.NodeType.Text, data); } get localName() { return '#text'; } }; xml.CDataSection = class extends xml.CharacterData { constructor(document, data) { super(document, xml.NodeType.CDATA, data); } }; xml.Entity = class extends xml.Node { constructor(document, name) { super(document, xml.NodeType.Entity); this._name = name; this._publicId = ''; this._systemId = ''; this._notationName = ''; this._value = ''; } get localName() { return this._name; } get publicId() { return this._publicId; } set publicId(value) { this._publicId = value; } get systemId() { return this._systemId; } set systemId(value) { this._systemId = value; } get notationName() { return this._notationName; } set notationName(value) { this._notationName = value; } set value(value) { this._value = value; } get value() { return this._value; } }; xml.ProcessingInstruction = class extends xml.Node { constructor(document, target, data) { super(document, xml.NodeType.ProcessingInstruction); this._target = target; this._data = data; } get localName() { return this._target; } get target() { return this._target; } get data() { return this._data; } }; xml.Comment = class extends xml.CharacterData { constructor(document, data) { super(document, xml.NodeType.Comment, data); } get localName() { return '#comment'; } }; xml.Document = class extends xml.Node { constructor() { super(null, xml.NodeType.Document); this._documentElement = null; this._documentType = null; } get documentElement() { return this._documentElement; } get documentType() { return this._documentType; } appendChild(newChild) { super.appendChild(newChild); if (newChild.nodeType === xml.NodeType.Element) { this._documentElement = newChild; } if (newChild.nodeType === xml.NodeType.DocumentType) { this._documentType = newChild; } } createElement(localName) { return new xml.Element(this, null, localName); } createElementNS(namespaceURI, qualifiedName) { return new xml.Element(this, namespaceURI, qualifiedName); } createAttribute(localName) { return new xml.Attribute(this, null, localName); } createAttributeNS(namespaceURI, qualifiedName) { return new xml.Attribute(this, namespaceURI, qualifiedName); } createTextNode(data) { return new xml.Text(this, data); } createCDATASection(data) { return new xml.CDataSection(this, data); } createProcessingInstruction(target, data) { return new xml.ProcessingInstruction(this, target, data); } createComment(data) { return new xml.Comment(this, data); } createDocumentType(qualifiedName, publicId, systemId) { return new xml.DocumentType(this, qualifiedName, publicId, systemId); } }; xml.DocumentType = class extends xml.Node { constructor(document, qualifiedName, publicId, systemId) { super(document, xml.NodeType.DocumentType); this._name = qualifiedName; this._publicId = publicId; this._systemId = systemId; this._entities = new xml.NamedNodeMap(); } get name() { return this._name; } get publicId() { return this._publicId; } get systemId() { return this._systemId; } get entities() { return this._entities; } appendChild(newChild) { if (newChild.nodeType === xml.NodeType.Entity) { this._entities.setNamedItem(newChild); } } createEntity(name) { return new xml.Entity(this.ownerDocument, name); } }; xml.NamedNodeMap = class extends Array { getNamedItem(qualifiedName) { for (let i = this.length - 1; i >= 0; i--) { const node = this[i]; const key = node.prefix ? node.prefix + ':' + node.localName : node.localName; if (qualifiedName == key) { return node; } } return null; } getNamedItemNS(namespaceURI, localName) { for (let i = this.length - 1; i >= 0; i--) { const node = this[i]; if (localName === node.localName && namespaceURI == node.namespaceURI) { return node; } } return null; } setNamedItem(node) { const qualifiedName = node.prefix ? node.prefix + ':' + node.localName : node.localName; for (let i = this.length - 1; i >= 0; i--) { const node = this[i]; const key = node.prefix ? node.prefix + ':' + node.localName : node.localName; if (qualifiedName == key) { const oldNode = this[i]; this[i] = node; return oldNode; } } this.push(node); return null; } }; xml.NodeType = { None: 0, Element: 1, Attribute: 2, Text: 3, CDATA: 4, EntityReference: 5, Entity: 6, ProcessingInstruction: 7, Comment: 8, Document: 9, DocumentType: 10, DocumentFragment: 11, Notation: 12 }; xml.Utility = class { static split(name) { const index = name.indexOf(':'); if (index < 0 || index === name.length - 1) { return [ null, name ]; } const localName = name.substring(index + 1); const c = localName.codePointAt(0); if (localName.indexOf(':') !== -1 || !xml.Utility.nameStartCharRegExp.test(String.fromCodePoint(c)) && (c < 0x10000 || c > 0xEFFFF)) { return [ null, name ]; } const prefix = name.substring(0, index); return [ prefix, localName ]; } }; xml.Error = class extends Error { constructor(message) { super(message); this.name = 'XML Error'; } }; if (typeof module !== 'undefined' && typeof module.exports === 'object') { module.exports.TextReader = xml.TextReader; }