You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1791 lines
63 KiB
JavaScript
1791 lines
63 KiB
JavaScript
|
|
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 </" + name + "> 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('<![')) {
|
|
this._ignoreSectContents();
|
|
}
|
|
else {
|
|
this._next();
|
|
}
|
|
}
|
|
}
|
|
|
|
_push(value) {
|
|
this._stack.push(value);
|
|
}
|
|
|
|
_pop() {
|
|
return this._stack.pop();
|
|
}
|
|
|
|
_node() {
|
|
return this._stack[this._stack.length - 1];
|
|
}
|
|
|
|
_document() {
|
|
return this._stack[0];
|
|
}
|
|
|
|
_nodeType() {
|
|
return this._node().nodeType;
|
|
}
|
|
|
|
_appendChild(newChild) {
|
|
return this._node().appendChild(newChild);
|
|
}
|
|
|
|
_lookupNamespaceURI(prefix) {
|
|
return this._node().lookupNamespaceURI(prefix);
|
|
}
|
|
|
|
_name() {
|
|
const position = this._position;
|
|
const name = [];
|
|
const c = this._char.codePointAt(0);
|
|
if (this._nameStartCharRegExp.test(this._char) || (c >= 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;
|
|
}
|