@xmldom/xmldom 0.9.6 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.9.7](https://github.com/xmldom/xmldom/compare/0.9.6...0.9.7)
8
+
9
+ ### Added
10
+
11
+ - Implementation of `hasAttributes` [`#804`](https://github.com/xmldom/xmldom/pull/804)
12
+
13
+ ### Fixed
14
+
15
+ - locator is now true even when other options are being used for the DOMParser [`#802`](https://github.com/xmldom/xmldom/issues/802) / [`#803`](https://github.com/xmldom/xmldom/pull/803)
16
+ - allow case-insensitive DOCTYPE in HTML [`#817`](https://github.com/xmldom/xmldom/issues/817) / [`#819`](https://github.com/xmldom/xmldom/pull/819)
17
+
18
+ ### Performance
19
+
20
+ - simplify `DOM.compareDocumentPosition` [`#805`](https://github.com/xmldom/xmldom/pull/805)
21
+
22
+ ### Chore
23
+
24
+ - updated devDependencies
25
+
26
+ Thank you,
27
+ [@zorkow](https://github.com/zorkow),
28
+ [@Ponynjaa](https://github.com/Ponynjaa),
29
+ [@WesselKroos](https://github.com/WesselKroos),
30
+ for your contributions.
31
+
32
+
7
33
  ## [0.9.6](https://github.com/xmldom/xmldom/compare/0.9.5...0.9.6)
8
34
 
9
35
  ### Fixed
@@ -324,7 +324,7 @@ var MIME_TYPE = freeze({
324
324
  XML_APPLICATION: 'application/xml',
325
325
 
326
326
  /**
327
- * `text/html`, an alias for `application/xml`.
327
+ * `text/xml`, an alias for `application/xml`.
328
328
  *
329
329
  * @see https://tools.ietf.org/html/rfc7303#section-9.2 RFC 7303
330
330
  * @see https://www.iana.org/assignments/media-types/text/xml IANA MimeType registration
package/lib/dom-parser.js CHANGED
@@ -101,7 +101,10 @@ function normalizeLineEndings(input) {
101
101
  * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-parsing-and-serialization
102
102
  */
103
103
  function DOMParser(options) {
104
- options = options || { locator: true };
104
+ options = options || {};
105
+ if (options.locator === undefined) {
106
+ options.locator = true;
107
+ }
105
108
 
106
109
  /**
107
110
  * The method to use instead of `conventions.assign`, which is used to copy values from
package/lib/dom.js CHANGED
@@ -257,23 +257,6 @@ var DocumentPosition = conventions.freeze({
257
257
  });
258
258
 
259
259
  //helper functions for compareDocumentPosition
260
- /**
261
- * Constructs a parent chain for a node.
262
- *
263
- * @param {Node} node
264
- * The start node from which the parent chain will be constructed.
265
- * @returns {Node[]}
266
- * The array of nodes representing the parent chain from the root to the specified node.
267
- */
268
- function parentChain(node) {
269
- var chain = [];
270
- while (node.parentNode || node.ownerElement) {
271
- node = node.parentNode || node.ownerElement;
272
- chain.unshift(node);
273
- }
274
- return chain;
275
- }
276
-
277
260
  /**
278
261
  * Finds the common ancestor in two parent chains.
279
262
  *
@@ -1476,15 +1459,36 @@ Node.prototype = {
1476
1459
  : DocumentPosition.DOCUMENT_POSITION_PRECEDING)
1477
1460
  );
1478
1461
  }
1479
- var chain1 = parentChain(node1);
1480
- var chain2 = parentChain(node2);
1481
- if ((!attr1 && chain2.indexOf(node1) >= 0) || (attr2 && node1 === node2)) {
1462
+ if (attr2 && node1 === node2) {
1482
1463
  return DocumentPosition.DOCUMENT_POSITION_CONTAINS + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
1483
1464
  }
1484
- if ((!attr2 && chain1.indexOf(node2) >= 0) || (attr1 && node1 === node2)) {
1465
+ if (attr1 && node1 === node2) {
1485
1466
  return DocumentPosition.DOCUMENT_POSITION_CONTAINED_BY + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
1486
1467
  }
1487
- var ca = commonAncestor(chain2, chain1);
1468
+
1469
+ var chain1 = [];
1470
+ var ancestor1 = node1.parentNode;
1471
+ while (ancestor1) {
1472
+ if (!attr2 && ancestor1 === node2) {
1473
+ return DocumentPosition.DOCUMENT_POSITION_CONTAINED_BY + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
1474
+ }
1475
+ chain1.push(ancestor1);
1476
+ ancestor1 = ancestor1.parentNode;
1477
+ }
1478
+ chain1.reverse();
1479
+
1480
+ var chain2 = [];
1481
+ var ancestor2 = node2.parentNode;
1482
+ while (ancestor2) {
1483
+ if (!attr1 && ancestor2 === node1) {
1484
+ return DocumentPosition.DOCUMENT_POSITION_CONTAINS + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
1485
+ }
1486
+ chain2.push(ancestor2);
1487
+ ancestor2 = ancestor2.parentNode;
1488
+ }
1489
+ chain2.reverse();
1490
+
1491
+ var ca = commonAncestor(chain1, chain2);
1488
1492
  for (var n in ca.childNodes) {
1489
1493
  var child = ca.childNodes[n];
1490
1494
  if (child === node2) return DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
@@ -2353,6 +2357,16 @@ Element.prototype = {
2353
2357
  _isInHTMLDocumentAndNamespace: function () {
2354
2358
  return this.ownerDocument.type === 'html' && this.namespaceURI === NAMESPACE.HTML;
2355
2359
  },
2360
+ /**
2361
+ * Implementaton of Level2 Core function hasAttributes.
2362
+ *
2363
+ * @returns {boolean}
2364
+ * True if attribute list is not empty.
2365
+ * @see https://www.w3.org/TR/DOM-Level-2-Core/#core-ID-NodeHasAttrs
2366
+ */
2367
+ hasAttributes: function () {
2368
+ return !!(this.attributes && this.attributes.length);
2369
+ },
2356
2370
  hasAttribute: function (name) {
2357
2371
  return !!this.getAttributeNode(name);
2358
2372
  },
package/lib/grammar.js CHANGED
@@ -381,6 +381,9 @@ var ATTLIST_DECL_START = '<!ATTLIST';
381
381
  // to support XML without namespaces in DTD we can not restrict it to QName
382
382
  var AttlistDecl = reg(ATTLIST_DECL_START, S, Name, AttDef, '*', S_OPT, '>');
383
383
 
384
+ // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#about:legacy-compat
385
+ var ABOUT_LEGACY_COMPAT = 'about:legacy-compat';
386
+ var ABOUT_LEGACY_COMPAT_SystemLiteral = regg('"' + ABOUT_LEGACY_COMPAT + '"', '|', "'" + ABOUT_LEGACY_COMPAT + "'");
384
387
  var SYSTEM = 'SYSTEM';
385
388
  var PUBLIC = 'PUBLIC';
386
389
  // https://www.w3.org/TR/xml11/#NT-ExternalID
@@ -494,6 +497,8 @@ exports.chars_without = chars_without;
494
497
  exports.detectUnicodeSupport = detectUnicodeSupport;
495
498
  exports.reg = reg;
496
499
  exports.regg = regg;
500
+ exports.ABOUT_LEGACY_COMPAT = ABOUT_LEGACY_COMPAT;
501
+ exports.ABOUT_LEGACY_COMPAT_SystemLiteral = ABOUT_LEGACY_COMPAT_SystemLiteral;
497
502
  exports.AttlistDecl = AttlistDecl;
498
503
  exports.CDATA_START = CDATA_START;
499
504
  exports.CDATA_END = CDATA_END;
package/lib/sax.js CHANGED
@@ -587,8 +587,10 @@ function _copy(source, target) {
587
587
  * @property {function(): string} substringFromIndex
588
588
  * creates a substring from the current index to the end of `source`
589
589
  * @property {function(compareWith: string): boolean} substringStartsWith
590
- * Checks if source contains `compareWith`,
591
- * starting from the current index.
590
+ * Checks if `source` contains `compareWith`, starting from the current index.
591
+ * @property {function(compareWith: string): boolean} substringStartsWithCaseInsensitive
592
+ * Checks if `source` contains `compareWith`, starting from the current index,
593
+ * comparing the upper case of both sides.
592
594
  * @see {@link parseUtils}
593
595
  */
594
596
 
@@ -634,6 +636,9 @@ function parseUtils(source, start) {
634
636
  function substringStartsWith(text) {
635
637
  return source.substring(index, index + text.length) === text;
636
638
  }
639
+ function substringStartsWithCaseInsensitive(text) {
640
+ return source.substring(index, index + text.length).toUpperCase() === text.toUpperCase();
641
+ }
637
642
 
638
643
  function getMatch(args) {
639
644
  var expr = g.reg('^', args);
@@ -657,6 +662,7 @@ function parseUtils(source, start) {
657
662
  skipBlanks: skipBlanks,
658
663
  substringFromIndex: substringFromIndex,
659
664
  substringStartsWith: substringStartsWith,
665
+ substringStartsWithCaseInsensitive: substringStartsWithCaseInsensitive,
660
666
  };
661
667
  }
662
668
 
@@ -753,7 +759,7 @@ function parseDoctypeInternalSubset(p, errorHandler) {
753
759
  function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler, isHTML) {
754
760
  var p = parseUtils(source, start);
755
761
 
756
- switch (p.char(2)) {
762
+ switch (isHTML ? p.char(2).toUpperCase() : p.char(2)) {
757
763
  case '-':
758
764
  // should be a comment
759
765
  var comment = p.getMatch(g.Comment);
@@ -782,7 +788,7 @@ function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler, isH
782
788
  if (domBuilder.doc && domBuilder.doc.documentElement) {
783
789
  return errorHandler.fatalError('Doctype not allowed inside or after documentElement at position ' + p.getIndex());
784
790
  }
785
- if (!p.substringStartsWith(g.DOCTYPE_DECL_START)) {
791
+ if (isHTML ? !p.substringStartsWithCaseInsensitive(g.DOCTYPE_DECL_START) : !p.substringStartsWith(g.DOCTYPE_DECL_START)) {
786
792
  return errorHandler.fatalError('Expected ' + g.DOCTYPE_DECL_START + ' at position ' + p.getIndex());
787
793
  }
788
794
  p.skip(g.DOCTYPE_DECL_START.length);
@@ -800,6 +806,10 @@ function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler, isH
800
806
  doctype.name = p.getMatch(g.Name);
801
807
  if (!doctype.name)
802
808
  return errorHandler.fatalError('doctype name missing or contains unexpected characters at position ' + p.getIndex());
809
+
810
+ if (isHTML && doctype.name.toLowerCase() !== 'html') {
811
+ errorHandler.warning('Unexpected DOCTYPE in HTML document at position ' + p.getIndex());
812
+ }
803
813
  p.skipBlanks();
804
814
 
805
815
  // Check for ExternalID
@@ -815,10 +825,26 @@ function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler, isH
815
825
  doctype.publicId = match.groups.PubidLiteral;
816
826
  }
817
827
  p.skip(match[0].length);
828
+ } else if (isHTML && p.substringStartsWithCaseInsensitive(g.SYSTEM)) {
829
+ // https://html.spec.whatwg.org/multipage/syntax.html#doctype-legacy-string
830
+ p.skip(g.SYSTEM.length);
831
+ if (p.skipBlanks() < 1) {
832
+ return errorHandler.fatalError('Expected whitespace after ' + g.SYSTEM + ' at position ' + p.getIndex());
833
+ }
834
+ doctype.systemId = p.getMatch(g.ABOUT_LEGACY_COMPAT_SystemLiteral);
835
+ if (!doctype.systemId) {
836
+ return errorHandler.fatalError(
837
+ 'Expected ' + g.ABOUT_LEGACY_COMPAT + ' in single or double quotes after ' + g.SYSTEM + ' at position ' + p.getIndex()
838
+ );
839
+ }
840
+ }
841
+ if (isHTML && doctype.systemId && !g.ABOUT_LEGACY_COMPAT_SystemLiteral.test(doctype.systemId)) {
842
+ errorHandler.warning('Unexpected doctype.systemId in HTML document at position ' + p.getIndex());
843
+ }
844
+ if (!isHTML) {
845
+ p.skipBlanks();
846
+ doctype.internalSubset = parseDoctypeInternalSubset(p, errorHandler);
818
847
  }
819
-
820
- p.skipBlanks();
821
- doctype.internalSubset = parseDoctypeInternalSubset(p, errorHandler);
822
848
  p.skipBlanks();
823
849
  if (p.char() !== '>') {
824
850
  return errorHandler.fatalError('doctype not terminated with > at position ' + p.getIndex());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmldom/xmldom",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "description": "A pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module.",
5
5
  "keywords": [
6
6
  "w3c",
@@ -48,16 +48,16 @@
48
48
  "@jazzer.js/jest-runner": "2.1.0",
49
49
  "auto-changelog": "2.5.0",
50
50
  "eslint": "8.57.1",
51
- "eslint-config-prettier": "9.1.0",
51
+ "eslint-config-prettier": "10.0.1",
52
52
  "eslint-plugin-anti-trojan-source": "1.1.1",
53
53
  "eslint-plugin-es5": "1.5.0",
54
- "eslint-plugin-n": "17.14.0",
55
- "eslint-plugin-prettier": "5.2.1",
54
+ "eslint-plugin-n": "17.15.1",
55
+ "eslint-plugin-prettier": "5.2.2",
56
56
  "get-stream": "6.0.1",
57
57
  "jest": "29.7.0",
58
- "nodemon": "3.1.7",
58
+ "nodemon": "3.1.9",
59
59
  "np": "8.0.4",
60
- "prettier": "3.4.1",
60
+ "prettier": "3.4.2",
61
61
  "rxjs": "7.8.1",
62
62
  "xmltest": "2.0.3",
63
63
  "yauzl": "3.2.0"