@xmldom/xmldom
Advanced tools
Comparing version 0.9.0-beta.8 to 0.9.0-beta.9
@@ -7,2 +7,37 @@ # Changelog | ||
## [0.9.0-beta.9](https://github.com/xmldom/xmldom/compare/0.9.0-beta.8...0.9.0-beta.9) | ||
### Fixed | ||
- Set nodeName property in ProcessingInstruction [`#509`](https://github.com/xmldom/xmldom/pull/509) / [`#505`](https://github.com/xmldom/xmldom/issues/505) | ||
- preserve DOCTYPE internal subset [`#498`](https://github.com/xmldom/xmldom/pull/498) / [`#497`](https://github.com/xmldom/xmldom/pull/497) / [`#117`](https://github.com/xmldom/xmldom/issues/117)\ | ||
BREAKING CHANGES: Many documents that were previously accepted by xmldom, esecially non well-formed ones are no longer accepted. Some issues that were formerly reported as errors are now a fatalError. | ||
- DOMParser: Align parseFromString errors with specs [`#454`](https://github.com/xmldom/xmldom/pull/454) | ||
### Chore | ||
- stop running mutation tests using stryker [`#496`](https://github.com/xmldom/xmldom/pull/496) | ||
- make `toErrorSnapshot` windows compatible [`#503`](https://github.com/xmldom/xmldom/pull/503) | ||
Thank you, [@cjbarth](https://github.com/cjbarth), [@shunkica](https://github.com/shunkica), [@pmahend1](https://github.com/pmahend1), [@niklasl](https://github.com/niklasl), for your contributions | ||
## [0.8.9](https://github.com/xmldom/xmldom/compare/0.8.8...0.8.9) | ||
### Fixed | ||
- Set nodeName property in ProcessingInstruction [`#509`](https://github.com/xmldom/xmldom/pull/509) / [`#505`](https://github.com/xmldom/xmldom/issues/505) | ||
Thank you, [@cjbarth](https://github.com/cjbarth), for your contributions | ||
## [0.7.12](https://github.com/xmldom/xmldom/compare/0.7.11...0.7.12) | ||
### Fixed | ||
- Set nodeName property in ProcessingInstruction [`#509`](https://github.com/xmldom/xmldom/pull/509) / [`#505`](https://github.com/xmldom/xmldom/issues/505) | ||
Thank you, [@cjbarth](https://github.com/cjbarth), for your contributions | ||
## [0.9.0-beta.8](https://github.com/xmldom/xmldom/compare/0.9.0-beta.7...0.9.0-beta.8) | ||
@@ -12,3 +47,3 @@ | ||
- fix: Throw DOMException when calling removeChild with invalid parameter [`#494`](https://github.com/xmldom/xmldom/pull/494) / [`#135`](https://github.com/xmldom/xmldom/issues/135) | ||
- Throw DOMException when calling removeChild with invalid parameter [`#494`](https://github.com/xmldom/xmldom/pull/494) / [`#135`](https://github.com/xmldom/xmldom/issues/135) | ||
@@ -15,0 +50,0 @@ BREAKING CHANGE: Previously it was possible (but not documented) to call `Node.removeChild` with any node in the tree, |
382
index.d.ts
/// <reference lib="dom" /> | ||
declare module '@xmldom/xmldom' { | ||
var DOMParser: DOMParserStatic | ||
var XMLSerializer: XMLSerializerStatic | ||
var DOMImplementation: DOMImplementationStatic | ||
// START ./lib/conventions.js | ||
/** | ||
* Since xmldom can not rely on `Object.assign`, | ||
* it uses/provides a simplified version that is sufficient for its needs. | ||
* | ||
* @throws TypeError if target is not an object | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign | ||
* @see https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign | ||
*/ | ||
function assign<T, S>(target:T, source:S): T & S; | ||
/** | ||
* Only returns true if `value` matches MIME_TYPE.HTML, which indicates an HTML document. | ||
* | ||
* @see https://www.iana.org/assignments/media-types/text/html | ||
* @see https://en.wikipedia.org/wiki/HTML | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString | ||
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring | ||
*/ | ||
function isHtmlMimeType(mimeType: string): mimeType is MIME_TYPE.HTML; | ||
/** | ||
* Only returns true if `mimeType` is one of the allowed values for `DOMParser.parseFromString`. | ||
*/ | ||
function isValidMimeType(mimeType: string): mimeType is MIME_TYPE; | ||
interface DOMImplementationStatic { | ||
new (): DOMImplementation | ||
/** | ||
* All mime types that are allowed as input to `DOMParser.parseFromString` | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString#Argument02 MDN | ||
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#domparsersupportedtype WHATWG HTML Spec | ||
* @see DOMParser.prototype.parseFromString | ||
*/ | ||
enum MIME_TYPE { | ||
/** | ||
* `text/html`, the only mime type that triggers treating an XML document as HTML. | ||
* | ||
* @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration | ||
* @see https://en.wikipedia.org/wiki/HTML Wikipedia | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString MDN | ||
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring WHATWG HTML Spec | ||
*/ | ||
HTML = 'text/html', | ||
/** | ||
* `application/xml`, the standard mime type for XML documents. | ||
* | ||
* @see https://www.iana.org/assignments/media-types/application/xml IANA MimeType registration | ||
* @see https://tools.ietf.org/html/rfc7303#section-9.1 RFC 7303 | ||
* @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia | ||
*/ | ||
XML_APPLICATION = 'application/xml', | ||
/** | ||
* `text/html`, an alias for `application/xml`. | ||
* | ||
* @see https://tools.ietf.org/html/rfc7303#section-9.2 RFC 7303 | ||
* @see https://www.iana.org/assignments/media-types/text/xml IANA MimeType registration | ||
* @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia | ||
*/ | ||
XML_TEXT = 'text/xml', | ||
/** | ||
* `application/xhtml+xml`, indicates an XML document that has the default HTML namespace, | ||
* but is parsed as an XML document. | ||
* | ||
* @see https://www.iana.org/assignments/media-types/application/xhtml+xml IANA MimeType registration | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument WHATWG DOM Spec | ||
* @see https://en.wikipedia.org/wiki/XHTML Wikipedia | ||
*/ | ||
XML_XHTML_APPLICATION = 'application/xhtml+xml', | ||
/** | ||
* `image/svg+xml`, | ||
* | ||
* @see https://www.iana.org/assignments/media-types/image/svg+xml IANA MimeType registration | ||
* @see https://www.w3.org/TR/SVG11/ W3C SVG 1.1 | ||
* @see https://en.wikipedia.org/wiki/Scalable_Vector_Graphics Wikipedia | ||
*/ | ||
XML_SVG_IMAGE = 'image/svg+xml', | ||
} | ||
/** | ||
* Namespaces that are used in xmldom. | ||
* | ||
* @see http://www.w3.org/TR/REC-xml-names | ||
*/ | ||
enum NAMESPACE { | ||
/** | ||
* The XHTML namespace. | ||
* | ||
* @see http://www.w3.org/1999/xhtml | ||
*/ | ||
HTML = 'http://www.w3.org/1999/xhtml', | ||
/** | ||
* The SVG namespace. | ||
* | ||
* @see http://www.w3.org/2000/svg | ||
*/ | ||
SVG = 'http://www.w3.org/2000/svg', | ||
/** | ||
* The `xml:` namespace. | ||
* | ||
* @see http://www.w3.org/XML/1998/namespace | ||
*/ | ||
XML = 'http://www.w3.org/XML/1998/namespace', | ||
interface DOMParserStatic { | ||
new (): DOMParser | ||
new (options: DOMParserOptions): DOMParser | ||
/** | ||
* The `xmlns:` namespace | ||
* | ||
* @see https://www.w3.org/2000/xmlns/ | ||
*/ | ||
XMLNS = 'http://www.w3.org/2000/xmlns/', | ||
} | ||
/** | ||
* A custom error that will not be caught by XMLReader aka the SAX parser. | ||
*/ | ||
class ParseError extends Error { | ||
constructor(message:string, locator?:any); | ||
} | ||
// END ./lib/conventions.js | ||
// START ./lib/dom.js | ||
/** | ||
* The error class for errors reported by the DOM API. | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException | ||
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html | ||
* @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html | ||
*/ | ||
class DOMException extends Error { | ||
constructor(code: number, message: string); | ||
} | ||
interface DOMImplementation { | ||
/** | ||
* The DOMImplementation interface represents an object providing methods | ||
* which are not dependent on any particular document. | ||
* Such an object is returned by the `Document.implementation` property. | ||
* | ||
* __The individual methods describe the differences compared to the specs.__ | ||
* | ||
* @constructor | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation MDN | ||
* @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 DOM Level 1 Core | ||
* (Initial) | ||
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490 DOM Level 2 Core | ||
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-102161490 DOM Level 3 Core | ||
* @see https://dom.spec.whatwg.org/#domimplementation DOM Living Standard | ||
*/ | ||
new (): DOMImplementation; | ||
/** | ||
* Creates an XML Document object of the specified type with its document element. | ||
* | ||
* __It behaves slightly different from the description in the living standard__: | ||
* - There is no interface/class `XMLDocument`, it returns a `Document` instance (with it's | ||
* `type` set to `'xml'`). | ||
* - `encoding`, `mode`, `origin`, `url` fields are currently not declared. | ||
* | ||
* @returns {Document} the XML document | ||
* | ||
* @see DOMImplementation.createHTMLDocument | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocument MDN | ||
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocument DOM | ||
* Level 2 Core (initial) | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument DOM Level 2 Core | ||
*/ | ||
createDocument( | ||
namespaceURI: string | null, | ||
qualifiedName: string, | ||
doctype?: DocumentType | null | ||
): Document; | ||
/** | ||
* Returns a doctype, with the given `qualifiedName`, `publicId`, and `systemId`. | ||
* | ||
* __This behavior is slightly different from the in the specs__: | ||
* - `encoding`, `mode`, `origin`, `url` fields are currently not declared. | ||
* | ||
* @returns {DocumentType} which can either be used with `DOMImplementation.createDocument` | ||
* upon document creation or can be put into the document via methods like | ||
* `Node.insertBefore()` or `Node.replaceChild()` | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocumentType | ||
* MDN | ||
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocType DOM | ||
* Level 2 Core | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype DOM Living | ||
* Standard | ||
*/ | ||
createDocumentType( | ||
qualifiedName: string, | ||
publicId?: string, | ||
systemId?: string | ||
): DocumentType; | ||
/** | ||
* Returns an HTML document, that might already have a basic DOM structure. | ||
* | ||
* __It behaves slightly different from the description in the living standard__: | ||
* - If the first argument is `false` no initial nodes are added (steps 3-7 in the specs are | ||
* omitted) | ||
* - `encoding`, `mode`, `origin`, `url` fields are currently not declared. | ||
* | ||
* @see DOMImplementation.createDocument | ||
* | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument | ||
* @see https://dom.spec.whatwg.org/#html-document | ||
*/ | ||
createHTMLDocument(title?: string | false): Document; | ||
/** | ||
* The DOMImplementation.hasFeature() method returns a Boolean flag indicating if a given | ||
* feature is supported. The different implementations fairly diverged in what kind of features | ||
* were reported. The latest version of the spec settled to force this method to always return | ||
* true, where the functionality was accurate and in use. | ||
* | ||
* @deprecated It is deprecated and modern browsers return true in all cases. | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/hasFeature MDN | ||
* @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-5CED94D7 DOM Level 1 Core | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature DOM Living Standard | ||
*/ | ||
hasFeature(feature: string, version?: string): true; | ||
} | ||
var XMLSerializer: XMLSerializerStatic; | ||
interface XMLSerializerStatic { | ||
new (): XMLSerializer | ||
new (): XMLSerializer; | ||
} | ||
// END ./lib/dom.js | ||
// START ./lib/dom-parser.js | ||
var DOMParser: DOMParserStatic; | ||
interface DOMParserStatic { | ||
/** | ||
* The DOMParser interface provides the ability to parse XML or HTML source code | ||
* from a string into a DOM `Document`. | ||
* | ||
* _xmldom is different from the spec in that it allows an `options` parameter, | ||
* to control the behavior._ | ||
* | ||
* @param {DOMParserOptions} [options] | ||
* @constructor | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser | ||
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-parsing-and-serialization | ||
*/ | ||
new (options?: DOMParserOptions): DOMParser; | ||
} | ||
/** | ||
* The DOMParser interface provides the ability to parse XML or HTML source code | ||
* from a string into a DOM `Document`. | ||
* | ||
* _xmldom is different from the spec in that it allows an `options` parameter, | ||
* to control the behavior._ | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser | ||
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-parsing-and-serialization | ||
*/ | ||
interface DOMParser { | ||
parseFromString(source: string, mimeType?: string): Document | undefined | ||
/** | ||
* Parses `source` using the options in the way configured by the `DOMParserOptions` of `this` | ||
* `DOMParser`. If `mimeType` is `text/html` an HTML `Document` is created, otherwise an XML | ||
* `Document` is created. | ||
* | ||
* __It behaves different from the description in the living standard__: | ||
* - Uses the `options` passed to the `DOMParser` constructor to modify the | ||
* behavior. | ||
* - Any unexpected input is reported to `onError` with either a `warning`, `error` or `fatalError` level. | ||
* - Any `fatalError` throws a `ParseError` which prevents further processing. | ||
* - Any error thrown by `onError` is converted to a `ParseError` which prevents further processing | ||
* - If no `Document` was created during parsing it is reported as a `fatalError`. | ||
* | ||
* @throws ParseError for any `fatalError` or anything that is thrown by `onError` | ||
* @throws TypeError for any invalid `mimeType` | ||
* @returns the `Document` node | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString | ||
* @see https://html.spec.whatwg.org/#dom-domparser-parsefromstring-dev | ||
*/ | ||
parseFromString(source: string, mimeType: MIME_TYPE = MIME_TYPE.XML_TEXT): Document; | ||
} | ||
interface XMLSerializer { | ||
serializeToString(node: Node): string | ||
serializeToString(node: Node, nodeFilter?: (node: Node) => boolean): string; | ||
} | ||
interface DOMParserOptions { | ||
errorHandler?: ErrorHandlerFunction | ErrorHandlerObject | ||
locator?: boolean | ||
normalizeLineEndings?: (source: string) => string | ||
xmlns?: Record<string, string | null | undefined> | ||
/** | ||
* The method to use instead of `Object.assign` (defaults to `conventions.assign`), | ||
* which is used to copy values from the options before they are used for parsing. | ||
* | ||
* @private | ||
* @see conventions.assign | ||
*/ | ||
readonly assign?: typeof Object.assign; | ||
/** | ||
* For internal testing: The class for creating an instance for handling events from the SAX | ||
* parser. | ||
* __**Warning: By configuring a faulty implementation, | ||
* the specified behavior can completely be broken.**__ | ||
* | ||
* @private | ||
*/ | ||
readonly domHandler?: unknown; | ||
/** | ||
* DEPRECATED: Use `onError` instead! | ||
* | ||
* For backwards compatibility: | ||
* If it is a function, it will be used as a value for `onError`, | ||
* but it receives different argument types than before 0.9.0. | ||
* @throws If it is an object. | ||
* @deprecated | ||
*/ | ||
readonly errorHandler?: ErrorHandlerFunction; | ||
/** | ||
* Configures if the nodes created during parsing | ||
* will have a `lineNumber` and a `columnNumber` attribute | ||
* describing their location in the XML string. | ||
* Default is true. | ||
*/ | ||
readonly locator?: boolean; | ||
/** | ||
* used to replace line endings before parsing, defaults to `normalizeLineEndings`, | ||
* which normalizes line endings according to <https://www.w3.org/TR/xml11/#sec-line-ends>. | ||
*/ | ||
readonly normalizeLineEndings?: (source: string) => string; | ||
/** | ||
* A function that is invoked for every error that occurs during parsing. | ||
* | ||
* If it is not provided, all errors are reported to `console.error` | ||
* and only `fatalError`s are thrown as a `ParseError`, | ||
* which prevents any further processing. | ||
* If the provided method throws, a `ParserError` is thrown, | ||
* which prevents any further processing. | ||
* | ||
* Be aware that many `warning`s are considered an error | ||
* that prevents further processing in most implementations. | ||
* | ||
* @param level the error level as reported by the SAXParser | ||
* @param message the error message | ||
* @param context the DOMHandler instance used for parsing | ||
* | ||
* @see onErrorStopParsing | ||
* @see onWarningStopParsing | ||
*/ | ||
readonly onError?: ErrorHandlerFunction; | ||
/** | ||
* The XML namespaces that should be assumed when parsing. | ||
* The default namespace can be provided by the key that is the empty string. | ||
* When the `mimeType` for HTML, XHTML or SVG are passed to `parseFromString`, | ||
* the default namespace that will be used, | ||
* will be overridden according to the specification. | ||
*/ | ||
readonly xmlns?: Readonly<Record<string, string | null | undefined>>; | ||
} | ||
interface ErrorHandlerFunction { | ||
(level: 'warn' | 'error' | 'fatalError', msg: string): void | ||
(level: 'warn' | 'error' | 'fatalError', msg: string, context: any): void; | ||
} | ||
interface ErrorHandlerObject { | ||
warning?: (msg: string) => void | ||
error?: (msg: string) => void | ||
fatalError?: (msg: string) => void | ||
} | ||
/** | ||
* A method that prevents any further parsing when an `error` | ||
* with level `error` is reported during parsing. | ||
* | ||
* @see DOMParserOptions.onError | ||
* @see onWarningStopParsing | ||
*/ | ||
function onErrorStopParsing(): void | never; | ||
/** | ||
* A method that prevents any further parsing when an `error` | ||
* with any level is reported during parsing. | ||
* | ||
* @see DOMParserOptions.onError | ||
* @see onErrorStopParsing | ||
*/ | ||
function onWarningStopParsing(): never; | ||
// END ./lib/dom-parser.js | ||
} |
@@ -58,4 +58,4 @@ 'use strict'; | ||
/** | ||
* Since we can not rely on `Object.assign` we provide a simplified version | ||
* that is sufficient for our needs. | ||
* Since xmldom can not rely on `Object.assign`, | ||
* it uses/provides a simplified version that is sufficient for its needs. | ||
* | ||
@@ -231,2 +231,30 @@ * @param {Object} target | ||
} | ||
/** | ||
* Only returns true if `value` matches MIME_TYPE.HTML, which indicates an HTML document. | ||
* | ||
* @param {string} mimeType | ||
* @returns {mimeType is 'text/html'} | ||
* | ||
* @see https://www.iana.org/assignments/media-types/text/html | ||
* @see https://en.wikipedia.org/wiki/HTML | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString | ||
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring | ||
*/ | ||
function isHTMLMimeType(mimeType) { | ||
return mimeType === MIME_TYPE.HTML; | ||
} | ||
/** | ||
* For both the `text/html` and the `application/xhtml+xml` namespace | ||
* the spec defines that the HTML namespace is provided as the default. | ||
* | ||
* @param {string} mimeType | ||
* @returns {boolean} | ||
* | ||
* @see https://dom.spec.whatwg.org/#dom-document-createelement | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument | ||
*/ | ||
function hasDefaultHTMLNamespace(mimeType) { | ||
return isHTMLMimeType(mimeType) || mimeType === MIME_TYPE.XML_XHTML_APPLICATION; | ||
} | ||
@@ -244,3 +272,2 @@ /** | ||
* | ||
* @see DOMParser.SupportedType.isHTML | ||
* @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration | ||
@@ -254,32 +281,2 @@ * @see https://en.wikipedia.org/wiki/HTML Wikipedia | ||
/** | ||
* Helper method to check a mime type if it indicates an HTML document | ||
* | ||
* @param {string} [value] | ||
* @returns {boolean} | ||
* | ||
* @see [IANA MimeType registration](https://www.iana.org/assignments/media-types/text/html) | ||
* @see [Wikipedia](https://en.wikipedia.org/wiki/HTML) | ||
* @see [`DOMParser.parseFromString` @ MDN](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString) | ||
* @see [`DOMParser.parseFromString` @ HTML Specification](https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring) | ||
*/ | ||
isHTML: function (value) { | ||
return value === MIME_TYPE.HTML; | ||
}, | ||
/** | ||
* For both the `text/html` and the `application/xhtml+xml` namespace | ||
* the spec defines that the HTML namespace is provided as the default in some cases. | ||
* | ||
* @param {string} mimeType | ||
* @returns {boolean} | ||
* | ||
* @see https://dom.spec.whatwg.org/#dom-document-createelement | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument | ||
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument | ||
*/ | ||
hasDefaultHTMLNamespace: function (mimeType) { | ||
return MIME_TYPE.isHTML(mimeType) || mimeType === MIME_TYPE.XML_XHTML_APPLICATION; | ||
}, | ||
/** | ||
* `application/xml`, the standard mime type for XML documents. | ||
@@ -321,4 +318,22 @@ * | ||
}); | ||
/** | ||
* @typedef {'application/xhtml+xml' | 'application/xml' | 'image/svg+xml' | 'text/html' | 'text/xml'} MimeType | ||
*/ | ||
/** | ||
* @type {MimeType[]} | ||
* @private | ||
*/ | ||
var _MIME_TYPES = Object.keys(MIME_TYPE).map(function (key) { | ||
return MIME_TYPE[key]; | ||
}); | ||
/** | ||
* Only returns true if `mimeType` is one of the allowed values for `DOMParser.parseFromString`. | ||
* @param {string} mimeType | ||
* @returns {mimeType is 'application/xhtml+xml' | 'application/xml' | 'image/svg+xml' | 'text/html' | 'text/xml'} | ||
*/ | ||
function isValidMimeType(mimeType) { | ||
return _MIME_TYPES.indexOf(mimeType) > -1; | ||
} | ||
/** | ||
* Namespaces that are used in this code base. | ||
@@ -337,13 +352,2 @@ * | ||
/** | ||
* Checks if `uri` equals `NAMESPACE.HTML`. | ||
* | ||
* @param {string} [uri] | ||
* | ||
* @see NAMESPACE.HTML | ||
*/ | ||
isHTML: function (uri) { | ||
return uri === NAMESPACE.HTML; | ||
}, | ||
/** | ||
* The SVG namespace. | ||
@@ -370,18 +374,16 @@ * | ||
// https://www.w3.org/TR/xml-names/#ns-decl | ||
// https://www.w3.org/TR/REC-xml/#NT-Name | ||
// https://www.w3.org/TR/xml-names/#ns-qualnames | ||
// NAME_START_CHAR without ":" | ||
var QNAME_START_CHAR = | ||
/[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/; //\u10000-\uEFFFF | ||
//[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] | ||
var NAME_START_CHAR = new RegExp('[:' + QNAME_START_CHAR.source.slice(1, -1) + ']'); | ||
var NAME_CHAR = new RegExp('[\\-\\.0-9' + NAME_START_CHAR.source.slice(1, -1) + '\\u00B7\\u0300-\\u036F\\u203F-\\u2040]'); | ||
//[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] | ||
// NAME_CHAR without ":" | ||
var QNAME_CHAR = new RegExp('[\\-\\.0-9' + QNAME_START_CHAR.source.slice(1, -1) + '\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*'); | ||
// https://www.w3.org/TR/xml-names/#NT-NCName | ||
var NCNAME = QNAME_START_CHAR.source + QNAME_CHAR.source; | ||
// https://www.w3.org/TR/xml-names/#ns-qualnames | ||
var QNAME = new RegExp('^' + NCNAME + '(?::' + NCNAME + ')?$'); | ||
/** | ||
* Creates an error that will not be caught by XMLReader aka the SAX parser. | ||
* | ||
* @param {string} message | ||
* @param {any?} locator Optional, can provide details about the location in the source | ||
* @constructor | ||
*/ | ||
function ParseError(message, locator) { | ||
this.message = message; | ||
this.locator = locator; | ||
if (Error.captureStackTrace) Error.captureStackTrace(this, ParseError); | ||
} | ||
ParseError.prototype = new Error(); | ||
ParseError.prototype.name = ParseError.name; | ||
@@ -394,12 +396,11 @@ exports.assign = assign; | ||
exports.HTML_VOID_ELEMENTS = HTML_VOID_ELEMENTS; | ||
exports.hasDefaultHTMLNamespace = hasDefaultHTMLNamespace; | ||
exports.isHTMLBooleanAttribute = isHTMLBooleanAttribute; | ||
exports.isHTMLRawTextElement = isHTMLRawTextElement; | ||
exports.isHTMLEscapableRawTextElement = isHTMLEscapableRawTextElement; | ||
exports.isHTMLMimeType = isHTMLMimeType; | ||
exports.isHTMLVoidElement = isHTMLVoidElement; | ||
exports.isValidMimeType = isValidMimeType; | ||
exports.MIME_TYPE = MIME_TYPE; | ||
exports.NAME_CHAR = NAME_CHAR; | ||
exports.NAME_START_CHAR = NAME_START_CHAR; | ||
exports.NAMESPACE = NAMESPACE; | ||
exports.QNAME = QNAME; | ||
exports.QNAME_CHAR = QNAME_CHAR; | ||
exports.QNAME_START_CHAR = QNAME_START_CHAR; | ||
exports.ParseError = ParseError; |
@@ -10,6 +10,9 @@ 'use strict'; | ||
var hasDefaultHTMLNamespace = conventions.hasDefaultHTMLNamespace; | ||
var isHTMLMimeType = conventions.isHTMLMimeType; | ||
var isValidMimeType = conventions.isValidMimeType; | ||
var MIME_TYPE = conventions.MIME_TYPE; | ||
var NAMESPACE = conventions.NAMESPACE; | ||
var ParseError = conventions.ParseError; | ||
var ParseError = sax.ParseError; | ||
var XMLReader = sax.XMLReader; | ||
@@ -55,4 +58,19 @@ | ||
* For internal testing: The class for creating an instance for handling events from the SAX parser. | ||
* Warning: By configuring a faulty implementation, the specified behavior can completely be broken. | ||
* __**Warning: By configuring a faulty implementation, | ||
* the specified behavior can completely be broken.**__ | ||
* | ||
* @property {Function} [errorHandler] | ||
* DEPRECATED! use `onError` instead. | ||
* @property {function(level:ErrorLevel, message:string, context: DOMHandler):void} [onError] | ||
* A function that is invoked for every error that occurs during parsing. | ||
* | ||
* If it is not provided, all errors are reported to `console.error` | ||
* and only `fatalError`s are thrown as a `ParseError`, | ||
* which prevents any further processing. | ||
* If the provided method throws, a `ParserError` is thrown, | ||
* which prevents any further processing. | ||
* | ||
* Be aware that many `warning`s are considered an error | ||
* that prevents further processing in most implementations. | ||
*. | ||
* @property {boolean} [locator=true] | ||
@@ -92,3 +110,3 @@ * Configures if the nodes created during parsing | ||
/** | ||
* The method to use instead of `Object.assign` (or if not available `conventions.assign`), | ||
* The method to use instead of `Object.assign` (defaults to or `conventions.assign`), | ||
* which is used to copy values from the options before they are used for parsing. | ||
@@ -101,3 +119,3 @@ * | ||
*/ | ||
this.assign = options.assign || Object.assign || conventions.assign; | ||
this.assign = options.assign || conventions.assign; | ||
@@ -115,7 +133,24 @@ /** | ||
/** | ||
* A function that can be invoked as the errorHandler instead of the default ones. | ||
* @type {Function | undefined} | ||
* @readonly | ||
* A function that is invoked for every error that occurs during parsing. | ||
* | ||
* If it is not provided, all errors are reported to `console.error` | ||
* and only `fatalError`s are thrown as a `ParseError`, | ||
* which prevents any further processing. | ||
* If the provided method throws, a `ParserError` is thrown, | ||
* which prevents any further processing. | ||
* | ||
* Be aware that many `warning`s are considered an error | ||
* that prevents further processing in most implementations. | ||
* | ||
* @type {function(level:ErrorLevel, message:string, context: DOMHandler):void} | ||
* | ||
* @see onErrorStopParsing | ||
* @see onWarningStopParsing | ||
*/ | ||
this.errorHandler = options.errorHandler; | ||
this.onError = options.onError || options.errorHandler; | ||
if (options.errorHandler && typeof options.errorHandler !== 'function') { | ||
throw new TypeError('errorHandler object is no longer supported, switch to onError!'); | ||
} else if (options.errorHandler) { | ||
options.errorHandler('warning', 'The `errorHandler` option has been deprecated, use `onError` instead!', this); | ||
} | ||
@@ -152,26 +187,25 @@ /** | ||
/** | ||
* Parses `source` using the options in the way configured by the `DOMParserOptions` of `this` `DOMParser`. | ||
* If `mimeType` is `text/html` an HTML `Document` is created, otherwise an XML `Document` is created. | ||
* Parses `source` using the options in the way configured by the `DOMParserOptions` of `this` | ||
* `DOMParser`. If `mimeType` is `text/html` an HTML `Document` is created, otherwise an XML | ||
* `Document` is created. | ||
* | ||
* __It behaves very different from the description in the living standard__: | ||
* - Only allows the first argument to be a string (calls `error` handler otherwise.) | ||
* - The second parameter is optional (defaults to `application/xml`) and can be any string, | ||
* no `TypeError` will be thrown for values not listed in the spec. | ||
* - Uses the `options` passed to the `DOMParser` constructor to modify the behavior/implementation. | ||
* - Instead of creating a Document containing the error message, | ||
* it triggers `errorHandler`(s) when unexpected input is found, which means it can return `undefined`. | ||
* All error handlers can throw an `Error`, by default only the `fatalError` handler throws (a `ParserError`). | ||
* - All errors thrown during the parsing that are not a `ParserError` are caught and reported using the `error` handler. | ||
* - If no `ParserError` is thrown, this method returns the `DOMHandler.doc`, | ||
* which most likely is the `Document` that has been created during parsing, or `undefined`. | ||
* __**Warning: By configuring a faulty DOMHandler implementation, | ||
* the specified behavior can completely be broken.**__ | ||
* __It behaves different from the description in the living standard__: | ||
* - Uses the `options` passed to the `DOMParser` constructor to modify the | ||
* behavior. | ||
* - Any unexpected input is reported to `onError` with either a `warning`, `error` or `fatalError` level. | ||
* - Any `fatalError` throws a `ParseError` which prevents further processing. | ||
* - Any error thrown by `onError` is converted to a `ParseError` which prevents further processing | ||
* - If no `Document` was created during parsing it is reported as a `fatalError`. | ||
* __**Warning: By configuring a faulty DOMHandler implementation, | ||
* the specified behavior can completely be broken.**__ | ||
* | ||
* @param {string} source Only string input is possible! | ||
* @param {string} source The XML mime type only allows string input! | ||
* @param {string} [mimeType='application/xml'] | ||
* the mimeType or contentType of the document to be created | ||
* determines the `type` of document created (XML or HTML) | ||
* @returns {Document | undefined} | ||
* @throws ParseError for specific errors depending on the configured `errorHandler`s and/or `domBuilder` | ||
* | ||
* @throws ParseError for any `fatalError` or anything that is thrown by `onError` | ||
* @throws TypeError for any invalid `mimeType` | ||
* @returns the `Document` node | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString | ||
@@ -181,6 +215,9 @@ * @see https://html.spec.whatwg.org/#dom-domparser-parsefromstring-dev | ||
DOMParser.prototype.parseFromString = function (source, mimeType) { | ||
if (!isValidMimeType(mimeType)) { | ||
throw new TypeError('DOMParser.parseFromString: the provided mimeType "' + mimeType + '" is not valid.'); | ||
} | ||
var defaultNSMap = this.assign({}, this.xmlns); | ||
var entityMap = entities.XML_ENTITIES; | ||
var defaultNamespace = defaultNSMap[''] || null; | ||
if (MIME_TYPE.hasDefaultHTMLNamespace(mimeType)) { | ||
if (hasDefaultHTMLNamespace(mimeType)) { | ||
entityMap = entities.HTML_ENTITIES; | ||
@@ -197,2 +234,3 @@ defaultNamespace = NAMESPACE.HTML; | ||
defaultNamespace: defaultNamespace, | ||
onError: this.onError, | ||
}); | ||
@@ -205,43 +243,14 @@ var locator = this.locator ? {} : undefined; | ||
var sax = new XMLReader(); | ||
sax.errorHandler = buildErrorHandler(this.errorHandler, domBuilder, locator); | ||
sax.errorHandler = domBuilder; | ||
sax.domBuilder = domBuilder; | ||
if (source && typeof source === 'string') { | ||
sax.parse(this.normalizeLineEndings(source), defaultNSMap, entityMap); | ||
} else { | ||
sax.errorHandler.error('invalid doc source'); | ||
var isXml = !conventions.isHTMLMimeType(mimeType); | ||
if (isXml && typeof source !== 'string') { | ||
sax.errorHandler.fatalError('source is not a string'); | ||
} | ||
sax.parse(this.normalizeLineEndings(String(source)), defaultNSMap, entityMap); | ||
if (!domBuilder.doc.documentElement) { | ||
sax.errorHandler.fatalError('missing root element'); | ||
} | ||
return domBuilder.doc; | ||
}; | ||
function buildErrorHandler(errorImpl, domBuilder, locator) { | ||
if (!errorImpl) { | ||
if (domBuilder instanceof DOMHandler) { | ||
return domBuilder; | ||
} | ||
errorImpl = domBuilder; | ||
} | ||
var errorHandler = {}; | ||
var isCallback = errorImpl instanceof Function; | ||
locator = locator || {}; | ||
function build(key) { | ||
var fn = errorImpl[key]; | ||
if (!fn && isCallback) { | ||
fn = | ||
errorImpl.length == 2 | ||
? function (msg) { | ||
errorImpl(key, msg); | ||
} | ||
: errorImpl; | ||
} | ||
errorHandler[key] = | ||
(fn && | ||
function (msg) { | ||
fn('[xmldom ' + key + ']\t' + msg + _locator(locator)); | ||
}) || | ||
function () {}; | ||
} | ||
build('warning'); | ||
build('error'); | ||
build('fatalError'); | ||
return errorHandler; | ||
} | ||
@@ -330,3 +339,9 @@ /** | ||
this.locator = undefined; | ||
/** | ||
* @type {function (level:ErrorLevel ,message:string, context:DOMHandler):void} | ||
* @readonly | ||
*/ | ||
this.onError = opt.onError; | ||
} | ||
function position(locator, node) { | ||
@@ -336,2 +351,3 @@ node.lineNumber = locator.lineNumber; | ||
} | ||
DOMHandler.prototype = { | ||
@@ -348,3 +364,3 @@ /** | ||
var impl = new DOMImplementation(); | ||
this.doc = MIME_TYPE.isHTML(this.mimeType) ? impl.createHTMLDocument(false) : impl.createDocument(this.defaultNamespace, ''); | ||
this.doc = isHTMLMimeType(this.mimeType) ? impl.createHTMLDocument(false) : impl.createDocument(this.defaultNamespace, ''); | ||
}, | ||
@@ -430,6 +446,6 @@ startElement: function (namespaceURI, localName, qName, attrs) { | ||
startDTD: function (name, publicId, systemId) { | ||
startDTD: function (name, publicId, systemId, internalSubset) { | ||
var impl = this.doc.implementation; | ||
if (impl && impl.createDocumentType) { | ||
var dt = impl.createDocumentType(name, publicId, systemId); | ||
var dt = impl.createDocumentType(name, publicId, systemId, internalSubset); | ||
this.locator && position(this.locator, dt); | ||
@@ -440,16 +456,35 @@ appendElement(this, dt); | ||
}, | ||
reportError: function (level, message) { | ||
if (typeof this.onError === 'function') { | ||
try { | ||
this.onError(level, message, this); | ||
} catch (e) { | ||
throw new ParseError('Reporting ' + level + ' "' + message + '" caused ' + e, this.locator); | ||
} | ||
} else { | ||
console.error('[xmldom ' + level + ']\t' + message, _locator(this.locator)); | ||
} | ||
}, | ||
/** | ||
* @see org.xml.sax.ErrorHandler | ||
* @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html | ||
* @see http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html | ||
*/ | ||
warning: function (error) { | ||
console.warn('[xmldom warning]\t' + error, _locator(this.locator)); | ||
warning: function (message) { | ||
this.reportError('warning', message); | ||
}, | ||
error: function (error) { | ||
console.error('[xmldom error]\t' + error, _locator(this.locator)); | ||
error: function (message) { | ||
this.reportError('error', message); | ||
}, | ||
fatalError: function (error) { | ||
throw new ParseError(error, this.locator); | ||
/** | ||
* This function reports a fatal error and throws a ParseError. | ||
* | ||
* @param {string} message - The message to be used for reporting and throwing the error. | ||
* @returns {never} This function always throws an error and never returns a value. | ||
* @throws {ParseError} Always throws a ParseError with the provided message. | ||
*/ | ||
fatalError: function (message) { | ||
this.reportError('fatalError', message); | ||
throw new ParseError(message, this.locator); | ||
}, | ||
}; | ||
function _locator(l) { | ||
@@ -460,2 +495,3 @@ if (l) { | ||
} | ||
function _toString(chars, start, length) { | ||
@@ -514,12 +550,35 @@ if (typeof chars == 'string') { | ||
/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */ | ||
function appendElement(hander, node) { | ||
if (!hander.currentElement) { | ||
hander.doc.appendChild(node); | ||
function appendElement(handler, node) { | ||
if (!handler.currentElement) { | ||
handler.doc.appendChild(node); | ||
} else { | ||
hander.currentElement.appendChild(node); | ||
handler.currentElement.appendChild(node); | ||
} | ||
} //appendChild and setAttributeNS are preformance key | ||
} | ||
/** | ||
* A method that prevents any further parsing when an `error` | ||
* with level `error` is reported during parsing. | ||
* | ||
* @see DOMParserOptions.onError | ||
* @see onWarningStopParsing | ||
*/ | ||
function onErrorStopParsing(level) { | ||
if (level === 'error') throw 'onErrorStopParsing'; | ||
} | ||
/** | ||
* A method that prevents any further parsing when any `error` is reported during parsing. | ||
* | ||
* @see DOMParserOptions.onError | ||
* @see onErrorStopParsing | ||
*/ | ||
function onWarningStopParsing() { | ||
throw 'onWarningStopParsing'; | ||
} | ||
exports.__DOMHandler = DOMHandler; | ||
exports.DOMParser = DOMParser; | ||
exports.normalizeLineEndings = normalizeLineEndings; | ||
exports.DOMParser = DOMParser; | ||
exports.onErrorStopParsing = onErrorStopParsing; | ||
exports.onWarningStopParsing = onWarningStopParsing; |
'use strict'; | ||
var conventions = require('./conventions'); | ||
exports.assign = conventions.assign; | ||
exports.hasDefaultHTMLNamespace = conventions.hasDefaultHTMLNamespace; | ||
exports.isHTMLMimeType = conventions.isHTMLMimeType; | ||
exports.isValidMimeType = conventions.isValidMimeType; | ||
exports.MIME_TYPE = conventions.MIME_TYPE; | ||
exports.NAMESPACE = conventions.NAMESPACE; | ||
exports.ParseError = conventions.ParseError; | ||
var dom = require('./dom'); | ||
exports.DOMException = dom.DOMException; | ||
exports.DOMImplementation = dom.DOMImplementation; | ||
exports.XMLSerializer = dom.XMLSerializer; | ||
exports.DOMParser = require('./dom-parser').DOMParser; | ||
var domParser = require('./dom-parser'); | ||
exports.DOMParser = domParser.DOMParser; | ||
exports.onErrorStopParsing = domParser.onErrorStopParsing; | ||
exports.onWarningStopParsing = domParser.onWarningStopParsing; |
390
lib/sax.js
'use strict'; | ||
var conventions = require('./conventions'); | ||
var g = require('./grammar'); | ||
var isHTMLEscapableRawTextElement = conventions.isHTMLEscapableRawTextElement; | ||
var isHTMLMimeType = conventions.isHTMLMimeType; | ||
var isHTMLRawTextElement = conventions.isHTMLRawTextElement; | ||
var isHTMLEscapableRawTextElement = conventions.isHTMLEscapableRawTextElement; | ||
var NAMESPACE = conventions.NAMESPACE; | ||
var MIME_TYPE = conventions.MIME_TYPE; | ||
var ParseError = conventions.ParseError; | ||
//[5] Name ::= NameStartChar (NameChar)* | ||
// https://www.w3.org/TR/REC-xml/#NT-Name | ||
// https://www.w3.org/TR/xml-names/#ns-qualnames | ||
// roughly matches /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ | ||
// which means we currently do not allow : as the first character in a tag name | ||
var tagNamePattern = new RegExp( | ||
'^' + | ||
conventions.QNAME_START_CHAR.source + | ||
conventions.NAME_CHAR.source + | ||
'*(?:' + | ||
conventions.NAME_START_CHAR.source + | ||
conventions.NAME_CHAR.source + | ||
'*)?$' | ||
); | ||
//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') | ||
@@ -36,17 +25,2 @@ | ||
/** | ||
* Creates an error that will not be caught by XMLReader aka the SAX parser. | ||
* | ||
* @param {string} message | ||
* @param {any?} locator Optional, can provide details about the location in the source | ||
* @constructor | ||
*/ | ||
function ParseError(message, locator) { | ||
this.message = message; | ||
this.locator = locator; | ||
if (Error.captureStackTrace) Error.captureStackTrace(this, ParseError); | ||
} | ||
ParseError.prototype = new Error(); | ||
ParseError.prototype.name = ParseError.name; | ||
function XMLReader() {} | ||
@@ -63,4 +37,6 @@ | ||
}; | ||
function parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) { | ||
var isHTML = MIME_TYPE.isHTML(domBuilder.mimeType); | ||
var isHTML = isHTMLMimeType(domBuilder.mimeType); | ||
function fixedFromCharCode(code) { | ||
@@ -79,2 +55,3 @@ // String.prototype.fromCharCode does not supports | ||
} | ||
function entityReplacer(a) { | ||
@@ -91,2 +68,3 @@ var k = a.slice(1, -1); | ||
} | ||
function appendText(end) { | ||
@@ -101,2 +79,3 @@ //has some bugs | ||
} | ||
function position(p, m) { | ||
@@ -110,2 +89,3 @@ while (p >= lineEnd && (m = linePattern.exec(source))) { | ||
} | ||
var lineStart = 0; | ||
@@ -139,3 +119,3 @@ var lineEnd = 0; | ||
var tagNameRaw = source.substring(tagStart + 2, end > 0 ? end : undefined); | ||
var tagNameMatch = new RegExp('(' + tagNamePattern.source.slice(0, -1) + ')').exec(tagNameRaw); | ||
var tagNameMatch = g.QName_group.exec(tagNameRaw); | ||
// for the root level the config does not contain the tagName | ||
@@ -164,3 +144,5 @@ var tagName = | ||
// No known test case | ||
errorHandler.fatalError('end tag name: ' + tagName + ' is not match the current start tagName:' + config.tagName); | ||
return errorHandler.fatalError( | ||
'end tag name: ' + tagName + ' is not match the current start tagName:' + config.tagName | ||
); | ||
} | ||
@@ -176,7 +158,7 @@ } else { | ||
locator && position(tagStart); | ||
end = parseInstruction(source, tagStart, domBuilder); | ||
end = parseProcessingInstruction(source, tagStart, domBuilder, errorHandler); | ||
break; | ||
case '!': // <!doctype,<![CDATA,<!-- | ||
locator && position(tagStart); | ||
end = parseDCC(source, tagStart, domBuilder, errorHandler); | ||
end = parseDoctypeCommentOrCData(source, tagStart, domBuilder, errorHandler); | ||
break; | ||
@@ -237,2 +219,3 @@ default: | ||
} | ||
function copyLocator(f, t) { | ||
@@ -256,3 +239,3 @@ t.lineNumber = f.lineNumber; | ||
if (el.attributeNames.hasOwnProperty(qname)) { | ||
errorHandler.fatalError('Attribute ' + qname + ' redefined'); | ||
return errorHandler.fatalError('Attribute ' + qname + ' redefined'); | ||
} | ||
@@ -269,2 +252,3 @@ el.addValue( | ||
} | ||
var attrName; | ||
@@ -379,3 +363,2 @@ var value; | ||
} | ||
// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) | ||
return p; | ||
@@ -446,2 +429,3 @@ /*xml space '\x20' | #x9 | #xD | #xA; */ | ||
} | ||
/** | ||
@@ -527,2 +511,3 @@ * @return true if has new namespace define | ||
} | ||
function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) { | ||
@@ -546,2 +531,3 @@ // https://html.spec.whatwg.org/#raw-text-elements | ||
} | ||
function fixSelfClosed(source, elStartEnd, tagName, closeMap) { | ||
@@ -571,68 +557,272 @@ //if(tagName in closeMap){ | ||
function parseDCC(source, start, domBuilder, errorHandler) { | ||
//sure start with '<!' | ||
var next = source.charAt(start + 2); | ||
switch (next) { | ||
case '-': | ||
if (source.charAt(start + 3) === '-') { | ||
var end = source.indexOf('-->', start + 4); | ||
//append comment source.substring(4,end)//<!-- | ||
if (end > start) { | ||
domBuilder.comment(source, start + 4, end - start - 4); | ||
return end + 3; | ||
} else { | ||
errorHandler.error('Unclosed comment'); | ||
return -1; | ||
/** | ||
* @typedef ParseUtils | ||
* @property {function(relativeIndex: number?): string | undefined} char provides look ahead access | ||
* to a singe character relative to the current index | ||
* @property {function(): number} getIndex provides read-only access to the current index | ||
* @property {function(reg: RegExp): string | null} getMatch applies the provided regular expression | ||
* enforcing that it starts at the current index and returns the complete matching string, | ||
* and moves the current index by the length of the matching string. | ||
* @property {function(): string} getSource provides read-only access to the complete source | ||
* @property {function(places: number?): void} skip moves the current index | ||
* by places (defaults to 1) | ||
* @property {function(): number} skipBlanks moves the current index by the amount of white space | ||
* that directly follows the current index and returns the amount of whitespace chars skipped (0..n), | ||
* or -1 if the end of the source was reached. | ||
* @property {function(): string} substringFromIndex creates a substring from the current index to the end of `source` | ||
* @property {function(compareWith: string): boolean} substringStartsWith checks if source contains `compareWith`, | ||
* starting from the current index | ||
* | ||
* @see parseUtils | ||
*/ | ||
/** | ||
* A temporary scope for parsing and look ahead operations in `source`, | ||
* starting from index `start`. | ||
* | ||
* Some operations move the current index by a number of positions, | ||
* after which `getIndex` returns the new index. | ||
* | ||
* @param {string} source | ||
* @param {number} start | ||
* @returns {ParseUtils} | ||
*/ | ||
function parseUtils(source, start) { | ||
var index = start; | ||
function char(n) { | ||
n = n || 0; | ||
return source.charAt(index + n); | ||
} | ||
function skip(n) { | ||
n = n || 1; | ||
index += n; | ||
} | ||
function skipBlanks() { | ||
var blanks = 0; | ||
while (index < source.length) { | ||
var c = char(); | ||
if (c !== ' ' && c !== '\n' && c !== '\t' && c !== '\r') { | ||
return blanks; | ||
} | ||
blanks++; | ||
skip(); | ||
} | ||
return -1; | ||
} | ||
function substringFromIndex() { | ||
return source.substring(index); | ||
} | ||
function substringStartsWith(text) { | ||
return source.substring(index, index + text.length) === text; | ||
} | ||
function getMatch(args) { | ||
var expr = g.reg('^', args); | ||
var match = expr.exec(substringFromIndex()); | ||
if (match) { | ||
skip(match[0].length); | ||
return match[0]; | ||
} | ||
return null; | ||
} | ||
return { | ||
char: char, | ||
getIndex: function () { | ||
return index; | ||
}, | ||
getMatch: getMatch, | ||
getSource: function () { | ||
return source; | ||
}, | ||
skip: skip, | ||
skipBlanks: skipBlanks, | ||
substringFromIndex: substringFromIndex, | ||
substringStartsWith: substringStartsWith, | ||
}; | ||
} | ||
/** | ||
* @param {ParseUtils} p | ||
* @param {DOMHandler} errorHandler | ||
* @returns {string} | ||
*/ | ||
function parseDoctypeInternalSubset(p, errorHandler) { | ||
/** | ||
* @param {ParseUtils} p | ||
* @param {DOMHandler} errorHandler | ||
* @returns {string} | ||
*/ | ||
function parsePI(p, errorHandler) { | ||
var match = g.PI.exec(p.substringFromIndex()); | ||
if (!match) { | ||
return errorHandler.fatalError('processing instruction is not well-formed at position ' + p.getIndex()); | ||
} | ||
if (match[1].toLowerCase() === 'xml') { | ||
return errorHandler.fatalError( | ||
'xml declaration is only allowed at the start of the document, but found at position ' + p.getIndex() | ||
); | ||
} | ||
p.skip(match[0].length); | ||
return match[0]; | ||
} | ||
// Parse internal subset | ||
var source = p.getSource(); | ||
if (p.char() === '[') { | ||
p.skip(1); | ||
var intSubsetStart = p.getIndex(); | ||
p.skipBlanks(); | ||
while (p.getIndex() < source.length) { | ||
var current = null; | ||
// Only in external subset | ||
// if (char() === '<' && char(1) === '!' && char(2) === '[') { | ||
// parseConditionalSections(p, errorHandler); | ||
// } else | ||
if (p.char() === '<' && p.char(1) === '!') { | ||
switch (p.char(2)) { | ||
case 'E': | ||
if (p.char(3) === 'L') { | ||
current = p.getMatch(g.elementdecl); | ||
} else if (p.char(3) === 'N') { | ||
current = p.getMatch(g.EntityDecl); | ||
} | ||
break; | ||
case 'A': | ||
current = p.getMatch(g.AttlistDecl); | ||
break; | ||
case 'N': | ||
current = p.getMatch(g.NotationDecl); | ||
break; | ||
case '-': | ||
current = p.getMatch(g.Comment); | ||
break; | ||
} | ||
} else if (p.char() === '<' && p.char(1) === '?') { | ||
current = parsePI(p, errorHandler); | ||
} else if (p.char() === '%') { | ||
current = p.getMatch(g.PEReference); | ||
} else { | ||
//error | ||
return -1; | ||
return errorHandler.fatalError('Error detected in Markup declaration'); | ||
} | ||
default: | ||
if (source.substr(start + 3, 6) == 'CDATA[') { | ||
var end = source.indexOf(']]>', start + 9); | ||
if (!current) { | ||
return errorHandler.fatalError('Error in internal subset at position ' + p.getIndex()); | ||
} | ||
p.skipBlanks(); | ||
if (p.char() === ']') { | ||
var internalSubset = source.substring(intSubsetStart, p.getIndex()); | ||
p.skip(1); | ||
return internalSubset; | ||
} | ||
p.skipBlanks(); | ||
} | ||
return errorHandler.fatalError('doctype internal subset is not well-formed, missing ]'); | ||
} | ||
} | ||
/** | ||
* Called when the parser encounters an element starting with '<!'. | ||
* @param {string} source the xml | ||
* @param {number} start the start index of the '<!' | ||
* @param {DOMHandler} domBuilder | ||
* @param {DOMHandler} errorHandler | ||
* @returns {number|never} the end index of the element | ||
* @throws ParseError in case the element is not well-formed | ||
*/ | ||
function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler) { | ||
var p = parseUtils(source, start); | ||
switch (p.char(2)) { | ||
case '-': | ||
// should be a comment | ||
var comment = p.getMatch(g.Comment); | ||
if (comment) { | ||
domBuilder.comment(comment, g.COMMENT_START.length, comment.length - g.COMMENT_START.length - g.COMMENT_END.length); | ||
return p.getIndex(); | ||
} else { | ||
return errorHandler.fatalError('comment is not well-formed at position ' + p.getIndex()); | ||
} | ||
case '[': | ||
// should be CDATA | ||
var cdata = p.getMatch(g.CDSect); | ||
if (cdata) { | ||
domBuilder.startCDATA(); | ||
domBuilder.characters(source, start + 9, end - start - 9); | ||
domBuilder.characters(cdata, g.CDATA_START.length, cdata.length - g.CDATA_START.length - g.CDATA_END.length); | ||
domBuilder.endCDATA(); | ||
return end + 3; | ||
return p.getIndex(); | ||
} else { | ||
return errorHandler.fatalError('Invalid CDATA starting at position ' + start); | ||
} | ||
//<!DOCTYPE | ||
//startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) | ||
var matchs = split(source, start); | ||
var len = matchs.length; | ||
if (len > 1 && /!doctype/i.test(matchs[0][0])) { | ||
var name = matchs[1][0]; | ||
var pubid = false; | ||
var sysid = false; | ||
if (len > 3) { | ||
if (/^public$/i.test(matchs[2][0])) { | ||
pubid = matchs[3][0]; | ||
sysid = len > 4 && matchs[4][0]; | ||
} else if (/^system$/i.test(matchs[2][0])) { | ||
sysid = matchs[3][0]; | ||
} | ||
case 'D': { | ||
// should be DOCTYPE | ||
var doctype = { | ||
name: undefined, | ||
publicId: undefined, | ||
systemId: undefined, | ||
internalSubset: undefined, | ||
}; | ||
if (!p.substringStartsWith(g.DOCTYPE_DECL_START)) { | ||
return errorHandler.fatalError('Expected ' + g.DOCTYPE_DECL_START + ' at position ' + p.getIndex()); | ||
} | ||
p.skip(g.DOCTYPE_DECL_START.length); | ||
if (p.skipBlanks() < 1) { | ||
return errorHandler.fatalError('Expected whitespace after ' + g.DOCTYPE_DECL_START + ' at position ' + p.getIndex()); | ||
} | ||
// Parse the DOCTYPE name | ||
doctype.name = p.getMatch(g.Name); | ||
if (!doctype.name) | ||
return errorHandler.fatalError('doctype name missing or contains unexpected characters at position ' + p.getIndex()); | ||
p.skipBlanks(); | ||
// Check for ExternalID | ||
if (p.substringStartsWith(g.PUBLIC) || p.substringStartsWith(g.SYSTEM)) { | ||
var match = g.ExternalID_match.exec(p.substringFromIndex()); | ||
if (!match) { | ||
return errorHandler.fatalError('doctype external id is not well-formed at position ' + p.getIndex()); | ||
} | ||
var lastMatch = matchs[len - 1]; | ||
domBuilder.startDTD(name, pubid, sysid); | ||
domBuilder.endDTD(); | ||
if (match.groups.SystemLiteralOnly !== undefined) { | ||
doctype.systemId = match.groups.SystemLiteralOnly; | ||
} else { | ||
doctype.systemId = match.groups.SystemLiteral; | ||
doctype.publicId = match.groups.PubidLiteral; | ||
} | ||
p.skip(match[0].length); | ||
} | ||
return lastMatch.index + lastMatch[0].length; | ||
p.skipBlanks(); | ||
doctype.internalSubset = parseDoctypeInternalSubset(p, errorHandler); | ||
p.skipBlanks(); | ||
if (p.char() !== '>') { | ||
return errorHandler.fatalError('doctype not terminated with > at position ' + p.getIndex()); | ||
} | ||
p.skip(1); | ||
domBuilder.startDTD(doctype.name, doctype.publicId, doctype.systemId, doctype.internalSubset); | ||
domBuilder.endDTD(); | ||
return p.getIndex(); | ||
} | ||
default: | ||
return errorHandler.fatalError('Not well-formed XML starting with "<!" at position ' + start); | ||
} | ||
return -1; | ||
} | ||
function parseInstruction(source, start, domBuilder) { | ||
var end = source.indexOf('?>', start); | ||
if (end) { | ||
var match = source.substring(start, end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/); | ||
if (match) { | ||
domBuilder.processingInstruction(match[1], match[2]); | ||
return end + 2; | ||
} else { | ||
//error | ||
return -1; | ||
function parseProcessingInstruction(source, start, domBuilder, errorHandler) { | ||
var match = source.substring(start).match(g.PI); | ||
if (!match) { | ||
return errorHandler.fatalError('Invalid processing instruction starting at position ' + start); | ||
} | ||
if (match[1].toLowerCase() === 'xml') { | ||
if (start > 0) { | ||
return errorHandler.fatalError( | ||
'processing instruction at position ' + start + ' is an xml declaration which is only at the start of the document' | ||
); | ||
} | ||
if (!g.XMLDecl.test(source.substring(start))) { | ||
return errorHandler.fatalError('xml declaration is not well-formed'); | ||
} | ||
} | ||
return -1; | ||
domBuilder.processingInstruction(match[1], match[2]); | ||
return start + match[0].length; | ||
} | ||
@@ -643,5 +833,6 @@ | ||
} | ||
ElementAttributes.prototype = { | ||
setTagName: function (tagName) { | ||
if (!tagNamePattern.test(tagName)) { | ||
if (!g.QName_exact.test(tagName)) { | ||
throw new Error('invalid tagName:' + tagName); | ||
@@ -652,3 +843,3 @@ } | ||
addValue: function (qName, value, offset) { | ||
if (!tagNamePattern.test(qName)) { | ||
if (!g.QName_exact.test(qName)) { | ||
throw new Error('invalid attribute:' + qName); | ||
@@ -687,15 +878,4 @@ } | ||
function split(source, start) { | ||
var match; | ||
var buf = []; | ||
var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g; | ||
reg.lastIndex = start; | ||
reg.exec(source); //skip < | ||
while ((match = reg.exec(source))) { | ||
buf.push(match); | ||
if (match[1]) return buf; | ||
} | ||
} | ||
exports.XMLReader = XMLReader; | ||
exports.ParseError = ParseError; | ||
exports.parseUtils = parseUtils; | ||
exports.parseDoctypeCommentOrCData = parseDoctypeCommentOrCData; |
{ | ||
"name": "@xmldom/xmldom", | ||
"version": "0.9.0-beta.8", | ||
"version": "0.9.0-beta.9", | ||
"description": "A pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module.", | ||
@@ -35,5 +35,4 @@ "keywords": [ | ||
"start": "nodemon --watch package.json --watch lib --watch test --exec 'npm --silent run test && npm --silent run lint'", | ||
"stryker": "stryker run", | ||
"stryker:dry-run": "stryker run -m '' --reporters progress", | ||
"test": "jest", | ||
"test:types": "cd examples/typescript-node-es6 && ./pretest.sh 3 && ./pretest.sh 4 && ./pretest.sh 5 && node dist/index.js", | ||
"testrelease": "npm test && eslint lib", | ||
@@ -48,3 +47,2 @@ "version": "./changelog-has-version.sh", | ||
"devDependencies": { | ||
"@stryker-mutator/core": "5.6.1", | ||
"auto-changelog": "2.4.0", | ||
@@ -62,2 +60,3 @@ "eslint": "8.42.0", | ||
"prettier": "2.8.8", | ||
"rxjs": "7.8.1", | ||
"xmltest": "1.5.0", | ||
@@ -64,0 +63,0 @@ "yauzl": "2.10.0" |
@@ -7,9 +7,12 @@ # @xmldom/xmldom | ||
[![license(MIT)](https://img.shields.io/npm/l/@xmldom/xmldom?color=blue&style=flat-square)](https://github.com/xmldom/xmldom/blob/master/LICENSE) | ||
[![npm](https://img.shields.io/npm/v/@xmldom/xmldom?style=flat-square)](https://www.npmjs.com/package/@xmldom/xmldom) | ||
[![Socket Badge](https://socket.dev/api/badge/npm/package/@xmldom/xmldom)](https://socket.dev/npm/package/@xmldom/xmldom) | ||
[![snyk.io package health](https://snyk.io/advisor/npm-package/@xmldom/xmldom/badge.svg)](https://snyk.io/advisor/npm-package/@xmldom/xmldom) | ||
[![npm:latest](https://img.shields.io/npm/v/@xmldom/xmldom/latest?style=flat-square)](https://www.npmjs.com/package/@xmldom/xmldom) | ||
[![npm:next](https://img.shields.io/npm/v/@xmldom/xmldom/next?style=flat-square)](https://www.npmjs.com/package/@xmldom/xmldom?activeTab=versions) | ||
[![npm:lts](https://img.shields.io/npm/v/@xmldom/xmldom/lts?style=flat-square)](https://www.npmjs.com/package/@xmldom/xmldom?activeTab=versions) | ||
[![bug issues](https://img.shields.io/github/issues/xmldom/xmldom/bug?color=red&style=flat-square)](https://github.com/xmldom/xmldom/issues?q=is%3Aissue+is%3Aopen+label%3Abug) | ||
[![help-wanted issues](https://img.shields.io/github/issues/xmldom/xmldom/help-wanted?color=darkgreen&style=flat-square)](https://github.com/xmldom/xmldom/issues?q=is%3Aissue+is%3Aopen+label%3Ahelp-wanted) | ||
[![Mutation report](https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fxmldom%2Fxmldom%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/xmldom/xmldom/master) | ||
xmldom is a javascript [ponyfill](https://ponyfill.com/) to provide the following APIs [that are present in modern browsers](https://caniuse.com/xml-serializer) to other runtimes: | ||
@@ -59,3 +62,3 @@ - convert an XML string into a DOM tree | ||
Note: in Typescript ~and ES6~(see #316) you can use the `import` approach, as follows: | ||
Note: in Typescript ~~and ES6~~ (see [#316](https://github.com/xmldom/xmldom/issues/316)) you can use the `import` approach, as follows: | ||
@@ -68,29 +71,14 @@ ```typescript | ||
* [DOMParser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser): | ||
* [DOMParser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser): | ||
```javascript | ||
parseFromString(xmlsource,mimeType) | ||
``` | ||
* **options extension** _by xmldom_ (not DOM standard!!) | ||
```javascript | ||
parseFromString(xmlsource, mimeType) | ||
``` | ||
* **options extension** _by xmldom_ (not DOM standard!!) | ||
```javascript | ||
//added the options argument | ||
new DOMParser(options) | ||
//errorHandler is supported | ||
new DOMParser({ | ||
/** | ||
* locator is always need for error position info | ||
*/ | ||
locator:{}, | ||
/** | ||
* you can override the errorHandler for xml parser | ||
* @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html | ||
*/ | ||
errorHandler:{warning:function(w){console.warn(w)},error:callback,fatalError:callback} | ||
//only callback model | ||
//errorHandler:function(level,msg){console.log(level,msg)} | ||
}) | ||
``` | ||
```javascript | ||
// the options argument can be used to modify behavior | ||
// for more details check the documentation on the code or type definition | ||
new DOMParser(options) | ||
``` | ||
@@ -166,3 +154,3 @@ * [XMLSerializer](https://developer.mozilla.org/en-US/docs/Web/API/XMLSerializer) | ||
method: | ||
- `hasFeature(feature, version)` | ||
- `hasFeature(feature, version)` (deprecated) | ||
- `createDocumentType(qualifiedName, publicId, systemId)` | ||
@@ -325,20 +313,22 @@ - `createDocument(namespaceURI, qualifiedName, doctype)` | ||
The original author claims that xmldom implements [DOM Level 2] in a "fully compatible" way and some parts of [DOM Level 3], but there are not enough tests to prove this. Both Specifications are now superseded by the [DOM Level 4 aka Living standard] wich has a much broader scope than xmldom. | ||
In the past there have been multiple (even breaking) changes to align xmldom with the living standard, | ||
so if you find a difference that is not documented, any contribution to resolve the difference is very welcome (even just reporting it as an issue). | ||
xmldom implements the following interfaces (most constructors are currently not exposed): | ||
- `Attr` | ||
xmldom implements the following interfaces (only the ones marked with `*` are currently exposed): | ||
- `Attr` * | ||
- `CDATASection` | ||
- `CharacterData` | ||
- `Comment` | ||
- `Document` | ||
- `Document` * | ||
- `DocumentFragment` | ||
- `DocumentType` | ||
- `DOMException` (constructor exposed) | ||
- `DOMImplementation` (constructor exposed) | ||
- `Element` | ||
- `DocumentType` * | ||
- `DOMException` * | ||
- `DOMImplementation` * | ||
- `Element` * | ||
- `Entity` | ||
- `EntityReference` | ||
- `LiveNodeList` | ||
- `NamedNodeMap` | ||
- `Node` (constructor exposed) | ||
- `NodeList` | ||
- `NamedNodeMap` * | ||
- `Node` * | ||
- `NodeList` * | ||
- `Notation` | ||
@@ -352,4 +342,3 @@ - `ProcessingInstruction` | ||
xmldom does not have any goal of supporting the full spec, but it has some capability to parse, report and serialize things differently when "detecting HTML" (by checking the default namespace). | ||
There is an upcoming change to better align the implementation with the latest specs, related to <https://github.com/xmldom/xmldom/issues/203>. | ||
xmldom does not have any goal of supporting the full spec, but it has some capability to parse, report and serialize things differently when it is told to parse HTML (by passing the HTML namespace). | ||
@@ -356,0 +345,0 @@ ### SAX, XML, XMLNS |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
268424
14
6907
346