domino 2.1.0 → 2.1.4
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 +19 -0
- package/README.md +25 -0
- package/lib/Element.js +15 -3
- package/lib/HTMLParser.js +14 -8
- package/lib/Node.js +11 -153
- package/lib/NodeUtils.js +168 -0
- package/lib/defineElement.js +2 -1
- package/lib/htmlelts.js +13 -2
- package/lib/index.js +51 -0
- package/lib/select.js +24 -11
- package/package.json +5 -5
- package/test/domino.js +162 -0
- package/test/web-platform-blacklist.json +38 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# domino 2.1.4 (16 Dec 2019)
|
|
2
|
+
* Bug fix for `Element#closest` when selector doesn't match (#154)
|
|
3
|
+
|
|
4
|
+
# domino 2.1.3 (6 Mar 2019)
|
|
5
|
+
* Bug fix for CSS `$=` selector and for matches on root `<html>` element.
|
|
6
|
+
* Renamed CSS `:matches` to `:is`
|
|
7
|
+
( https://github.com/w3c/csswg-drafts/issues/3258 )
|
|
8
|
+
* Bug fix for CSS matches with escape characters in tag name.
|
|
9
|
+
|
|
10
|
+
# domino 2.1.2 (14 Feb 2019)
|
|
11
|
+
* Allow writable Element constructors unless __domino_frozen__ is set to true (#138)
|
|
12
|
+
* Bug fix for CSS `$=` selector. (#135)
|
|
13
|
+
* Move `Node#_serializeOne()` to `NodeUtils.serializeOne()` to reduce pressure
|
|
14
|
+
on the megamorphic stub cache in V8, and thereby improve throughput (#142).
|
|
15
|
+
* Implemented `HTMLOptionElement#text` and `HTMLOptionElement#value` (#136)
|
|
16
|
+
|
|
17
|
+
# domino 2.1.1 (30 Nov 2018)
|
|
18
|
+
* Add `domino.createIncrementalHTMLParser` interface.
|
|
19
|
+
|
|
1
20
|
# domino 2.1.0 (13 Aug 2018)
|
|
2
21
|
* Fix `ContainerNode#removeChildren()` when there is more than one child (#129)
|
|
3
22
|
* Implement `Document#scrollingElement` (#107)
|
package/README.md
CHANGED
|
@@ -63,6 +63,31 @@ console.log(h1.innerHTML);
|
|
|
63
63
|
console.log(h1 instanceof Element);
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
There is also an incremental parser available, if you need to interleave
|
|
67
|
+
parsing with other processing:
|
|
68
|
+
```javascript
|
|
69
|
+
var domino = require('domino');
|
|
70
|
+
|
|
71
|
+
var pauseAfter = function(ms) {
|
|
72
|
+
var start = Date.now();
|
|
73
|
+
return function() { return (Date.now() - start) >= ms; };
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
var incrParser = domino.createIncrementalHTMLParser();
|
|
77
|
+
incrParser.write('<p>hello<');
|
|
78
|
+
incrParser.write('b>&am');
|
|
79
|
+
incrParser.process(pauseAfter(1/*ms*/)); // can interleave processing
|
|
80
|
+
incrParser.write('p;');
|
|
81
|
+
// ...etc...
|
|
82
|
+
incrParser.end(); // when done writing the document
|
|
83
|
+
|
|
84
|
+
while (incrParser.process(pauseAfter(10/*ms*/))) {
|
|
85
|
+
// ...do other housekeeping...
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(incrParser.document().outerHTML);
|
|
89
|
+
```
|
|
90
|
+
|
|
66
91
|
If you want a more standards-compliant way to create a `Document`, you can
|
|
67
92
|
also use [DOMImplementation](https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation):
|
|
68
93
|
```javascript
|
package/lib/Element.js
CHANGED
|
@@ -7,6 +7,7 @@ var NAMESPACE = utils.NAMESPACE;
|
|
|
7
7
|
var attributes = require('./attributes');
|
|
8
8
|
var Node = require('./Node');
|
|
9
9
|
var NodeList = require('./NodeList');
|
|
10
|
+
var NodeUtils = require('./NodeUtils');
|
|
10
11
|
var FilteredElementList = require('./FilteredElementList');
|
|
11
12
|
var DOMException = require('./DOMException');
|
|
12
13
|
var DOMTokenList = require('./DOMTokenList');
|
|
@@ -98,7 +99,15 @@ Element.prototype = Object.create(ContainerNode.prototype, {
|
|
|
98
99
|
// "the attribute must return the result of running the HTML fragment
|
|
99
100
|
// serialization algorithm on a fictional node whose only child is
|
|
100
101
|
// the context object"
|
|
101
|
-
|
|
102
|
+
//
|
|
103
|
+
// The serialization logic is intentionally implemented in a separate
|
|
104
|
+
// `NodeUtils` helper instead of the more obvious choice of a private
|
|
105
|
+
// `_serializeOne()` method on the `Node.prototype` in order to avoid
|
|
106
|
+
// the megamorphic `this._serializeOne` property access, which reduces
|
|
107
|
+
// performance unnecessarily. If you need specialized behavior for a
|
|
108
|
+
// certain subclass, you'll need to implement that in `NodeUtils`.
|
|
109
|
+
// See https://github.com/fgnass/domino/pull/142 for more information.
|
|
110
|
+
return NodeUtils.serializeOne(this, { nodeType: 0 });
|
|
102
111
|
},
|
|
103
112
|
set: function(v) {
|
|
104
113
|
var document = this.ownerDocument;
|
|
@@ -913,8 +922,11 @@ Element.prototype = Object.create(ContainerNode.prototype, {
|
|
|
913
922
|
|
|
914
923
|
closest: { value: function(selector) {
|
|
915
924
|
var el = this;
|
|
916
|
-
|
|
917
|
-
|
|
925
|
+
do {
|
|
926
|
+
if (el.matches && el.matches(selector)) { return el; }
|
|
927
|
+
el = el.parentElement || el.parentNode;
|
|
928
|
+
} while (el !== null && el.nodeType === Node.ELEMENT_NODE);
|
|
929
|
+
return null;
|
|
918
930
|
}},
|
|
919
931
|
|
|
920
932
|
querySelector: { value: function(selector) {
|
package/lib/HTMLParser.js
CHANGED
|
@@ -2035,13 +2035,15 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2035
2035
|
// text to be parsed, and should be false or omitted otherwise.
|
|
2036
2036
|
// The second argument must not be set for recursive invocations
|
|
2037
2037
|
// from document.write()
|
|
2038
|
-
parse: function(s, end) {
|
|
2038
|
+
parse: function(s, end, shouldPauseFunc) {
|
|
2039
|
+
var moreToDo;
|
|
2039
2040
|
|
|
2040
2041
|
// If we're paused, remember the text to parse, but
|
|
2041
2042
|
// don't parse it now.
|
|
2043
|
+
// (Don't invoke shouldPauseFunc because we haven't handled 'end' yet.)
|
|
2042
2044
|
if (paused > 0) {
|
|
2043
2045
|
leftovers += s;
|
|
2044
|
-
return;
|
|
2046
|
+
return true; // more to do
|
|
2045
2047
|
}
|
|
2046
2048
|
|
|
2047
2049
|
|
|
@@ -2077,7 +2079,7 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2077
2079
|
}
|
|
2078
2080
|
|
|
2079
2081
|
reentrant_invocations++;
|
|
2080
|
-
scanChars();
|
|
2082
|
+
moreToDo = scanChars(shouldPauseFunc);
|
|
2081
2083
|
leftovers = chars.substring(nextchar, numchars);
|
|
2082
2084
|
reentrant_invocations--;
|
|
2083
2085
|
}
|
|
@@ -2096,6 +2098,7 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2096
2098
|
|
|
2097
2099
|
// Now scan as many of these new chars as we can
|
|
2098
2100
|
scanChars();
|
|
2101
|
+
moreToDo = false;
|
|
2099
2102
|
|
|
2100
2103
|
leftovers = chars.substring(nextchar, numchars);
|
|
2101
2104
|
|
|
@@ -2117,6 +2120,7 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2117
2120
|
// Decrement the counter
|
|
2118
2121
|
reentrant_invocations--;
|
|
2119
2122
|
}
|
|
2123
|
+
return moreToDo;
|
|
2120
2124
|
}
|
|
2121
2125
|
};
|
|
2122
2126
|
|
|
@@ -2191,7 +2195,7 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2191
2195
|
// (This may leave 1 or more characters in the buffer: like a CR
|
|
2192
2196
|
// waiting to see if the next char is LF, or for states that require
|
|
2193
2197
|
// lookahead...)
|
|
2194
|
-
function scanChars() {
|
|
2198
|
+
function scanChars(shouldPauseFunc) {
|
|
2195
2199
|
var codepoint, s, pattern, eof;
|
|
2196
2200
|
|
|
2197
2201
|
while(nextchar < numchars) {
|
|
@@ -2199,8 +2203,8 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2199
2203
|
// If we just tokenized a </script> tag, then the paused flag
|
|
2200
2204
|
// may have been set to tell us to stop tokenizing while
|
|
2201
2205
|
// the script is loading
|
|
2202
|
-
if (paused > 0) {
|
|
2203
|
-
return;
|
|
2206
|
+
if (paused > 0 || (shouldPauseFunc && shouldPauseFunc())) {
|
|
2207
|
+
return true;
|
|
2204
2208
|
}
|
|
2205
2209
|
|
|
2206
2210
|
|
|
@@ -2274,7 +2278,7 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2274
2278
|
}
|
|
2275
2279
|
else {
|
|
2276
2280
|
// Return now and wait for more chars later
|
|
2277
|
-
return;
|
|
2281
|
+
return true;
|
|
2278
2282
|
}
|
|
2279
2283
|
}
|
|
2280
2284
|
tokenizer(codepoint, s, eof);
|
|
@@ -2291,7 +2295,7 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2291
2295
|
}
|
|
2292
2296
|
else { // No match
|
|
2293
2297
|
// If more characters coming, wait for them
|
|
2294
|
-
if (!input_complete) return;
|
|
2298
|
+
if (!input_complete) return true;
|
|
2295
2299
|
|
|
2296
2300
|
// Otherwise, we've got to return what we've got
|
|
2297
2301
|
s = chars.substring(nextchar, numchars);
|
|
@@ -2307,6 +2311,7 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2307
2311
|
break;
|
|
2308
2312
|
}
|
|
2309
2313
|
}
|
|
2314
|
+
return false; // no more characters to scan!
|
|
2310
2315
|
}
|
|
2311
2316
|
|
|
2312
2317
|
|
|
@@ -2472,6 +2477,7 @@ function HTMLParser(address, fragmentContext, options) {
|
|
|
2472
2477
|
// A shortcut: look ahead and if this is a open or close tag
|
|
2473
2478
|
// in lowercase with no spaces and no attributes, just emit it now.
|
|
2474
2479
|
function emitSimpleTag() {
|
|
2480
|
+
if (nextchar === numchars) { return false; /* not even 1 char left */ }
|
|
2475
2481
|
SIMPLETAG.lastIndex = nextchar;
|
|
2476
2482
|
var matched = SIMPLETAG.exec(chars);
|
|
2477
2483
|
if (!matched) throw new Error("should never happen");
|
package/lib/Node.js
CHANGED
|
@@ -3,8 +3,8 @@ module.exports = Node;
|
|
|
3
3
|
|
|
4
4
|
var EventTarget = require('./EventTarget');
|
|
5
5
|
var LinkedList = require('./LinkedList');
|
|
6
|
+
var NodeUtils = require('./NodeUtils');
|
|
6
7
|
var utils = require('./utils');
|
|
7
|
-
var NAMESPACE = utils.NAMESPACE;
|
|
8
8
|
|
|
9
9
|
// All nodes have a nodeType and an ownerDocument.
|
|
10
10
|
// Once inserted, they also have a parentNode.
|
|
@@ -38,45 +38,6 @@ var DOCUMENT_POSITION_CONTAINS = Node.DOCUMENT_POSITION_CONTAINS
|
|
|
38
38
|
var DOCUMENT_POSITION_CONTAINED_BY = Node.DOCUMENT_POSITION_CONTAINED_BY = 0x10;
|
|
39
39
|
var DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
|
|
40
40
|
|
|
41
|
-
var hasRawContent = {
|
|
42
|
-
STYLE: true,
|
|
43
|
-
SCRIPT: true,
|
|
44
|
-
XMP: true,
|
|
45
|
-
IFRAME: true,
|
|
46
|
-
NOEMBED: true,
|
|
47
|
-
NOFRAMES: true,
|
|
48
|
-
PLAINTEXT: true
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
var emptyElements = {
|
|
52
|
-
area: true,
|
|
53
|
-
base: true,
|
|
54
|
-
basefont: true,
|
|
55
|
-
bgsound: true,
|
|
56
|
-
br: true,
|
|
57
|
-
col: true,
|
|
58
|
-
embed: true,
|
|
59
|
-
frame: true,
|
|
60
|
-
hr: true,
|
|
61
|
-
img: true,
|
|
62
|
-
input: true,
|
|
63
|
-
keygen: true,
|
|
64
|
-
link: true,
|
|
65
|
-
meta: true,
|
|
66
|
-
param: true,
|
|
67
|
-
source: true,
|
|
68
|
-
track: true,
|
|
69
|
-
wbr: true
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
var extraNewLine = {
|
|
73
|
-
/* Removed in https://github.com/whatwg/html/issues/944
|
|
74
|
-
pre: true,
|
|
75
|
-
textarea: true,
|
|
76
|
-
listing: true
|
|
77
|
-
*/
|
|
78
|
-
};
|
|
79
|
-
|
|
80
41
|
Node.prototype = Object.create(EventTarget.prototype, {
|
|
81
42
|
|
|
82
43
|
// Node that are not inserted into the tree inherit a null parent
|
|
@@ -729,78 +690,18 @@ Node.prototype = Object.create(EventTarget.prototype, {
|
|
|
729
690
|
// This is used by the innerHTML getter
|
|
730
691
|
// The serialization spec is at:
|
|
731
692
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments
|
|
693
|
+
//
|
|
694
|
+
// The serialization logic is intentionally implemented in a separate
|
|
695
|
+
// `NodeUtils` helper instead of the more obvious choice of a private
|
|
696
|
+
// `_serializeOne()` method on the `Node.prototype` in order to avoid
|
|
697
|
+
// the megamorphic `this._serializeOne` property access, which reduces
|
|
698
|
+
// performance unnecessarily. If you need specialized behavior for a
|
|
699
|
+
// certain subclass, you'll need to implement that in `NodeUtils`.
|
|
700
|
+
// See https://github.com/fgnass/domino/pull/142 for more information.
|
|
732
701
|
serialize: { value: function() {
|
|
733
702
|
var s = '';
|
|
734
703
|
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
|
|
735
|
-
s +=
|
|
736
|
-
}
|
|
737
|
-
return s;
|
|
738
|
-
}},
|
|
739
|
-
_serializeOne: { value: function(parent) {
|
|
740
|
-
var kid = this, s = '';
|
|
741
|
-
switch(kid.nodeType) {
|
|
742
|
-
case 1: //ELEMENT_NODE
|
|
743
|
-
var ns = kid.namespaceURI;
|
|
744
|
-
var html = ns === NAMESPACE.HTML;
|
|
745
|
-
var tagname = (html || ns === NAMESPACE.SVG || ns === NAMESPACE.MATHML) ? kid.localName : kid.tagName;
|
|
746
|
-
|
|
747
|
-
s += '<' + tagname;
|
|
748
|
-
|
|
749
|
-
for(var j = 0, k = kid._numattrs; j < k; j++) {
|
|
750
|
-
var a = kid._attr(j);
|
|
751
|
-
s += ' ' + attrname(a);
|
|
752
|
-
if (a.value !== undefined) s += '="' + escapeAttr(a.value) + '"';
|
|
753
|
-
}
|
|
754
|
-
s += '>';
|
|
755
|
-
|
|
756
|
-
if (!(html && emptyElements[tagname])) {
|
|
757
|
-
var ss = kid.serialize();
|
|
758
|
-
if (html && extraNewLine[tagname] && ss.charAt(0)==='\n') s += '\n';
|
|
759
|
-
// Serialize children and add end tag for all others
|
|
760
|
-
s += ss;
|
|
761
|
-
s += '</' + tagname + '>';
|
|
762
|
-
}
|
|
763
|
-
break;
|
|
764
|
-
case 3: //TEXT_NODE
|
|
765
|
-
case 4: //CDATA_SECTION_NODE
|
|
766
|
-
var parenttag;
|
|
767
|
-
if (parent.nodeType === ELEMENT_NODE &&
|
|
768
|
-
parent.namespaceURI === NAMESPACE.HTML)
|
|
769
|
-
parenttag = parent.tagName;
|
|
770
|
-
else
|
|
771
|
-
parenttag = '';
|
|
772
|
-
|
|
773
|
-
if (hasRawContent[parenttag] ||
|
|
774
|
-
(parenttag==='NOSCRIPT' && parent.ownerDocument._scripting_enabled)) {
|
|
775
|
-
s += kid.data;
|
|
776
|
-
} else {
|
|
777
|
-
s += escape(kid.data);
|
|
778
|
-
}
|
|
779
|
-
break;
|
|
780
|
-
case 8: //COMMENT_NODE
|
|
781
|
-
s += '<!--' + kid.data + '-->';
|
|
782
|
-
break;
|
|
783
|
-
case 7: //PROCESSING_INSTRUCTION_NODE
|
|
784
|
-
s += '<?' + kid.target + ' ' + kid.data + '?>';
|
|
785
|
-
break;
|
|
786
|
-
case 10: //DOCUMENT_TYPE_NODE
|
|
787
|
-
s += '<!DOCTYPE ' + kid.name;
|
|
788
|
-
|
|
789
|
-
if (false) {
|
|
790
|
-
// Latest HTML serialization spec omits the public/system ID
|
|
791
|
-
if (kid.publicID) {
|
|
792
|
-
s += ' PUBLIC "' + kid.publicId + '"';
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
if (kid.systemId) {
|
|
796
|
-
s += ' "' + kid.systemId + '"';
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
s += '>';
|
|
801
|
-
break;
|
|
802
|
-
default:
|
|
803
|
-
utils.InvalidState();
|
|
704
|
+
s += NodeUtils.serializeOne(kid, this);
|
|
804
705
|
}
|
|
805
706
|
return s;
|
|
806
707
|
}},
|
|
@@ -808,7 +709,7 @@ Node.prototype = Object.create(EventTarget.prototype, {
|
|
|
808
709
|
// Non-standard, but often useful for debugging.
|
|
809
710
|
outerHTML: {
|
|
810
711
|
get: function() {
|
|
811
|
-
return
|
|
712
|
+
return NodeUtils.serializeOne(this, { nodeType: 0 });
|
|
812
713
|
},
|
|
813
714
|
set: utils.nyi,
|
|
814
715
|
},
|
|
@@ -835,46 +736,3 @@ Node.prototype = Object.create(EventTarget.prototype, {
|
|
|
835
736
|
DOCUMENT_POSITION_CONTAINED_BY: { value: DOCUMENT_POSITION_CONTAINED_BY },
|
|
836
737
|
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: { value: DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC },
|
|
837
738
|
});
|
|
838
|
-
|
|
839
|
-
function escape(s) {
|
|
840
|
-
return s.replace(/[&<>\u00A0]/g, function(c) {
|
|
841
|
-
switch(c) {
|
|
842
|
-
case '&': return '&';
|
|
843
|
-
case '<': return '<';
|
|
844
|
-
case '>': return '>';
|
|
845
|
-
case '\u00A0': return ' ';
|
|
846
|
-
}
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
function escapeAttr(s) {
|
|
851
|
-
var toEscape = /[&"\u00A0]/g;
|
|
852
|
-
if (!toEscape.test(s)) {
|
|
853
|
-
// nothing to do, fast path
|
|
854
|
-
return s;
|
|
855
|
-
} else {
|
|
856
|
-
return s.replace(toEscape, function(c) {
|
|
857
|
-
switch(c) {
|
|
858
|
-
case '&': return '&';
|
|
859
|
-
case '"': return '"';
|
|
860
|
-
case '\u00A0': return ' ';
|
|
861
|
-
}
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
function attrname(a) {
|
|
867
|
-
var ns = a.namespaceURI;
|
|
868
|
-
if (!ns)
|
|
869
|
-
return a.localName;
|
|
870
|
-
if (ns === NAMESPACE.XML)
|
|
871
|
-
return 'xml:' + a.localName;
|
|
872
|
-
if (ns === NAMESPACE.XLINK)
|
|
873
|
-
return 'xlink:' + a.localName;
|
|
874
|
-
|
|
875
|
-
if (ns === NAMESPACE.XMLNS) {
|
|
876
|
-
if (a.localName === 'xmlns') return 'xmlns';
|
|
877
|
-
else return 'xmlns:' + a.localName;
|
|
878
|
-
}
|
|
879
|
-
return a.name;
|
|
880
|
-
}
|
package/lib/NodeUtils.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
module.exports = {
|
|
3
|
+
// NOTE: The `serializeOne()` function used to live on the `Node.prototype`
|
|
4
|
+
// as a private method `Node#_serializeOne(child)`, however that requires
|
|
5
|
+
// a megamorphic property access `this._serializeOne` just to get to the
|
|
6
|
+
// method, and this is being done on lots of different `Node` subclasses,
|
|
7
|
+
// which puts a lot of pressure on V8's megamorphic stub cache. So by
|
|
8
|
+
// moving the helper off of the `Node.prototype` and into a separate
|
|
9
|
+
// function in this helper module, we get a monomorphic property access
|
|
10
|
+
// `NodeUtils.serializeOne` to get to the function and reduce pressure
|
|
11
|
+
// on the megamorphic stub cache.
|
|
12
|
+
// See https://github.com/fgnass/domino/pull/142 for more information.
|
|
13
|
+
serializeOne: serializeOne
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
var utils = require('./utils');
|
|
17
|
+
var NAMESPACE = utils.NAMESPACE;
|
|
18
|
+
|
|
19
|
+
var hasRawContent = {
|
|
20
|
+
STYLE: true,
|
|
21
|
+
SCRIPT: true,
|
|
22
|
+
XMP: true,
|
|
23
|
+
IFRAME: true,
|
|
24
|
+
NOEMBED: true,
|
|
25
|
+
NOFRAMES: true,
|
|
26
|
+
PLAINTEXT: true
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
var emptyElements = {
|
|
30
|
+
area: true,
|
|
31
|
+
base: true,
|
|
32
|
+
basefont: true,
|
|
33
|
+
bgsound: true,
|
|
34
|
+
br: true,
|
|
35
|
+
col: true,
|
|
36
|
+
embed: true,
|
|
37
|
+
frame: true,
|
|
38
|
+
hr: true,
|
|
39
|
+
img: true,
|
|
40
|
+
input: true,
|
|
41
|
+
keygen: true,
|
|
42
|
+
link: true,
|
|
43
|
+
meta: true,
|
|
44
|
+
param: true,
|
|
45
|
+
source: true,
|
|
46
|
+
track: true,
|
|
47
|
+
wbr: true
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
var extraNewLine = {
|
|
51
|
+
/* Removed in https://github.com/whatwg/html/issues/944
|
|
52
|
+
pre: true,
|
|
53
|
+
textarea: true,
|
|
54
|
+
listing: true
|
|
55
|
+
*/
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function escape(s) {
|
|
59
|
+
return s.replace(/[&<>\u00A0]/g, function(c) {
|
|
60
|
+
switch(c) {
|
|
61
|
+
case '&': return '&';
|
|
62
|
+
case '<': return '<';
|
|
63
|
+
case '>': return '>';
|
|
64
|
+
case '\u00A0': return ' ';
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function escapeAttr(s) {
|
|
70
|
+
var toEscape = /[&"\u00A0]/g;
|
|
71
|
+
if (!toEscape.test(s)) {
|
|
72
|
+
// nothing to do, fast path
|
|
73
|
+
return s;
|
|
74
|
+
} else {
|
|
75
|
+
return s.replace(toEscape, function(c) {
|
|
76
|
+
switch(c) {
|
|
77
|
+
case '&': return '&';
|
|
78
|
+
case '"': return '"';
|
|
79
|
+
case '\u00A0': return ' ';
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function attrname(a) {
|
|
86
|
+
var ns = a.namespaceURI;
|
|
87
|
+
if (!ns)
|
|
88
|
+
return a.localName;
|
|
89
|
+
if (ns === NAMESPACE.XML)
|
|
90
|
+
return 'xml:' + a.localName;
|
|
91
|
+
if (ns === NAMESPACE.XLINK)
|
|
92
|
+
return 'xlink:' + a.localName;
|
|
93
|
+
|
|
94
|
+
if (ns === NAMESPACE.XMLNS) {
|
|
95
|
+
if (a.localName === 'xmlns') return 'xmlns';
|
|
96
|
+
else return 'xmlns:' + a.localName;
|
|
97
|
+
}
|
|
98
|
+
return a.name;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function serializeOne(kid, parent) {
|
|
102
|
+
var s = '';
|
|
103
|
+
switch(kid.nodeType) {
|
|
104
|
+
case 1: //ELEMENT_NODE
|
|
105
|
+
var ns = kid.namespaceURI;
|
|
106
|
+
var html = ns === NAMESPACE.HTML;
|
|
107
|
+
var tagname = (html || ns === NAMESPACE.SVG || ns === NAMESPACE.MATHML) ? kid.localName : kid.tagName;
|
|
108
|
+
|
|
109
|
+
s += '<' + tagname;
|
|
110
|
+
|
|
111
|
+
for(var j = 0, k = kid._numattrs; j < k; j++) {
|
|
112
|
+
var a = kid._attr(j);
|
|
113
|
+
s += ' ' + attrname(a);
|
|
114
|
+
if (a.value !== undefined) s += '="' + escapeAttr(a.value) + '"';
|
|
115
|
+
}
|
|
116
|
+
s += '>';
|
|
117
|
+
|
|
118
|
+
if (!(html && emptyElements[tagname])) {
|
|
119
|
+
var ss = kid.serialize();
|
|
120
|
+
if (html && extraNewLine[tagname] && ss.charAt(0)==='\n') s += '\n';
|
|
121
|
+
// Serialize children and add end tag for all others
|
|
122
|
+
s += ss;
|
|
123
|
+
s += '</' + tagname + '>';
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
case 3: //TEXT_NODE
|
|
127
|
+
case 4: //CDATA_SECTION_NODE
|
|
128
|
+
var parenttag;
|
|
129
|
+
if (parent.nodeType === 1 /*ELEMENT_NODE*/ &&
|
|
130
|
+
parent.namespaceURI === NAMESPACE.HTML)
|
|
131
|
+
parenttag = parent.tagName;
|
|
132
|
+
else
|
|
133
|
+
parenttag = '';
|
|
134
|
+
|
|
135
|
+
if (hasRawContent[parenttag] ||
|
|
136
|
+
(parenttag==='NOSCRIPT' && parent.ownerDocument._scripting_enabled)) {
|
|
137
|
+
s += kid.data;
|
|
138
|
+
} else {
|
|
139
|
+
s += escape(kid.data);
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
case 8: //COMMENT_NODE
|
|
143
|
+
s += '<!--' + kid.data + '-->';
|
|
144
|
+
break;
|
|
145
|
+
case 7: //PROCESSING_INSTRUCTION_NODE
|
|
146
|
+
s += '<?' + kid.target + ' ' + kid.data + '?>';
|
|
147
|
+
break;
|
|
148
|
+
case 10: //DOCUMENT_TYPE_NODE
|
|
149
|
+
s += '<!DOCTYPE ' + kid.name;
|
|
150
|
+
|
|
151
|
+
if (false) {
|
|
152
|
+
// Latest HTML serialization spec omits the public/system ID
|
|
153
|
+
if (kid.publicID) {
|
|
154
|
+
s += ' PUBLIC "' + kid.publicId + '"';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (kid.systemId) {
|
|
158
|
+
s += ' "' + kid.systemId + '"';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
s += '>';
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
utils.InvalidStateError();
|
|
166
|
+
}
|
|
167
|
+
return s;
|
|
168
|
+
}
|
package/lib/defineElement.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var attributes = require('./attributes');
|
|
4
4
|
var sloppy = require('./sloppy');
|
|
5
|
+
var isApiWritable = require("./config").isApiWritable;
|
|
5
6
|
|
|
6
7
|
module.exports = function(spec, defaultConstructor, tagList, tagNameToImpl) {
|
|
7
8
|
var c = spec.ctor;
|
|
@@ -17,7 +18,7 @@ module.exports = function(spec, defaultConstructor, tagList, tagNameToImpl) {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
props.constructor = { value : c };
|
|
21
|
+
props.constructor = { value : c, writable: isApiWritable };
|
|
21
22
|
c.prototype = Object.create((spec.superclass || defaultConstructor).prototype, props);
|
|
22
23
|
if (spec.events) {
|
|
23
24
|
addEventHandlers(c, spec.events);
|
package/lib/htmlelts.js
CHANGED
|
@@ -830,13 +830,24 @@ define({
|
|
|
830
830
|
if (p.localName === 'select') return p.form;
|
|
831
831
|
p = p.parentNode;
|
|
832
832
|
}
|
|
833
|
-
}}
|
|
833
|
+
}},
|
|
834
|
+
value: {
|
|
835
|
+
get: function() { return this._getattr('value') || this.text; },
|
|
836
|
+
set: function(v) { this._setattr('value', v); },
|
|
837
|
+
},
|
|
838
|
+
text: {
|
|
839
|
+
get: function() {
|
|
840
|
+
// Strip and collapse whitespace
|
|
841
|
+
return this.textContent.replace(/[ \t\n\f\r]+/g, ' ').trim();
|
|
842
|
+
},
|
|
843
|
+
set: function(v) { this.textContent = v; },
|
|
844
|
+
},
|
|
845
|
+
// missing: index
|
|
834
846
|
},
|
|
835
847
|
attributes: {
|
|
836
848
|
disabled: Boolean,
|
|
837
849
|
defaultSelected: {name: 'selected', type: Boolean},
|
|
838
850
|
label: String,
|
|
839
|
-
value: String,
|
|
840
851
|
}
|
|
841
852
|
});
|
|
842
853
|
|
package/lib/index.js
CHANGED
|
@@ -19,6 +19,57 @@ exports.createDocument = function(html, force) {
|
|
|
19
19
|
return new DOMImplementation(null).createHTMLDocument("");
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
exports.createIncrementalHTMLParser = function() {
|
|
23
|
+
var parser = new HTMLParser();
|
|
24
|
+
/** API for incremental parser. */
|
|
25
|
+
return {
|
|
26
|
+
/** Provide an additional chunk of text to be parsed. */
|
|
27
|
+
write: function(s) {
|
|
28
|
+
if (s.length > 0) {
|
|
29
|
+
parser.parse(s, false, function() { return true; });
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Signal that we are done providing input text, optionally
|
|
34
|
+
* providing one last chunk as a parameter.
|
|
35
|
+
*/
|
|
36
|
+
end: function(s) {
|
|
37
|
+
parser.parse(s || '', true, function() { return true; });
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* Performs a chunk of parsing work, returning at the end of
|
|
41
|
+
* the next token as soon as shouldPauseFunc() returns true.
|
|
42
|
+
* Returns true iff there is more work to do.
|
|
43
|
+
*
|
|
44
|
+
* For example:
|
|
45
|
+
* ```
|
|
46
|
+
* var incrParser = domino.createIncrementalHTMLParser();
|
|
47
|
+
* incrParser.end('...long html document...');
|
|
48
|
+
* while (true) {
|
|
49
|
+
* // Pause every 10ms
|
|
50
|
+
* var start = Date.now();
|
|
51
|
+
* var pauseIn10 = function() { return (Date.now() - start) >= 10; };
|
|
52
|
+
* if (!incrParser.process(pauseIn10)) {
|
|
53
|
+
* break;
|
|
54
|
+
* }
|
|
55
|
+
* ...yield to other tasks, do other housekeeping, etc...
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
process: function(shouldPauseFunc) {
|
|
60
|
+
return parser.parse('', false, shouldPauseFunc);
|
|
61
|
+
},
|
|
62
|
+
/**
|
|
63
|
+
* Returns the result of the incremental parse. Valid after
|
|
64
|
+
* `this.end()` has been called and `this.process()` has returned
|
|
65
|
+
* false.
|
|
66
|
+
*/
|
|
67
|
+
document: function() {
|
|
68
|
+
return parser.document();
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
22
73
|
exports.createWindow = function(html, address) {
|
|
23
74
|
var document = exports.createDocument(html);
|
|
24
75
|
if (address !== undefined) { document._address = address; }
|
package/lib/select.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Zest (https://github.com/chjj/zest)
|
|
5
5
|
* A css selector engine.
|
|
6
6
|
* Copyright (c) 2011-2012, Christopher Jeffrey. (MIT Licensed)
|
|
7
|
+
* Domino version based on Zest v0.1.3 with bugfixes applied.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -55,6 +56,13 @@ var lastChild = function(el) {
|
|
|
55
56
|
return el;
|
|
56
57
|
};
|
|
57
58
|
|
|
59
|
+
var parentIsElement = function(n) {
|
|
60
|
+
if (!n.parentNode) { return false; }
|
|
61
|
+
var nodeType = n.parentNode.nodeType;
|
|
62
|
+
// The root `html` element can be a first- or last-child, too.
|
|
63
|
+
return nodeType === 1 || nodeType === 9;
|
|
64
|
+
};
|
|
65
|
+
|
|
58
66
|
var unquote = function(str) {
|
|
59
67
|
if (!str) return str;
|
|
60
68
|
var ch = str[0];
|
|
@@ -164,7 +172,7 @@ var nth = function(param_, test, last) {
|
|
|
164
172
|
, advance = !last ? next : prev;
|
|
165
173
|
|
|
166
174
|
return function(el) {
|
|
167
|
-
if (el
|
|
175
|
+
if (!parentIsElement(el)) return;
|
|
168
176
|
|
|
169
177
|
var rel = find(el.parentNode)
|
|
170
178
|
, pos = 0;
|
|
@@ -263,14 +271,13 @@ var selectors = {
|
|
|
263
271
|
};
|
|
264
272
|
},
|
|
265
273
|
':first-child': function(el) {
|
|
266
|
-
return !prev(el) && el
|
|
274
|
+
return !prev(el) && parentIsElement(el);
|
|
267
275
|
},
|
|
268
276
|
':last-child': function(el) {
|
|
269
|
-
return !next(el) && el
|
|
277
|
+
return !next(el) && parentIsElement(el);
|
|
270
278
|
},
|
|
271
279
|
':only-child': function(el) {
|
|
272
|
-
return !prev(el) && !next(el)
|
|
273
|
-
&& el.parentNode.nodeType === 1;
|
|
280
|
+
return !prev(el) && !next(el) && parentIsElement(el);
|
|
274
281
|
},
|
|
275
282
|
':nth-child': function(param, last) {
|
|
276
283
|
return nth(param, function() {
|
|
@@ -293,7 +300,7 @@ var selectors = {
|
|
|
293
300
|
};
|
|
294
301
|
},
|
|
295
302
|
':first-of-type': function(el) {
|
|
296
|
-
if (el
|
|
303
|
+
if (!parentIsElement(el)) return;
|
|
297
304
|
var type = el.nodeName;
|
|
298
305
|
/*jshint -W084 */
|
|
299
306
|
while (el = prev(el)) {
|
|
@@ -302,7 +309,7 @@ var selectors = {
|
|
|
302
309
|
return true;
|
|
303
310
|
},
|
|
304
311
|
':last-of-type': function(el) {
|
|
305
|
-
if (el
|
|
312
|
+
if (!parentIsElement(el)) return;
|
|
306
313
|
var type = el.nodeName;
|
|
307
314
|
/*jshint -W084 */
|
|
308
315
|
while (el = next(el)) {
|
|
@@ -340,9 +347,14 @@ var selectors = {
|
|
|
340
347
|
':focus': function(el) {
|
|
341
348
|
return el === el.ownerDocument.activeElement;
|
|
342
349
|
},
|
|
343
|
-
':
|
|
350
|
+
':is': function(sel) {
|
|
344
351
|
return compileGroup(sel);
|
|
345
352
|
},
|
|
353
|
+
// :matches is an older name for :is; see
|
|
354
|
+
// https://github.com/w3c/csswg-drafts/issues/3258
|
|
355
|
+
':matches': function(sel) {
|
|
356
|
+
return selectors[':is'](sel);
|
|
357
|
+
},
|
|
346
358
|
':nth-match': function(param, last) {
|
|
347
359
|
var args = param.split(/\s*,\s*/)
|
|
348
360
|
, arg = args.shift()
|
|
@@ -519,7 +531,8 @@ var operators = {
|
|
|
519
531
|
return attr.indexOf(val) === 0;
|
|
520
532
|
},
|
|
521
533
|
'$=': function(attr, val) {
|
|
522
|
-
|
|
534
|
+
var i = attr.lastIndexOf(val);
|
|
535
|
+
return i !== -1 && i + val.length === attr.length;
|
|
523
536
|
},
|
|
524
537
|
// non-standard
|
|
525
538
|
'!=': function(attr, val) {
|
|
@@ -656,7 +669,7 @@ var compile = function(sel_) {
|
|
|
656
669
|
while (sel) {
|
|
657
670
|
if (cap = rules.qname.exec(sel)) {
|
|
658
671
|
sel = sel.substring(cap[0].length);
|
|
659
|
-
qname = cap[1];
|
|
672
|
+
qname = decodeid(cap[1]);
|
|
660
673
|
buff.push(tok(qname, true));
|
|
661
674
|
} else if (cap = rules.simple.exec(sel)) {
|
|
662
675
|
sel = sel.substring(cap[0].length);
|
|
@@ -731,7 +744,7 @@ var tok = function(cap, qname) {
|
|
|
731
744
|
if (qname) {
|
|
732
745
|
return cap === '*'
|
|
733
746
|
? selectors['*']
|
|
734
|
-
: selectors.type(
|
|
747
|
+
: selectors.type(cap);
|
|
735
748
|
}
|
|
736
749
|
|
|
737
750
|
// class/id
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domino",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"license": "BSD-2-Clause",
|
|
5
5
|
"author": "Felix Gnass <fgnass@gmail.com>",
|
|
6
6
|
"description": "Server-side DOM implementation based on Mozilla's dom.js",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
},
|
|
23
23
|
"types": "lib/index.d.ts",
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"jquery": "^3.
|
|
26
|
-
"jshint": "^2.
|
|
27
|
-
"mocha": "^
|
|
28
|
-
"should": "^13.2.
|
|
25
|
+
"jquery": "^3.4.1",
|
|
26
|
+
"jshint": "^2.10.1",
|
|
27
|
+
"mocha": "^5.2.0",
|
|
28
|
+
"should": "^13.2.3"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/test/domino.js
CHANGED
|
@@ -16,6 +16,41 @@ exports.matches = function() {
|
|
|
16
16
|
h1.matches('h2,h1').should.equal(true);
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
exports.closest = function() {
|
|
20
|
+
// see https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
|
|
21
|
+
var html =
|
|
22
|
+
'<article>\n' +
|
|
23
|
+
' <div id="div-01">Here is div-01\n' +
|
|
24
|
+
' <div id="div-02">Here is div-02\n' +
|
|
25
|
+
' <div id="div-03">Here is div-03</div>\n' +
|
|
26
|
+
' </div>\n' +
|
|
27
|
+
' </div>\n' +
|
|
28
|
+
'</article>';
|
|
29
|
+
var document = domino.createWindow(html).document;
|
|
30
|
+
var el = document.getElementById('div-03');
|
|
31
|
+
var r1 = el.closest('#div-02');
|
|
32
|
+
r1.id.should.equal('div-02');
|
|
33
|
+
var r2 = el.closest('div div');
|
|
34
|
+
r2.id.should.equal('div-03');
|
|
35
|
+
var r3 = el.closest('article > div');
|
|
36
|
+
r3.id.should.equal('div-01');
|
|
37
|
+
var r4 = el.closest(':not(div)');
|
|
38
|
+
r4.tagName.should.equal('ARTICLE');
|
|
39
|
+
var r5 = el.closest('#nothere');
|
|
40
|
+
(r5 === null).should.be.true();
|
|
41
|
+
// GH #154
|
|
42
|
+
var disconnected = document.createElement('div');
|
|
43
|
+
var r6 = disconnected.closest('#nothere');
|
|
44
|
+
(r6 === null).should.be.true();
|
|
45
|
+
var fragment = document.createDocumentFragment();
|
|
46
|
+
fragment.appendChild(disconnected);
|
|
47
|
+
var r7 = disconnected.closest('#nothere');
|
|
48
|
+
(r7 === null).should.be.true();
|
|
49
|
+
var r8 = disconnected.closest(':not(div)');
|
|
50
|
+
// This should not return DocumentFragment! Always should return an Element.
|
|
51
|
+
(r8 === null).should.be.true();
|
|
52
|
+
};
|
|
53
|
+
|
|
19
54
|
exports.querySelectorAll = function() {
|
|
20
55
|
var window = domino.createWindow(html);
|
|
21
56
|
var d = window.document;
|
|
@@ -45,6 +80,39 @@ exports.orphanQSA = function() {
|
|
|
45
80
|
p.querySelectorAll('p').should.have.length(0);
|
|
46
81
|
};
|
|
47
82
|
|
|
83
|
+
// This is https://github.com/chjj/zest/issues/22
|
|
84
|
+
exports.tildeQSA = function() {
|
|
85
|
+
var document = domino.createDocument('<div class="foo-bar foo">');
|
|
86
|
+
var els = document.querySelectorAll('.foo');
|
|
87
|
+
els.should.have.length(1);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
exports.rootQSA = function() {
|
|
91
|
+
// the root element should be both first-child and last-child
|
|
92
|
+
var document = domino.createDocument('foo');
|
|
93
|
+
var html = document.documentElement;
|
|
94
|
+
html.matches(':first-child').should.be.true();
|
|
95
|
+
html.matches(':last-child').should.be.true();
|
|
96
|
+
html.matches(':only-child').should.be.true();
|
|
97
|
+
html.matches(':first-of-type').should.be.true();
|
|
98
|
+
html.matches(':last-of-type').should.be.true();
|
|
99
|
+
html.matches(':nth-child(1)').should.be.true();
|
|
100
|
+
html.matches(':nth-child(2)').should.be.false();
|
|
101
|
+
html.matches(':nth-last-child(1)').should.be.true();
|
|
102
|
+
html.matches(':nth-last-child(2)').should.be.false();
|
|
103
|
+
html.matches(':nth-of-type(1)').should.be.true();
|
|
104
|
+
html.matches(':nth-of-type(2)').should.be.false();
|
|
105
|
+
html.matches(':nth-last-of-type(1)').should.be.true();
|
|
106
|
+
html.matches(':nth-last-of-type(2)').should.be.false();
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
exports.escapeQSA = function() {
|
|
110
|
+
var document = domino.createDocument('<p>foo');
|
|
111
|
+
// ensure that selector parsing can handle escaped characters
|
|
112
|
+
document.querySelectorAll('p\\22\\27p').should.have.length(0);
|
|
113
|
+
document.querySelectorAll('\\50').should.have.length(1);
|
|
114
|
+
};
|
|
115
|
+
|
|
48
116
|
exports.gh20 = function() {
|
|
49
117
|
var window = domino.createWindow('');
|
|
50
118
|
var frag = window.document.createDocumentFragment();
|
|
@@ -1180,6 +1248,27 @@ exports.gh129 = function() {
|
|
|
1180
1248
|
(span.parentNode === null).should.equal(true);
|
|
1181
1249
|
};
|
|
1182
1250
|
|
|
1251
|
+
exports.gh135 = function() {
|
|
1252
|
+
var document = domino.createDocument('<a target="foobar"></a>');
|
|
1253
|
+
var a = document.querySelectorAll('*[target$="aaaaaaa"]');
|
|
1254
|
+
a.length.should.equal(0);
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
exports.gh136 = function() {
|
|
1258
|
+
var document = domino.createDocument('<option> \f\tabc\n');
|
|
1259
|
+
var o = document.querySelector('option');
|
|
1260
|
+
o.value.should.equal('abc');
|
|
1261
|
+
o.text.should.equal('abc');
|
|
1262
|
+
o.textContent.should.equal(' \f\tabc\n');
|
|
1263
|
+
o.value = ' def ';
|
|
1264
|
+
o.value.should.equal(' def ');
|
|
1265
|
+
o.text.should.equal('abc');
|
|
1266
|
+
o.text = ' ghi ';
|
|
1267
|
+
o.text.should.equal('ghi');
|
|
1268
|
+
o.textContent.should.equal(' ghi ');
|
|
1269
|
+
o.outerHTML.should.equal("<option value=\" def \"> ghi </option>");
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1183
1272
|
// Examples from
|
|
1184
1273
|
// https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML
|
|
1185
1274
|
exports.outerHTML1 = function() {
|
|
@@ -1271,3 +1360,76 @@ exports.insertForeignElement = function() {
|
|
|
1271
1360
|
'</iframe></xsl:template>'
|
|
1272
1361
|
);
|
|
1273
1362
|
};
|
|
1363
|
+
|
|
1364
|
+
exports.bodyNL = function() {
|
|
1365
|
+
var document = domino.createDocument(
|
|
1366
|
+
'<body></body>\n'
|
|
1367
|
+
);
|
|
1368
|
+
// This may be surprising, but it's what the spec requires.
|
|
1369
|
+
document.body.textContent.should.equal('\n');
|
|
1370
|
+
};
|
|
1371
|
+
|
|
1372
|
+
exports.testIncompleteTag = function() {
|
|
1373
|
+
var document = domino.createDocument('<p>hello<');
|
|
1374
|
+
document.body.outerHTML.should.equal('<body><p>hello<</p></body>');
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1377
|
+
exports.incrementalHTMLParser1 = function() {
|
|
1378
|
+
// Verify correct operation of incremental parser, fed chunks which split
|
|
1379
|
+
// up tokens.
|
|
1380
|
+
var incrParser = domino.createIncrementalHTMLParser();
|
|
1381
|
+
var neverPause = function() { return false; };
|
|
1382
|
+
incrParser.write('<p>hello<');
|
|
1383
|
+
incrParser.process(neverPause);
|
|
1384
|
+
|
|
1385
|
+
incrParser.write('b>foo&am');
|
|
1386
|
+
incrParser.process(neverPause);
|
|
1387
|
+
|
|
1388
|
+
incrParser.write('p;<');
|
|
1389
|
+
incrParser.process(neverPause);
|
|
1390
|
+
|
|
1391
|
+
incrParser.write('/p>');
|
|
1392
|
+
incrParser.process(neverPause);
|
|
1393
|
+
|
|
1394
|
+
incrParser.end();
|
|
1395
|
+
// omitting the pauseFunc is equivalent to passing neverPause
|
|
1396
|
+
incrParser.process().should.equal(/*no more to do*/false);
|
|
1397
|
+
|
|
1398
|
+
incrParser.document().outerHTML.should.equal(
|
|
1399
|
+
'<html><head></head><body><p>hello<b>foo&</b></p></body></html>'
|
|
1400
|
+
);
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
exports.incrementalHTMLParser2 = function() {
|
|
1404
|
+
// Verify correct operation of incremental parser when we only manage to
|
|
1405
|
+
// scan one token at each step.
|
|
1406
|
+
var justOneStep = function() {
|
|
1407
|
+
var counter = 1;
|
|
1408
|
+
return function() { return (counter--) <= 0; };
|
|
1409
|
+
};
|
|
1410
|
+
var incrParser = domino.createIncrementalHTMLParser();
|
|
1411
|
+
incrParser.write('<p>hello<');
|
|
1412
|
+
incrParser.write('b>foo&am');
|
|
1413
|
+
incrParser.write('p;<');
|
|
1414
|
+
incrParser.write('/p>');
|
|
1415
|
+
incrParser.end();
|
|
1416
|
+
|
|
1417
|
+
[
|
|
1418
|
+
'<html><head></head><body><p></p></body></html>',
|
|
1419
|
+
'<html><head></head><body><p>hello</p></body></html>',
|
|
1420
|
+
'<html><head></head><body><p>hello<b></b></p></body></html>',
|
|
1421
|
+
'<html><head></head><body><p>hello<b>foo</b></p></body></html>',
|
|
1422
|
+
'<html><head></head><body><p>hello<b>foo</b></p></body></html>',
|
|
1423
|
+
'<html><head></head><body><p>hello<b>foo</b></p></body></html>',
|
|
1424
|
+
'<html><head></head><body><p>hello<b>foo</b></p></body></html>',
|
|
1425
|
+
'<html><head></head><body><p>hello<b>foo</b></p></body></html>',
|
|
1426
|
+
'<html><head></head><body><p>hello<b>foo&</b></p></body></html>',
|
|
1427
|
+
].forEach(function(step) {
|
|
1428
|
+
incrParser.process(justOneStep()).should.equal(/*more to do!*/true);
|
|
1429
|
+
incrParser.document().outerHTML.should.equal(step);
|
|
1430
|
+
});
|
|
1431
|
+
incrParser.process(justOneStep()).should.equal(/*no more to do*/false);
|
|
1432
|
+
incrParser.document().outerHTML.should.equal(
|
|
1433
|
+
'<html><head></head><body><p>hello<b>foo&</b></p></body></html>'
|
|
1434
|
+
);
|
|
1435
|
+
};
|
|
@@ -2277,10 +2277,12 @@
|
|
|
2277
2277
|
"SVG elements should have a .dataset"
|
|
2278
2278
|
],
|
|
2279
2279
|
"html/dom/elements/global-attributes/dir_auto-contained-script-L-ref.html": [
|
|
2280
|
-
"Uncaught: Unexpected token &"
|
|
2280
|
+
"Uncaught: Unexpected token &",
|
|
2281
|
+
"Uncaught: Unexpected token '&'"
|
|
2281
2282
|
],
|
|
2282
2283
|
"html/dom/elements/global-attributes/dir_auto-contained-script-L.html": [
|
|
2283
|
-
"Uncaught: Unexpected token &"
|
|
2284
|
+
"Uncaught: Unexpected token &",
|
|
2285
|
+
"Uncaught: Unexpected token '&'"
|
|
2284
2286
|
],
|
|
2285
2287
|
"html/dom/elements/global-attributes/id-attribute.html": [
|
|
2286
2288
|
"User agents must associate the element with an id value for purposes of CSS.",
|
|
@@ -2944,19 +2946,24 @@
|
|
|
2944
2946
|
"getElementsByTagNameNS() should be a live collection"
|
|
2945
2947
|
],
|
|
2946
2948
|
"dom/nodes/Element-childElement-null-xhtml.xhtml": [
|
|
2947
|
-
"Uncaught: Unexpected token <"
|
|
2949
|
+
"Uncaught: Unexpected token <",
|
|
2950
|
+
"Uncaught: Unexpected token '<'"
|
|
2948
2951
|
],
|
|
2949
2952
|
"dom/nodes/Element-childElementCount-dynamic-add-xhtml.xhtml": [
|
|
2950
|
-
"Uncaught: Unexpected token <"
|
|
2953
|
+
"Uncaught: Unexpected token <",
|
|
2954
|
+
"Uncaught: Unexpected token '<'"
|
|
2951
2955
|
],
|
|
2952
2956
|
"dom/nodes/Element-childElementCount-dynamic-remove-xhtml.xhtml": [
|
|
2953
|
-
"Uncaught: Unexpected token <"
|
|
2957
|
+
"Uncaught: Unexpected token <",
|
|
2958
|
+
"Uncaught: Unexpected token '<'"
|
|
2954
2959
|
],
|
|
2955
2960
|
"dom/nodes/Element-childElementCount-nochild-xhtml.xhtml": [
|
|
2956
|
-
"Uncaught: Unexpected token <"
|
|
2961
|
+
"Uncaught: Unexpected token <",
|
|
2962
|
+
"Uncaught: Unexpected token '<'"
|
|
2957
2963
|
],
|
|
2958
2964
|
"dom/nodes/Element-childElementCount-xhtml.xhtml": [
|
|
2959
|
-
"Uncaught: Unexpected token <"
|
|
2965
|
+
"Uncaught: Unexpected token <",
|
|
2966
|
+
"Uncaught: Unexpected token '<'"
|
|
2960
2967
|
],
|
|
2961
2968
|
"dom/nodes/Element-children.html": [
|
|
2962
2969
|
"HTMLCollection edge cases",
|
|
@@ -2991,13 +2998,16 @@
|
|
|
2991
2998
|
"Element.closest with context node 'test4' and selector ':has(> :scope)'"
|
|
2992
2999
|
],
|
|
2993
3000
|
"dom/nodes/Element-firstElementChild-entity-xhtml.xhtml": [
|
|
2994
|
-
"Uncaught: Unexpected token <"
|
|
3001
|
+
"Uncaught: Unexpected token <",
|
|
3002
|
+
"Uncaught: Unexpected token '<'"
|
|
2995
3003
|
],
|
|
2996
3004
|
"dom/nodes/Element-firstElementChild-namespace-xhtml.xhtml": [
|
|
2997
|
-
"Uncaught: Unexpected token <"
|
|
3005
|
+
"Uncaught: Unexpected token <",
|
|
3006
|
+
"Uncaught: Unexpected token '<'"
|
|
2998
3007
|
],
|
|
2999
3008
|
"dom/nodes/Element-firstElementChild-xhtml.xhtml": [
|
|
3000
|
-
"Uncaught: Unexpected token <"
|
|
3009
|
+
"Uncaught: Unexpected token <",
|
|
3010
|
+
"Uncaught: Unexpected token '<'"
|
|
3001
3011
|
],
|
|
3002
3012
|
"dom/nodes/Element-getElementsByClassName.html": [
|
|
3003
3013
|
"Interface should be correct.",
|
|
@@ -3022,19 +3032,23 @@
|
|
|
3022
3032
|
"getElementsByTagNameNS() should be a live collection"
|
|
3023
3033
|
],
|
|
3024
3034
|
"dom/nodes/Element-lastElementChild-xhtml.xhtml": [
|
|
3025
|
-
"Uncaught: Unexpected token <"
|
|
3035
|
+
"Uncaught: Unexpected token <",
|
|
3036
|
+
"Uncaught: Unexpected token '<'"
|
|
3026
3037
|
],
|
|
3027
3038
|
"dom/nodes/Element-matches.html": [
|
|
3028
3039
|
"Selectors-API Level 2 Test Suite: HTML with Selectors Level 3"
|
|
3029
3040
|
],
|
|
3030
3041
|
"dom/nodes/Element-nextElementSibling-xhtml.xhtml": [
|
|
3031
|
-
"Uncaught: Unexpected token <"
|
|
3042
|
+
"Uncaught: Unexpected token <",
|
|
3043
|
+
"Uncaught: Unexpected token '<'"
|
|
3032
3044
|
],
|
|
3033
3045
|
"dom/nodes/Element-previousElementSibling-xhtml.xhtml": [
|
|
3034
|
-
"Uncaught: Unexpected token <"
|
|
3046
|
+
"Uncaught: Unexpected token <",
|
|
3047
|
+
"Uncaught: Unexpected token '<'"
|
|
3035
3048
|
],
|
|
3036
3049
|
"dom/nodes/Element-siblingElement-null-xhtml.xhtml": [
|
|
3037
|
-
"Uncaught: Unexpected token <"
|
|
3050
|
+
"Uncaught: Unexpected token <",
|
|
3051
|
+
"Uncaught: Unexpected token '<'"
|
|
3038
3052
|
],
|
|
3039
3053
|
"dom/nodes/Element-webkitMatchesSelector.html": [
|
|
3040
3054
|
"Selectors-API Level 2 Test Suite: HTML with Selectors Level 3"
|
|
@@ -4424,7 +4438,8 @@
|
|
|
4424
4438
|
"DocumentFrgment.prepend() with one element and text as argument, on a parent having a child."
|
|
4425
4439
|
],
|
|
4426
4440
|
"dom/nodes/ProcessingInstruction-escapes-1.xhtml": [
|
|
4427
|
-
"Uncaught: Unexpected token <"
|
|
4441
|
+
"Uncaught: Unexpected token <",
|
|
4442
|
+
"Uncaught: Unexpected token '<'"
|
|
4428
4443
|
],
|
|
4429
4444
|
"dom/nodes/ProcessingInstruction-literal-1.xhtml": [
|
|
4430
4445
|
"<?xml?> is not a ProcessingInstruction"
|
|
@@ -4614,10 +4629,12 @@
|
|
|
4614
4629
|
"innerHTML in XHTML: getting while the document is in an invalid state 1"
|
|
4615
4630
|
],
|
|
4616
4631
|
"domparsing/innerhtml-03.xhtml": [
|
|
4617
|
-
"Uncaught: Unexpected token <"
|
|
4632
|
+
"Uncaught: Unexpected token <",
|
|
4633
|
+
"Uncaught: Unexpected token '<'"
|
|
4618
4634
|
],
|
|
4619
4635
|
"domparsing/innerhtml-05.xhtml": [
|
|
4620
|
-
"Uncaught: Unexpected token <"
|
|
4636
|
+
"Uncaught: Unexpected token <",
|
|
4637
|
+
"Uncaught: Unexpected token '<'"
|
|
4621
4638
|
],
|
|
4622
4639
|
"domparsing/innerhtml-mxss.sub.html": [
|
|
4623
4640
|
"href before setter: 1680",
|
|
@@ -4652,7 +4669,8 @@
|
|
|
4652
4669
|
"href after setter: 3000"
|
|
4653
4670
|
],
|
|
4654
4671
|
"domparsing/insert_adjacent_html-xhtml.xhtml": [
|
|
4655
|
-
"Uncaught: Unexpected token <"
|
|
4672
|
+
"Uncaught: Unexpected token <",
|
|
4673
|
+
"Uncaught: Unexpected token '<'"
|
|
4656
4674
|
],
|
|
4657
4675
|
"domparsing/style_attribute_html.html": [
|
|
4658
4676
|
"Parsing of initial style attribute",
|
|
@@ -4661,7 +4679,8 @@
|
|
|
4661
4679
|
"Update style.backgroundColor"
|
|
4662
4680
|
],
|
|
4663
4681
|
"domparsing/xml-serialization.xhtml": [
|
|
4664
|
-
"Uncaught: Unexpected token <"
|
|
4682
|
+
"Uncaught: Unexpected token <",
|
|
4683
|
+
"Uncaught: Unexpected token '<'"
|
|
4665
4684
|
],
|
|
4666
4685
|
"domparsing/xmldomparser.html": [
|
|
4667
4686
|
"XML Dom Parse readyState Test"
|