Initial commit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
||||
[submodule "html5lib-tests"]
|
||||
path = test/html5lib-tests
|
||||
url = https://github.com/html5lib/html5lib-tests.git
|
||||
[submodule "test/web-platform-tests"]
|
||||
path = test/web-platform-tests
|
||||
url = https://github.com/w3c/web-platform-tests.git
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"ui": "exports",
|
||||
"reporter": "dot",
|
||||
"require": "should",
|
||||
"slow": 20,
|
||||
"timeout": 15000,
|
||||
"check-leaks": true
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
16.13.0
|
||||
+550
File diff suppressed because one or more lines are too long
+294
@@ -0,0 +1,294 @@
|
||||
# domino x.x.x (not yet released)
|
||||
|
||||
# domino 2.1.6 (16 Jul 2020)
|
||||
* Bumped version of lodash (#169)
|
||||
* Performance improvement to DOMTokenList (#166)
|
||||
* `mocha` dependency has been updated to 6.x. As a result, we are
|
||||
no longer testing on node 4.
|
||||
|
||||
# domino 2.1.5 (30 Apr 2020)
|
||||
* Bumped version of jquery dev dependency (#163)
|
||||
* Omit tests/ directory from NPM package (#161)
|
||||
|
||||
# domino 2.1.4 (16 Dec 2019)
|
||||
* Bug fix for `Element#closest` when selector doesn't match (#154)
|
||||
|
||||
# domino 2.1.3 (6 Mar 2019)
|
||||
* Bug fix for CSS `$=` selector and for matches on root `<html>` element.
|
||||
* Renamed CSS `:matches` to `:is`
|
||||
( https://github.com/w3c/csswg-drafts/issues/3258 )
|
||||
* Bug fix for CSS matches with escape characters in tag name.
|
||||
|
||||
# domino 2.1.2 (14 Feb 2019)
|
||||
* Allow writable Element constructors unless __domino_frozen__ is set to true (#138)
|
||||
* Bug fix for CSS `$=` selector. (#135)
|
||||
* Move `Node#_serializeOne()` to `NodeUtils.serializeOne()` to reduce pressure
|
||||
on the megamorphic stub cache in V8, and thereby improve throughput (#142).
|
||||
* Implemented `HTMLOptionElement#text` and `HTMLOptionElement#value` (#136)
|
||||
|
||||
# domino 2.1.1 (30 Nov 2018)
|
||||
* Add `domino.createIncrementalHTMLParser` interface.
|
||||
|
||||
# domino 2.1.0 (13 Aug 2018)
|
||||
* Fix `ContainerNode#removeChildren()` when there is more than one child (#129)
|
||||
* Implement `Document#scrollingElement` (#107)
|
||||
* Implement setter for `Element#outerHTML` (#102)
|
||||
* Handle null/undefined in setter for `Node#textContent`
|
||||
* Handle null/undefined/negative values in `CharacterData` interface methods
|
||||
* Spec-correctness fixes for `DOMTokenList`, including handling of duplicate
|
||||
keys.
|
||||
* Fix `[src=...]` selectors in `Document#querySelector()` and similar
|
||||
* Spec-correctness fixes for `Document#createElement()` and
|
||||
`Document#createElementNS()`, including proper exception type and type
|
||||
coercion.
|
||||
* Implement `Attr#cloneNode()`, `Element#getAttributeNode()`,
|
||||
`Element#getAttributeNodeNS()`, `Element#setAttributeNode()`,
|
||||
`Element#setAttributeNodeNS()`, and `Element#removeAttributeNode()`
|
||||
(DOM3 compatibility)
|
||||
* Implement `Document#createAttribute()` and `Document#createAttributeNS()`
|
||||
* Implement `Element#hasAttributes()`, `Element#toggleAttribute()`, and
|
||||
`Element#getAttributeNames()`
|
||||
* Implement `Text#wholeText`
|
||||
* Implement `Document#cloneNode()` and `DocumentType#cloneNode()`
|
||||
* Spec-correctness fixes for `Node#lookupPrefix()`,
|
||||
`Node#lookupNamespaceURI()`, and `Node#isDefaultNamespace`, including
|
||||
proper type coercion and reconciling DOM 3 and DOM 4 specifications.
|
||||
* Ensure `Document#title` continues to use correct whitespace stripping
|
||||
for node > 4, and properly set `<title>` when `undefined` is passed to
|
||||
`DOMImplementation#createHTMLDocument()`
|
||||
* Ensure `Element#attributes` implements `NamedNodeMap` and that indexed
|
||||
properties of `Element#attributes` work (previously you needed to use
|
||||
the `item()` accessor method)
|
||||
* Improve stubs for `HTMLElement#style`, `Document#documentURI`, and
|
||||
`Document#contentType`
|
||||
* Implement proper accessors for `HTMLSelectElement#autocomplete`,
|
||||
`HTMLTextAreaElement#type/value/defaultValue/textLength`, and
|
||||
`HTMLInputElement#width/height/minLength`
|
||||
* Implement `Element#insertAdjacentElement()`, `Element#insertAdjacentText()`,
|
||||
and `Element#insertAdjacentHTML()` (#102)
|
||||
* Spec-correctness fixes for `TreeWalker` and `NodeIterator`: read-only
|
||||
properties, proper exception types, type coercion of `NodeFilter` results.
|
||||
* Implement `NodeIterator` pre-removal steps. Note that in the absence
|
||||
of weak references, be cautious about the number of `NodeIterator`s you
|
||||
create on any single document, since domino does not artificially limit
|
||||
these.
|
||||
See https://github.com/tc39/proposal-weakrefs/issues/17 for details.
|
||||
* Preserve prefix of SVG elements during parsing. (#102)
|
||||
|
||||
# domino 2.0.3 (12 Jul 2018)
|
||||
* Define `blur()`, `focus()` and `forceSpellCheck()` on `HTMLElement` (#125)
|
||||
* Stringify argument tokens for DOMTokenList methods (#126)
|
||||
* Fix `HTMLAnchorElement#hash` when `href` attribute contains bare
|
||||
fragment (#127)
|
||||
* Implement case-insensitive CSS attribute matching (#128)
|
||||
* Implement `DOMTokenList#replace()`, `DOMTokenList#toggle(token, force)`,
|
||||
and `DOMTokenList#value`. Fix handling of non-space whitespace. (#111)
|
||||
|
||||
# domino 2.0.2 (28 Mar 2018)
|
||||
* Add TypeScript definitions (#103)
|
||||
* Add `flex` CSS styles (#119, #120)
|
||||
* Fix Element#matches with ~= selectors (#121)
|
||||
|
||||
# domino 2.0.1 (14 Feb 2018)
|
||||
* Allow attributes named 'xmlns' (#112)
|
||||
* Make DOMTokenList add/remove variadic (#109)
|
||||
* Make `Array.from` and for-of loops work on `Node#attributes`.
|
||||
|
||||
# domino 2.0.0 ( 8 Nov 2017)
|
||||
* Fix potential O(N^2) slowdown in FilteredElementList#item.
|
||||
* `mocha` dependency has been updated to 4.0.x. As a result, we are
|
||||
no longer testing on node pre-v4.0.0; see:
|
||||
https://boneskull.com/mocha-v4-nears-release/
|
||||
* Domino now uses a linked list representation for children of Node,
|
||||
unless/until the Node#childNodes accessor is used (which requires
|
||||
an indexed array to be built). Inserting a removing nodes can be
|
||||
much quicker using the linked list representation if care is
|
||||
taken not to deoptimize the tree by using the #childNodes accessor.
|
||||
This implementation strategy matches the one used by webkit and
|
||||
other browser-based implementations, and thus ought to match
|
||||
performance expectations of folks used to writing browser-based
|
||||
DOM manipulation code.
|
||||
|
||||
# domino 1.0.30 (24 Oct 2017)
|
||||
* Fix regexp capitalization in URLUtils (#101)
|
||||
* Fix O(N^2) slowdown in initial tree traversal using nextSibling/prevSibling
|
||||
* Update `mocha` dependency to 3.5.x and `should` to 13.1.x.
|
||||
|
||||
# domino 1.0.29 ( 7 Aug 2017)
|
||||
* Fix "#id" optimization in querySelectorAll() when 0 or 2 matches for
|
||||
`id`. (#99)
|
||||
* Correct return value of CSSStyleDeclaration#getPropertyValue() when
|
||||
style is not set. (#98)
|
||||
|
||||
# domino 1.0.28 (27 Jan 2017)
|
||||
* Fix unescape mechanism in attribute values. (#95)
|
||||
* Disable nonstandard "ignore case" version of attribute matching.
|
||||
* Add `dom/nodes` tests from w3c/web-platform-tests. (#92, @pimterry)
|
||||
* Make selected API methods writable to support polyfills. (#89, @pimterry)
|
||||
* Fix `Element#hasAttribute`/`Element#hasAttributeNS` after
|
||||
`Element#removeAttribute`/`Element#removeAttributeNS`. (#90, @clint-tseng)
|
||||
* Fix deep `Document#importNode`. (#93)
|
||||
* Ensure that `Node#parentNode` is `null` (not `undefined`) when removed.
|
||||
* Tweak JavaScript properties which are DOM reflections of element
|
||||
attributes in order to more closely match the DOM 4 spec.
|
||||
* Implement `ChildNode#before()`, `ChildNode#after()`, and
|
||||
`ChildNode#replaceWith()`.
|
||||
|
||||
# domino 1.0.27 (17 Oct 2016)
|
||||
* Fix bug in AFE list replacement over existing bookmark.
|
||||
* Update htmlwg test suite to latest w3c/web-platform-tests.
|
||||
* Update html5lib test suite to latest.
|
||||
* HTML5 spec update: <menuitem> is no longer an empty element.
|
||||
* HTML5 spec update: tweaked HTML entity parsing in attributes.
|
||||
* HTML5 spec update: dashes are allowed in HTML comments.
|
||||
* HTML5 spec update: remove special handling of <isindex>.
|
||||
* Improve handling of legacy elements: `<xmp>`, `<listing>`, `acronym`,
|
||||
`basefont`, `big`, `center`, `nobr`, `noembed`, `noframes`, `plaintext`,
|
||||
`rb`, `rtc`, `strike`, and `tt`.
|
||||
* HTML5 spec update: Remove extra newline in serialization of `<pre>`,
|
||||
`<listing>`, `<textarea>`. (#88)
|
||||
* HTML5 spec update: Remove case normalization for defunct SVG attributes.
|
||||
* Implement HTMLMenuItemElement#label.
|
||||
* Basic SVG support. (#81, #82)
|
||||
|
||||
# domino 1.0.26 (15 Oct 2016)
|
||||
* Implement Document#dir.
|
||||
* Minor spec-compliance fixes to Document#title and classList#contains.
|
||||
* Implement Element#closest(). (#84)
|
||||
* Actually run the HTMLWG tests (#83)
|
||||
* Expose the HTML5 tree builder implementation. (#87)
|
||||
* Add workaround to W3C test harness for node >= 0.11.7.
|
||||
* Update the form-associated element list to match HTML5.
|
||||
|
||||
# domino 1.0.25 (19 May 2016)
|
||||
* Fix broken stopping of immediate propagation of Events. (#78)
|
||||
* Properly set "scripting enabled" flag when parsing fragments.
|
||||
* Fix handling of escaped or invalid CSS identifiers in
|
||||
`querySelector` and friends. (#79)
|
||||
|
||||
# domino 1.0.24 (05 Apr 2016)
|
||||
* Implement WindowTimers interface on Window. (#72)
|
||||
* Factor out the NavigatorID interface and make more spec-compliant.
|
||||
* Implement `HTMLTemplateElement` and parse `<template>` tags.
|
||||
* Properly parse the `<main>` tag.
|
||||
* Remove support for the non-standard `<command>` tag.
|
||||
* Create `HTMLCanvasElement` when parsing `<canvas>` tags.
|
||||
* Create `HTMLDialogElement` when parsing `<dialog>` tags.
|
||||
* Fix parsing of `<ruby>` tags, especially `<rb>` and `<rtc>`.
|
||||
* Create `HTMLMenuItemElement` when parsing `<menuitem>` tags.
|
||||
* Create `HTMLSourceElement` when parsing `<source>` tags.
|
||||
* Create `HTMLTrackElement` when parsing `<track>` tags.
|
||||
* Improve parsing of `<svg>` elements.
|
||||
* Fix parsing of `<isindex>` element in unusual contexts.
|
||||
* Serialize `<!DOCTYPE>` according to latest HTML5 spec.
|
||||
* Update adoption agency algorithm to match latest HTML5 spec.
|
||||
* Add additional parameter to `domino.createDocument` to
|
||||
allow creating a document from an empty string if desired.
|
||||
* Add tree builder test cases from `html5lib-tests`.
|
||||
* Implement `Document#location`. (#75)
|
||||
* Stub out additional properties of `HTMLIFrameElement`. (#76)
|
||||
|
||||
# domino 1.0.23 (30 Jan 2016)
|
||||
* Fix `CSSStyleDeclaration#setProperty`. (#71)
|
||||
* Update bundled CSS parser to 0.2.5+domino1.
|
||||
|
||||
# domino 1.0.22 (27 Jan 2016)
|
||||
* Prevent TypeError due to undefined property when parsing styles. (#68)
|
||||
* Support legacy `Attr#nodeValue` and `Attr#textContent` aliases. (#70)
|
||||
|
||||
# domino 1.0.21 (23 Dec 2015)
|
||||
* Improve performance when adding nodes with duplicate IDs. (#60)
|
||||
* Be more careful about setting prototype to `null` when using
|
||||
Objects as a Map. (#61)
|
||||
* Fix a global leak in NodeIterator.
|
||||
* Improve efficiency of `Node#replaceChild` and `Node#insert`. (#62)
|
||||
* Bug fix for `Node#normalize` which could cause deletion of empty
|
||||
`Comment` or `ProcessingInstruction` nodes. (#63)
|
||||
* Don't lowercase non-ASCII tag and attribute names. (#65)
|
||||
* Fix a number of minor bugs in rarely used code, discovered
|
||||
during delinting. (#66)
|
||||
* Implement `Node.contains`. (#67)
|
||||
|
||||
# domino 1.0.20 (20 Nov 2015)
|
||||
* CharacterData implements the NonDocumentTypeChildNode
|
||||
interface. (#57, #58)
|
||||
* Fix CSS `[style]` selector. (#59)
|
||||
|
||||
# domino 1.0.19 (29 Jul 2015)
|
||||
* Bug fixes for `TreeWalker` / `document.createTreeWalker` (filter
|
||||
argument was ignored; various traversal issues)
|
||||
* Implement `NodeIterator` / `document.createNodeIterator` (#54)
|
||||
* Update `mocha` dependency to 2.2.x and `should` to 7.0.x.
|
||||
|
||||
# domino 1.0.18 (25 Sep 2014)
|
||||
* HTMLAnchorElement now implements URLUtils. (#47)
|
||||
* Be consistent with our handling of null/empty namespaces. (#48)
|
||||
* Update `mocha` dependency to 1.21.x and `should` to 4.0.x.
|
||||
|
||||
# domino 1.0.17 (14 May 2014)
|
||||
* Brown paper bag bug fix for an HTML parsing regression introduced in
|
||||
domino 1.0.16. (#45)
|
||||
* Update `mocha` dependency to 1.18.x and `should` to 3.3.x.
|
||||
|
||||
# domino 1.0.16 (13 May 2014)
|
||||
**DO NOT USE:** contains parser regression, fixed in 1.0.17.
|
||||
* Various performance improvements to the HTML5 parser. (#43, #44)
|
||||
* Fix `Element#isHTML` for non-HTML elements. (#41)
|
||||
|
||||
# domino 1.0.15 (21 Jan 2014)
|
||||
* Implement `Element#matches()`.
|
||||
* Fix CSS `[lang]`, `[dir]`, etc selectors.
|
||||
* Update `mocha` dependency to 1.17.x.
|
||||
|
||||
# domino 1.0.14 (21 Dec 2013)
|
||||
* `Element#classList.length` should be 0 if there's no `class`
|
||||
attribute.
|
||||
* Add `height`/`width` attributes to `HTMLImageElement`.
|
||||
* Fix node 0.11 incompatibility in the w3c test harness.
|
||||
* Update `mocha` dependency to 1.16.x; update `should` dependency to 2.1.x.
|
||||
|
||||
# domino 1.0.13 (8 Oct 2013)
|
||||
* Include `<th>` elements in `HTMLTableRowElement#cells`. (#38, #39)
|
||||
* Fix old call to `toLowerCase()` function. (#37)
|
||||
* Update `mocha` and `should` dependencies.
|
||||
|
||||
# domino 1.0.12 (9 Jul 2013)
|
||||
* Fix bug in formatting element adoption agency algorithm. (#36)
|
||||
* Coerce `document.createTextNode` argument to a string. (#34, #35)
|
||||
* Work around performance regression in node <= 0.6.
|
||||
|
||||
# domino 1.0.11 (1 May 2013)
|
||||
* Fix rooted element traversal (`Element#nextElement`,
|
||||
`Element#getElementsByTagName`). (#31, #32)
|
||||
* Update zest to fix bugs in `+` and `>` combinators.
|
||||
* Don't overflow the stack if attribute values are very large (>64k).
|
||||
|
||||
# domino 1.0.10 (12 Apr 2013)
|
||||
* Document issues with `Element#attributes`. (#27)
|
||||
* Fix `Document#title` to match DOM spec. (#29)
|
||||
* Add missing `require('utils')` for `handleErrors`. (#28)
|
||||
* Implement `DocumentFragment#querySelector` and
|
||||
`DocumentFragment#querySelectorAll`. (#20, #26)
|
||||
* Fix `querySelectorAll` on unparented `Element`s. (#23)
|
||||
* Move `outerHTML`/`innerHTML` properties from `HTMLElement` to
|
||||
`Element` to match dom parsing spec. (#21)
|
||||
* Update zest selector library to 0.0.4. (#25)
|
||||
* Fix regression in node 0.10. (#22, #24)
|
||||
* Update `mocha` and `should` dependencies.
|
||||
|
||||
# domino 1.0.9 (11 Mar 2013)
|
||||
* Support jQuery 1.9.x by allowing `Element#attributes[qname]`.
|
||||
* Implement `HTMLElement#outerHTML`. (#18)
|
||||
* Only add newlines after `<pre>`/`<textarea>`/`<listing>` if
|
||||
necessary, to match HTML5 serialization spec. (#16, #17)
|
||||
* Mirror node type properties (`ELEMENT_NODE`, etc) into
|
||||
`Node.prototype`. (#14, #15)
|
||||
|
||||
# domino 1.0.8
|
||||
|
||||
**DO NOT USE:** was inadvertently published identical to domino 1.0.7.
|
||||
|
||||
# domino 1.0.7 (16 Jan 2013)
|
||||
* Throw `SyntaxError` upon invocation rather than build-time. (#10)
|
||||
* Return nodes in document order. (#11)
|
||||
* Added a TreeWalker implementation.
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
# Contributing to Mixmark's Domino
|
||||
|
||||
Mixmark's Domino is intended to serve as a reference HTML parser implementation for Turndown package.
|
||||
No contributions other than bugfixes to supported Turndown use case will be accepted.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2011 The Mozilla Foundation.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
This repository is only intended to be used as a reference implementation of Turndown's reference HTML parser.
|
||||
It is not intended to be used as a general-purpose DOM implementation. No contributions other than bugfixes to
|
||||
supported Turndown use case will be accepted.
|
||||
|
||||
|
||||
# Server-side DOM implementation based on Mozilla's dom.js
|
||||
|
||||
This is a fork of [Angular Domino](https://github.com/angular/domino), which is fork of the original [Domino](https://github.com/fgnass/domino).
|
||||
|
||||
As the name might suggest, domino's goal is to provide a <b>DOM in No</b>de.
|
||||
|
||||
In contrast to the original [dom.js](https://github.com/andreasgal/dom.js) project, domino was not designed to run untrusted code. Hence it doesn't have to hide its internals behind a proxy facade which makes the code not only simpler, but also [more performant](https://github.com/fgnass/dombench).
|
||||
|
||||
Domino currently doesn't use any harmony/ES6 features like proxies or WeakMaps and therefore also runs in older Node versions.
|
||||
|
||||
## Speed over Compliance
|
||||
|
||||
Domino is intended for _building_ pages rather than scraping them. Hence Domino doesn't execute scripts nor does it download external resources.
|
||||
|
||||
Also Domino doesn't generally implement properties which have been deprecated in HTML5.
|
||||
|
||||
Domino sticks to [DOM level 4](http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-attr), which means that Attributes do not inherit the Node interface.
|
||||
|
||||
<b>Note that</b> because domino does not use proxies,
|
||||
`Element.attributes` is not a true JavaScript array; it is an object
|
||||
with a `length` property and an `item(n)` accessor method. See
|
||||
[github issue #27](https://github.com/fgnass/domino/issues/27) for
|
||||
further discussion. It does however implement direct indexed accessors
|
||||
(`element.attributes[i]`) and is live.
|
||||
|
||||
|
||||
|
||||
## CSS Selector Support
|
||||
|
||||
Domino provides support for `querySelector()`, `querySelectorAll()`, and `matches()` backed by the [Zest](https://github.com/chjj/zest) selector engine.
|
||||
|
||||
## Optimization
|
||||
|
||||
Domino represents the DOM tree structure in the same way Webkit and
|
||||
other browser-based implementations do: as a linked list of children
|
||||
which is converted to an array-based representation iff the
|
||||
`Node#childNodes` accessor is used. You will get the best performance
|
||||
from tree modification code (inserting and removing children) if you
|
||||
avoid the use of `Node#childNodes` and traverse the tree using
|
||||
`Node#firstChild`/`Node#nextSibling` (or
|
||||
`Node#lastChild`/`Node#previousSibling`) or `querySelector()`/etc.
|
||||
|
||||
## Usage
|
||||
|
||||
Domino supports the DOM level 4 API, and thus API documentation can be
|
||||
found on standard reference sites. For example, you could start from
|
||||
MDN's documentation for
|
||||
[Document](https://developer.mozilla.org/en-US/docs/Web/API/Document) and
|
||||
[Node](https://developer.mozilla.org/en-US/docs/Web/API/Node).
|
||||
|
||||
The only exception is the initial creation of a document:
|
||||
```javascript
|
||||
var domino = require('domino');
|
||||
var Element = domino.impl.Element; // etc
|
||||
|
||||
// alternatively: document = domino.createDocument(htmlString, true)
|
||||
|
||||
var h1 = document.querySelector('h1');
|
||||
console.log(h1.innerHTML);
|
||||
console.log(h1 instanceof Element);
|
||||
```
|
||||
|
||||
There is also an incremental parser available, if you need to interleave
|
||||
parsing with other processing:
|
||||
```javascript
|
||||
var domino = require('domino');
|
||||
|
||||
var pauseAfter = function(ms) {
|
||||
var start = Date.now();
|
||||
return function() { return (Date.now() - start) >= ms; };
|
||||
};
|
||||
|
||||
var incrParser = domino.createIncrementalHTMLParser();
|
||||
incrParser.write('<p>hello<');
|
||||
incrParser.write('b>&am');
|
||||
incrParser.process(pauseAfter(1/*ms*/)); // can interleave processing
|
||||
incrParser.write('p;');
|
||||
// ...etc...
|
||||
incrParser.end(); // when done writing the document
|
||||
|
||||
while (incrParser.process(pauseAfter(10/*ms*/))) {
|
||||
// ...do other housekeeping...
|
||||
}
|
||||
|
||||
console.log(incrParser.document().outerHTML);
|
||||
```
|
||||
|
||||
If you want a more standards-compliant way to create a `Document`, you can
|
||||
also use [DOMImplementation](https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation):
|
||||
```javascript
|
||||
var domino = require('domino');
|
||||
var domimpl = domino.createDOMImplementation();
|
||||
var doc = domimpl.createHTMLDocument();
|
||||
```
|
||||
|
||||
By default many domino methods will be stored in writable properties, to
|
||||
allow polyfills (as browsers do). You can lock down the implementation
|
||||
if desired as follows:
|
||||
```javascript
|
||||
global.__domino_frozen__ = true; // Must precede any `require('domino')`
|
||||
var domino = require('domino');
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
Domino includes test from the [W3C DOM Conformance Suites](http://www.w3.org/DOM/Test/)
|
||||
as well as tests from [HTML Working Group](http://www.w3.org/html/wg/wiki/Testing).
|
||||
|
||||
When you checkout this repository for the first time, run the following command to also check out code for the mentioned tests:
|
||||
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
The tests can be run via `npm test` or directly though the [Mocha](http://mochajs.org/) command line:
|
||||
|
||||

|
||||
|
||||
## License and Credits
|
||||
|
||||
The majority of the code was originally written by [Andreas Gal](https://github.com/andreasgal/) and [David Flanagan](https://github.com/davidflanagan) as part of the [dom.js](https://github.com/andreasgal/dom.js) project. Please refer to the included LICENSE file for the original copyright notice and disclaimer.
|
||||
|
||||
[Felix Gnass](https://github.com/fgnass/) extracted the code and turned
|
||||
it into a stand-alone npm package.
|
||||
|
||||
The code has been maintained since 2013 by [C. Scott Ananian](https://github.com/cscott/) on behalf of the Wikimedia Foundation, which uses it in its
|
||||
[Parsoid](https://www.mediawiki.org/wiki/Parsoid) project. A large number
|
||||
of improvements have been made, mostly focusing on correctness,
|
||||
performance, and (to a lesser extent) completeness of the implementation.
|
||||
|
||||
[1]: https://travis-ci.org/fgnass/domino.svg
|
||||
[2]: https://travis-ci.org/fgnass/domino
|
||||
[3]: https://david-dm.org/fgnass/domino.svg
|
||||
[4]: https://david-dm.org/fgnass/domino
|
||||
[5]: https://david-dm.org/fgnass/domino/dev-status.svg
|
||||
[6]: https://david-dm.org/fgnass/domino#info=devDependencies
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
"use strict";
|
||||
|
||||
const { parse } = require('./style_parser');
|
||||
|
||||
module.exports = function (elt) {
|
||||
const style = new CSSStyleDeclaration(elt)
|
||||
const handler = {
|
||||
get: function(target, property) {
|
||||
return property in target ? target[property] : target.getPropertyValue(dasherizeProperty(property));
|
||||
},
|
||||
has: function(target, key) {
|
||||
return true;
|
||||
},
|
||||
set: function(target, property, value) {
|
||||
if (property in target) {
|
||||
target[property] = value;
|
||||
} else {
|
||||
target.setProperty(dasherizeProperty(property), value ?? undefined);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
return new Proxy(style, handler);
|
||||
};
|
||||
|
||||
function dasherizeProperty(property) {
|
||||
return property.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
function CSSStyleDeclaration(elt) {
|
||||
this._element = elt;
|
||||
}
|
||||
|
||||
const IMPORTANT_BANG = '!important';
|
||||
|
||||
// Utility function for parsing style declarations
|
||||
// Pass in a string like "margin-left: 5px; border-style: solid"
|
||||
// and this function returns an object like
|
||||
// {"margin-left":"5px", "border-style":"solid"}
|
||||
function parseStyles(value) {
|
||||
const result = {
|
||||
property: {},
|
||||
priority: {},
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const styleValues = parse(value);
|
||||
if (styleValues.length < 2) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (let i = 0; i < styleValues.length; i += 2) {
|
||||
const name = styleValues[i];
|
||||
let value = styleValues[i+1];
|
||||
|
||||
if (value.endsWith(IMPORTANT_BANG)) {
|
||||
result.priority[name] = 'important';
|
||||
value = value.slice(0, -IMPORTANT_BANG.length).trim();
|
||||
}
|
||||
|
||||
result.property[name] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var NO_CHANGE = {}; // Private marker object
|
||||
|
||||
CSSStyleDeclaration.prototype = Object.create(Object.prototype, {
|
||||
|
||||
// Return the parsed form of the element's style attribute.
|
||||
// If the element's style attribute has never been parsed
|
||||
// or if it has changed since the last parse, then reparse it
|
||||
// Note that the styles don't get parsed until they're actually needed
|
||||
_parsed: { get: function() {
|
||||
if (!this._parsedStyles || this.cssText !== this._lastParsedText) {
|
||||
var text = this.cssText;
|
||||
this._parsedStyles = parseStyles(text);
|
||||
this._lastParsedText = text;
|
||||
delete this._names;
|
||||
}
|
||||
return this._parsedStyles;
|
||||
}},
|
||||
|
||||
// Call this method any time the parsed representation of the
|
||||
// style changes. It converts the style properties to a string and
|
||||
// sets cssText and the element's style attribute
|
||||
_serialize: { value: function() {
|
||||
var styles = this._parsed;
|
||||
var s = "";
|
||||
|
||||
for(var name in styles.property) {
|
||||
if (s) s += " ";
|
||||
s += name + ": " + styles.property[name];
|
||||
if (styles.priority[name]) {
|
||||
s += " !" + styles.priority[name];
|
||||
}
|
||||
s += ";";
|
||||
}
|
||||
|
||||
this.cssText = s; // also sets the style attribute
|
||||
this._lastParsedText = s; // so we don't reparse
|
||||
delete this._names;
|
||||
}},
|
||||
|
||||
cssText: {
|
||||
get: function() {
|
||||
// XXX: this is a CSSStyleDeclaration for an element.
|
||||
// A different impl might be necessary for a set of styles
|
||||
// associated returned by getComputedStyle(), e.g.
|
||||
return this._element.getAttribute("style");
|
||||
},
|
||||
set: function(value) {
|
||||
// XXX: I should parse and serialize the value to
|
||||
// normalize it and remove errors. FF and chrome do that.
|
||||
this._element.setAttribute("style", value);
|
||||
}
|
||||
},
|
||||
|
||||
length: { get: function() {
|
||||
if (!this._names)
|
||||
this._names = Object.getOwnPropertyNames(this._parsed.property);
|
||||
return this._names.length;
|
||||
}},
|
||||
|
||||
item: { value: function(n) {
|
||||
if (!this._names)
|
||||
this._names = Object.getOwnPropertyNames(this._parsed.property);
|
||||
return this._names[n];
|
||||
}},
|
||||
|
||||
getPropertyValue: { value: function(property) {
|
||||
property = property.toLowerCase();
|
||||
return this._parsed.property[property] || "";
|
||||
}},
|
||||
|
||||
getPropertyPriority: { value: function(property) {
|
||||
property = property.toLowerCase();
|
||||
return this._parsed.priority[property] || "";
|
||||
}},
|
||||
|
||||
setProperty: { value: function(property, value, priority) {
|
||||
property = property.toLowerCase();
|
||||
if (value === null || value === undefined) {
|
||||
value = "";
|
||||
}
|
||||
if (priority === null || priority === undefined) {
|
||||
priority = "";
|
||||
}
|
||||
|
||||
// String coercion
|
||||
if (value !== NO_CHANGE) {
|
||||
value = "" + value;
|
||||
}
|
||||
|
||||
value = value.trim();
|
||||
if (value === "") {
|
||||
this.removeProperty(property);
|
||||
return;
|
||||
}
|
||||
|
||||
if (priority !== "" && priority !== NO_CHANGE &&
|
||||
!/^important$/i.test(priority)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var styles = this._parsed;
|
||||
if (value === NO_CHANGE) {
|
||||
if (!styles.property[property]) {
|
||||
return; // Not a valid property name.
|
||||
}
|
||||
if (priority !== "") {
|
||||
styles.priority[property] = "important";
|
||||
} else {
|
||||
delete styles.priority[property];
|
||||
}
|
||||
} else {
|
||||
// We don't just accept the property value. Instead
|
||||
// we parse it to ensure that it is something valid.
|
||||
// If it contains a semicolon it is invalid
|
||||
if (value.indexOf(";") !== -1) return;
|
||||
|
||||
var newprops = parseStyles(property + ":" + value);
|
||||
if (Object.getOwnPropertyNames(newprops.property).length === 0) {
|
||||
return; // no valid property found
|
||||
}
|
||||
if (Object.getOwnPropertyNames(newprops.priority).length !== 0) {
|
||||
return; // if the value included '!important' it wasn't valid.
|
||||
}
|
||||
|
||||
// XXX handle shorthand properties
|
||||
|
||||
for (var p in newprops.property) {
|
||||
styles.property[p] = newprops.property[p];
|
||||
if (priority === NO_CHANGE) {
|
||||
continue;
|
||||
} else if (priority !== "") {
|
||||
styles.priority[p] = "important";
|
||||
} else if (styles.priority[p]) {
|
||||
delete styles.priority[p];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize and update cssText and element.style!
|
||||
this._serialize();
|
||||
}},
|
||||
|
||||
setPropertyValue: { value: function(property, value) {
|
||||
return this.setProperty(property, value, NO_CHANGE);
|
||||
}},
|
||||
|
||||
setPropertyPriority: { value: function(property, priority) {
|
||||
return this.setProperty(property, NO_CHANGE, priority);
|
||||
}},
|
||||
|
||||
removeProperty: { value: function(property) {
|
||||
property = property.toLowerCase();
|
||||
var styles = this._parsed;
|
||||
if (property in styles.property) {
|
||||
delete styles.property[property];
|
||||
delete styles.priority[property];
|
||||
|
||||
// Serialize and update cssText and element.style!
|
||||
this._serialize();
|
||||
}
|
||||
}},
|
||||
});
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/* jshint bitwise: false */
|
||||
"use strict";
|
||||
module.exports = CharacterData;
|
||||
|
||||
var Leaf = require('./Leaf');
|
||||
var utils = require('./utils');
|
||||
var ChildNode = require('./ChildNode');
|
||||
var NonDocumentTypeChildNode = require('./NonDocumentTypeChildNode');
|
||||
|
||||
function CharacterData() {
|
||||
Leaf.call(this);
|
||||
}
|
||||
|
||||
CharacterData.prototype = Object.create(Leaf.prototype, {
|
||||
// DOMString substringData(unsigned long offset,
|
||||
// unsigned long count);
|
||||
// The substringData(offset, count) method must run these steps:
|
||||
//
|
||||
// If offset is greater than the context object's
|
||||
// length, throw an INDEX_SIZE_ERR exception and
|
||||
// terminate these steps.
|
||||
//
|
||||
// If offset+count is greater than the context
|
||||
// object's length, return a DOMString whose value is
|
||||
// the UTF-16 code units from the offsetth UTF-16 code
|
||||
// unit to the end of data.
|
||||
//
|
||||
// Return a DOMString whose value is the UTF-16 code
|
||||
// units from the offsetth UTF-16 code unit to the
|
||||
// offset+countth UTF-16 code unit in data.
|
||||
substringData: { value: function substringData(offset, count) {
|
||||
if (arguments.length < 2) { throw new TypeError("Not enough arguments"); }
|
||||
// Convert arguments to WebIDL "unsigned long"
|
||||
offset = offset >>> 0;
|
||||
count = count >>> 0;
|
||||
if (offset > this.data.length || offset < 0 || count < 0) {
|
||||
utils.IndexSizeError();
|
||||
}
|
||||
return this.data.substring(offset, offset+count);
|
||||
}},
|
||||
|
||||
// void appendData(DOMString data);
|
||||
// The appendData(data) method must append data to the context
|
||||
// object's data.
|
||||
appendData: { value: function appendData(data) {
|
||||
if (arguments.length < 1) { throw new TypeError("Not enough arguments"); }
|
||||
this.data += String(data);
|
||||
}},
|
||||
|
||||
// void insertData(unsigned long offset, DOMString data);
|
||||
// The insertData(offset, data) method must run these steps:
|
||||
//
|
||||
// If offset is greater than the context object's
|
||||
// length, throw an INDEX_SIZE_ERR exception and
|
||||
// terminate these steps.
|
||||
//
|
||||
// Insert data into the context object's data after
|
||||
// offset UTF-16 code units.
|
||||
//
|
||||
insertData: { value: function insertData(offset, data) {
|
||||
return this.replaceData(offset, 0, data);
|
||||
}},
|
||||
|
||||
|
||||
// void deleteData(unsigned long offset, unsigned long count);
|
||||
// The deleteData(offset, count) method must run these steps:
|
||||
//
|
||||
// If offset is greater than the context object's
|
||||
// length, throw an INDEX_SIZE_ERR exception and
|
||||
// terminate these steps.
|
||||
//
|
||||
// If offset+count is greater than the context
|
||||
// object's length var count be length-offset.
|
||||
//
|
||||
// Starting from offset UTF-16 code units remove count
|
||||
// UTF-16 code units from the context object's data.
|
||||
deleteData: { value: function deleteData(offset, count) {
|
||||
return this.replaceData(offset, count, '');
|
||||
}},
|
||||
|
||||
|
||||
// void replaceData(unsigned long offset, unsigned long count,
|
||||
// DOMString data);
|
||||
//
|
||||
// The replaceData(offset, count, data) method must act as
|
||||
// if the deleteData() method is invoked with offset and
|
||||
// count as arguments followed by the insertData() method
|
||||
// with offset and data as arguments and re-throw any
|
||||
// exceptions these methods might have thrown.
|
||||
replaceData: { value: function replaceData(offset, count, data) {
|
||||
var curtext = this.data, len = curtext.length;
|
||||
// Convert arguments to correct WebIDL type
|
||||
offset = offset >>> 0;
|
||||
count = count >>> 0;
|
||||
data = String(data);
|
||||
|
||||
if (offset > len || offset < 0) utils.IndexSizeError();
|
||||
|
||||
if (offset+count > len)
|
||||
count = len - offset;
|
||||
|
||||
var prefix = curtext.substring(0, offset),
|
||||
suffix = curtext.substring(offset+count);
|
||||
|
||||
this.data = prefix + data + suffix;
|
||||
}},
|
||||
|
||||
// Utility method that Node.isEqualNode() calls to test Text and
|
||||
// Comment nodes for equality. It is okay to put it here, since
|
||||
// Node will have already verified that nodeType is equal
|
||||
isEqual: { value: function isEqual(n) {
|
||||
return this._data === n._data;
|
||||
}},
|
||||
|
||||
length: { get: function() { return this.data.length; }}
|
||||
|
||||
});
|
||||
|
||||
Object.defineProperties(CharacterData.prototype, ChildNode);
|
||||
Object.defineProperties(CharacterData.prototype, NonDocumentTypeChildNode);
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
"use strict";
|
||||
|
||||
var Node = require('./Node');
|
||||
var LinkedList = require('./LinkedList');
|
||||
|
||||
var createDocumentFragmentFromArguments = function(document, args) {
|
||||
var docFrag = document.createDocumentFragment();
|
||||
|
||||
for (var i=0; i<args.length; i++) {
|
||||
var argItem = args[i];
|
||||
var isNode = argItem instanceof Node;
|
||||
docFrag.appendChild(isNode ? argItem :
|
||||
document.createTextNode(String(argItem)));
|
||||
}
|
||||
|
||||
return docFrag;
|
||||
};
|
||||
|
||||
// The ChildNode interface contains methods that are particular to `Node`
|
||||
// objects that can have a parent. It is implemented by `Element`,
|
||||
// `DocumentType`, and `CharacterData` objects.
|
||||
var ChildNode = {
|
||||
|
||||
// Inserts a set of Node or String objects in the children list of this
|
||||
// ChildNode's parent, just after this ChildNode. String objects are
|
||||
// inserted as the equivalent Text nodes.
|
||||
after: { value: function after() {
|
||||
var argArr = Array.prototype.slice.call(arguments);
|
||||
var parentNode = this.parentNode, nextSibling = this.nextSibling;
|
||||
if (parentNode === null) { return; }
|
||||
// Find "viable next sibling"; that is, next one not in argArr
|
||||
while (nextSibling && argArr.some(function(v) { return v===nextSibling; }))
|
||||
nextSibling = nextSibling.nextSibling;
|
||||
// ok, parent and sibling are saved away since this node could itself
|
||||
// appear in argArr and we're about to move argArr to a document fragment.
|
||||
var docFrag = createDocumentFragmentFromArguments(this.doc, argArr);
|
||||
|
||||
parentNode.insertBefore(docFrag, nextSibling);
|
||||
}},
|
||||
|
||||
// Inserts a set of Node or String objects in the children list of this
|
||||
// ChildNode's parent, just before this ChildNode. String objects are
|
||||
// inserted as the equivalent Text nodes.
|
||||
before: { value: function before() {
|
||||
var argArr = Array.prototype.slice.call(arguments);
|
||||
var parentNode = this.parentNode, prevSibling = this.previousSibling;
|
||||
if (parentNode === null) { return; }
|
||||
// Find "viable prev sibling"; that is, prev one not in argArr
|
||||
while (prevSibling && argArr.some(function(v) { return v===prevSibling; }))
|
||||
prevSibling = prevSibling.previousSibling;
|
||||
// ok, parent and sibling are saved away since this node could itself
|
||||
// appear in argArr and we're about to move argArr to a document fragment.
|
||||
var docFrag = createDocumentFragmentFromArguments(this.doc, argArr);
|
||||
|
||||
var nextSibling =
|
||||
prevSibling ? prevSibling.nextSibling : parentNode.firstChild;
|
||||
parentNode.insertBefore(docFrag, nextSibling);
|
||||
}},
|
||||
|
||||
// Remove this node from its parent
|
||||
remove: { value: function remove() {
|
||||
if (this.parentNode === null) return;
|
||||
|
||||
// Send mutation events if necessary
|
||||
if (this.doc) {
|
||||
this.doc._preremoveNodeIterators(this);
|
||||
if (this.rooted) {
|
||||
this.doc.mutateRemove(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove this node from its parents array of children
|
||||
// and update the structure id for all ancestors
|
||||
this._remove();
|
||||
|
||||
// Forget this node's parent
|
||||
this.parentNode = null;
|
||||
}},
|
||||
|
||||
// Remove this node w/o uprooting or sending mutation events
|
||||
// (But do update the structure id for all ancestors)
|
||||
_remove: { value: function _remove() {
|
||||
var parent = this.parentNode;
|
||||
if (parent === null) return;
|
||||
if (parent._childNodes) {
|
||||
parent._childNodes.splice(this.index, 1);
|
||||
} else if (parent._firstChild === this) {
|
||||
if (this._nextSibling === this) {
|
||||
parent._firstChild = null;
|
||||
} else {
|
||||
parent._firstChild = this._nextSibling;
|
||||
}
|
||||
}
|
||||
LinkedList.remove(this);
|
||||
parent.modify();
|
||||
}},
|
||||
|
||||
// Replace this node with the nodes or strings provided as arguments.
|
||||
replaceWith: { value: function replaceWith() {
|
||||
var argArr = Array.prototype.slice.call(arguments);
|
||||
var parentNode = this.parentNode, nextSibling = this.nextSibling;
|
||||
if (parentNode === null) { return; }
|
||||
// Find "viable next sibling"; that is, next one not in argArr
|
||||
while (nextSibling && argArr.some(function(v) { return v===nextSibling; }))
|
||||
nextSibling = nextSibling.nextSibling;
|
||||
// ok, parent and sibling are saved away since this node could itself
|
||||
// appear in argArr and we're about to move argArr to a document fragment.
|
||||
var docFrag = createDocumentFragmentFromArguments(this.doc, argArr);
|
||||
if (this.parentNode === parentNode) {
|
||||
parentNode.replaceChild(docFrag, this);
|
||||
} else {
|
||||
// `this` was inserted into docFrag
|
||||
parentNode.insertBefore(docFrag, nextSibling);
|
||||
}
|
||||
}},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ChildNode;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
module.exports = Comment;
|
||||
|
||||
var Node = require('./Node');
|
||||
var CharacterData = require('./CharacterData');
|
||||
|
||||
function Comment(doc, data) {
|
||||
CharacterData.call(this);
|
||||
this.nodeType = Node.COMMENT_NODE;
|
||||
this.ownerDocument = doc;
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
var nodeValue = {
|
||||
get: function() { return this._data; },
|
||||
set: function(v) {
|
||||
if (v === null || v === undefined) { v = ''; } else { v = String(v); }
|
||||
this._data = v;
|
||||
if (this.rooted)
|
||||
this.ownerDocument.mutateValue(this);
|
||||
}
|
||||
};
|
||||
|
||||
Comment.prototype = Object.create(CharacterData.prototype, {
|
||||
nodeName: { value: '#comment' },
|
||||
nodeValue: nodeValue,
|
||||
textContent: nodeValue,
|
||||
innerText: nodeValue,
|
||||
data: {
|
||||
get: nodeValue.get,
|
||||
set: function(v) {
|
||||
nodeValue.set.call(this, v===null ? '' : String(v));
|
||||
},
|
||||
},
|
||||
|
||||
// Utility methods
|
||||
clone: { value: function clone() {
|
||||
return new Comment(this.ownerDocument, this._data);
|
||||
}},
|
||||
});
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
module.exports = ContainerNode;
|
||||
|
||||
var Node = require('./Node');
|
||||
var NodeList = require('./NodeList');
|
||||
|
||||
// This class defines common functionality for node subtypes that
|
||||
// can have children
|
||||
|
||||
function ContainerNode() {
|
||||
Node.call(this);
|
||||
this._firstChild = this._childNodes = null;
|
||||
}
|
||||
|
||||
// Primary representation is a circular linked list of siblings
|
||||
ContainerNode.prototype = Object.create(Node.prototype, {
|
||||
|
||||
hasChildNodes: { value: function() {
|
||||
if (this._childNodes) {
|
||||
return this._childNodes.length > 0;
|
||||
}
|
||||
return this._firstChild !== null;
|
||||
}},
|
||||
|
||||
childNodes: { get: function() {
|
||||
this._ensureChildNodes();
|
||||
return this._childNodes;
|
||||
}},
|
||||
|
||||
firstChild: { get: function() {
|
||||
if (this._childNodes) {
|
||||
return this._childNodes.length === 0 ? null : this._childNodes[0];
|
||||
}
|
||||
return this._firstChild;
|
||||
}},
|
||||
|
||||
lastChild: { get: function() {
|
||||
var kids = this._childNodes, first;
|
||||
if (kids) {
|
||||
return kids.length === 0 ? null: kids[kids.length-1];
|
||||
}
|
||||
first = this._firstChild;
|
||||
if (first === null) { return null; }
|
||||
return first._previousSibling; // circular linked list
|
||||
}},
|
||||
|
||||
_ensureChildNodes: { value: function() {
|
||||
if (this._childNodes) { return; }
|
||||
var first = this._firstChild,
|
||||
kid = first,
|
||||
childNodes = this._childNodes = new NodeList();
|
||||
if (first) do {
|
||||
childNodes.push(kid);
|
||||
kid = kid._nextSibling;
|
||||
} while (kid !== first); // circular linked list
|
||||
this._firstChild = null; // free memory
|
||||
}},
|
||||
|
||||
// Remove all of this node's children. This is a minor
|
||||
// optimization that only calls modify() once.
|
||||
removeChildren: { value: function removeChildren() {
|
||||
var root = this.rooted ? this.ownerDocument : null,
|
||||
next = this.firstChild,
|
||||
kid;
|
||||
while (next !== null) {
|
||||
kid = next;
|
||||
next = kid.nextSibling;
|
||||
|
||||
if (root) root.mutateRemove(kid);
|
||||
kid.parentNode = null;
|
||||
}
|
||||
if (this._childNodes) {
|
||||
this._childNodes.length = 0;
|
||||
} else {
|
||||
this._firstChild = null;
|
||||
}
|
||||
this.modify(); // Update last modified type once only
|
||||
}},
|
||||
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
module.exports = CustomEvent;
|
||||
|
||||
var Event = require('./Event');
|
||||
|
||||
function CustomEvent(type, dictionary) {
|
||||
// Just use the superclass constructor to initialize
|
||||
Event.call(this, type, dictionary);
|
||||
}
|
||||
CustomEvent.prototype = Object.create(Event.prototype, {
|
||||
constructor: { value: CustomEvent }
|
||||
});
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
"use strict";
|
||||
module.exports = DOMException;
|
||||
|
||||
var INDEX_SIZE_ERR = 1;
|
||||
var HIERARCHY_REQUEST_ERR = 3;
|
||||
var WRONG_DOCUMENT_ERR = 4;
|
||||
var INVALID_CHARACTER_ERR = 5;
|
||||
var NO_MODIFICATION_ALLOWED_ERR = 7;
|
||||
var NOT_FOUND_ERR = 8;
|
||||
var NOT_SUPPORTED_ERR = 9;
|
||||
var INVALID_STATE_ERR = 11;
|
||||
var SYNTAX_ERR = 12;
|
||||
var INVALID_MODIFICATION_ERR = 13;
|
||||
var NAMESPACE_ERR = 14;
|
||||
var INVALID_ACCESS_ERR = 15;
|
||||
var TYPE_MISMATCH_ERR = 17;
|
||||
var SECURITY_ERR = 18;
|
||||
var NETWORK_ERR = 19;
|
||||
var ABORT_ERR = 20;
|
||||
var URL_MISMATCH_ERR = 21;
|
||||
var QUOTA_EXCEEDED_ERR = 22;
|
||||
var TIMEOUT_ERR = 23;
|
||||
var INVALID_NODE_TYPE_ERR = 24;
|
||||
var DATA_CLONE_ERR = 25;
|
||||
|
||||
// Code to name
|
||||
var names = [
|
||||
null, // No error with code 0
|
||||
'INDEX_SIZE_ERR',
|
||||
null, // historical
|
||||
'HIERARCHY_REQUEST_ERR',
|
||||
'WRONG_DOCUMENT_ERR',
|
||||
'INVALID_CHARACTER_ERR',
|
||||
null, // historical
|
||||
'NO_MODIFICATION_ALLOWED_ERR',
|
||||
'NOT_FOUND_ERR',
|
||||
'NOT_SUPPORTED_ERR',
|
||||
'INUSE_ATTRIBUTE_ERR', // historical
|
||||
'INVALID_STATE_ERR',
|
||||
'SYNTAX_ERR',
|
||||
'INVALID_MODIFICATION_ERR',
|
||||
'NAMESPACE_ERR',
|
||||
'INVALID_ACCESS_ERR',
|
||||
null, // historical
|
||||
'TYPE_MISMATCH_ERR',
|
||||
'SECURITY_ERR',
|
||||
'NETWORK_ERR',
|
||||
'ABORT_ERR',
|
||||
'URL_MISMATCH_ERR',
|
||||
'QUOTA_EXCEEDED_ERR',
|
||||
'TIMEOUT_ERR',
|
||||
'INVALID_NODE_TYPE_ERR',
|
||||
'DATA_CLONE_ERR',
|
||||
];
|
||||
|
||||
// Code to message
|
||||
// These strings are from the 13 May 2011 Editor's Draft of DOM Core.
|
||||
// http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html
|
||||
// Copyright © 2011 W3C® (MIT, ERCIM, Keio), All Rights Reserved.
|
||||
// Used under the terms of the W3C Document License:
|
||||
// http://www.w3.org/Consortium/Legal/2002/copyright-documents-20021231
|
||||
var messages = [
|
||||
null, // No error with code 0
|
||||
'INDEX_SIZE_ERR (1): the index is not in the allowed range',
|
||||
null,
|
||||
'HIERARCHY_REQUEST_ERR (3): the operation would yield an incorrect nodes model',
|
||||
'WRONG_DOCUMENT_ERR (4): the object is in the wrong Document, a call to importNode is required',
|
||||
'INVALID_CHARACTER_ERR (5): the string contains invalid characters',
|
||||
null,
|
||||
'NO_MODIFICATION_ALLOWED_ERR (7): the object can not be modified',
|
||||
'NOT_FOUND_ERR (8): the object can not be found here',
|
||||
'NOT_SUPPORTED_ERR (9): this operation is not supported',
|
||||
'INUSE_ATTRIBUTE_ERR (10): setAttributeNode called on owned Attribute',
|
||||
'INVALID_STATE_ERR (11): the object is in an invalid state',
|
||||
'SYNTAX_ERR (12): the string did not match the expected pattern',
|
||||
'INVALID_MODIFICATION_ERR (13): the object can not be modified in this way',
|
||||
'NAMESPACE_ERR (14): the operation is not allowed by Namespaces in XML',
|
||||
'INVALID_ACCESS_ERR (15): the object does not support the operation or argument',
|
||||
null,
|
||||
'TYPE_MISMATCH_ERR (17): the type of the object does not match the expected type',
|
||||
'SECURITY_ERR (18): the operation is insecure',
|
||||
'NETWORK_ERR (19): a network error occurred',
|
||||
'ABORT_ERR (20): the user aborted an operation',
|
||||
'URL_MISMATCH_ERR (21): the given URL does not match another URL',
|
||||
'QUOTA_EXCEEDED_ERR (22): the quota has been exceeded',
|
||||
'TIMEOUT_ERR (23): a timeout occurred',
|
||||
'INVALID_NODE_TYPE_ERR (24): the supplied node is invalid or has an invalid ancestor for this operation',
|
||||
'DATA_CLONE_ERR (25): the object can not be cloned.'
|
||||
];
|
||||
|
||||
// Name to code
|
||||
var constants = {
|
||||
INDEX_SIZE_ERR: INDEX_SIZE_ERR,
|
||||
DOMSTRING_SIZE_ERR: 2, // historical
|
||||
HIERARCHY_REQUEST_ERR: HIERARCHY_REQUEST_ERR,
|
||||
WRONG_DOCUMENT_ERR: WRONG_DOCUMENT_ERR,
|
||||
INVALID_CHARACTER_ERR: INVALID_CHARACTER_ERR,
|
||||
NO_DATA_ALLOWED_ERR: 6, // historical
|
||||
NO_MODIFICATION_ALLOWED_ERR: NO_MODIFICATION_ALLOWED_ERR,
|
||||
NOT_FOUND_ERR: NOT_FOUND_ERR,
|
||||
NOT_SUPPORTED_ERR: NOT_SUPPORTED_ERR,
|
||||
INUSE_ATTRIBUTE_ERR: 10, // historical
|
||||
INVALID_STATE_ERR: INVALID_STATE_ERR,
|
||||
SYNTAX_ERR: SYNTAX_ERR,
|
||||
INVALID_MODIFICATION_ERR: INVALID_MODIFICATION_ERR,
|
||||
NAMESPACE_ERR: NAMESPACE_ERR,
|
||||
INVALID_ACCESS_ERR: INVALID_ACCESS_ERR,
|
||||
VALIDATION_ERR: 16, // historical
|
||||
TYPE_MISMATCH_ERR: TYPE_MISMATCH_ERR,
|
||||
SECURITY_ERR: SECURITY_ERR,
|
||||
NETWORK_ERR: NETWORK_ERR,
|
||||
ABORT_ERR: ABORT_ERR,
|
||||
URL_MISMATCH_ERR: URL_MISMATCH_ERR,
|
||||
QUOTA_EXCEEDED_ERR: QUOTA_EXCEEDED_ERR,
|
||||
TIMEOUT_ERR: TIMEOUT_ERR,
|
||||
INVALID_NODE_TYPE_ERR: INVALID_NODE_TYPE_ERR,
|
||||
DATA_CLONE_ERR: DATA_CLONE_ERR
|
||||
};
|
||||
|
||||
function DOMException(code) {
|
||||
Error.call(this);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.code = code;
|
||||
this.message = messages[code];
|
||||
this.name = names[code];
|
||||
}
|
||||
DOMException.prototype.__proto__ = Error.prototype;
|
||||
|
||||
// Initialize the constants on DOMException and DOMException.prototype
|
||||
for(var c in constants) {
|
||||
var v = { value: constants[c] };
|
||||
Object.defineProperty(DOMException, c, v);
|
||||
Object.defineProperty(DOMException.prototype, c, v);
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
"use strict";
|
||||
module.exports = DOMImplementation;
|
||||
|
||||
var Document = require('./Document');
|
||||
var DocumentType = require('./DocumentType');
|
||||
var HTMLParser = require('./HTMLParser');
|
||||
var utils = require('./utils');
|
||||
var xml = require('./xmlnames');
|
||||
|
||||
// Each document must have its own instance of the domimplementation object
|
||||
function DOMImplementation(contextObject) {
|
||||
this.contextObject = contextObject;
|
||||
}
|
||||
|
||||
|
||||
// Feature/version pairs that DOMImplementation.hasFeature() returns
|
||||
// true for. It returns false for anything else.
|
||||
var supportedFeatures = {
|
||||
'xml': { '': true, '1.0': true, '2.0': true }, // DOM Core
|
||||
'core': { '': true, '2.0': true }, // DOM Core
|
||||
'html': { '': true, '1.0': true, '2.0': true} , // HTML
|
||||
'xhtml': { '': true, '1.0': true, '2.0': true} , // HTML
|
||||
};
|
||||
|
||||
DOMImplementation.prototype = {
|
||||
hasFeature: function hasFeature(feature, version) {
|
||||
var f = supportedFeatures[(feature || '').toLowerCase()];
|
||||
return (f && f[version || '']) || false;
|
||||
},
|
||||
|
||||
createDocumentType: function createDocumentType(qualifiedName, publicId, systemId) {
|
||||
if (!xml.isValidQName(qualifiedName)) utils.InvalidCharacterError();
|
||||
|
||||
return new DocumentType(this.contextObject, qualifiedName, publicId, systemId);
|
||||
},
|
||||
|
||||
createDocument: function createDocument(namespace, qualifiedName, doctype) {
|
||||
//
|
||||
// Note that the current DOMCore spec makes it impossible to
|
||||
// create an HTML document with this function, even if the
|
||||
// namespace and doctype are propertly set. See this thread:
|
||||
// http://lists.w3.org/Archives/Public/www-dom/2011AprJun/0132.html
|
||||
//
|
||||
var d = new Document(false, null);
|
||||
var e;
|
||||
|
||||
if (qualifiedName)
|
||||
e = d.createElementNS(namespace, qualifiedName);
|
||||
else
|
||||
e = null;
|
||||
|
||||
if (doctype) {
|
||||
d.appendChild(doctype);
|
||||
}
|
||||
|
||||
if (e) d.appendChild(e);
|
||||
if (namespace === utils.NAMESPACE.HTML) {
|
||||
d._contentType = 'application/xhtml+xml';
|
||||
} else if (namespace === utils.NAMESPACE.SVG) {
|
||||
d._contentType = 'image/svg+xml';
|
||||
} else {
|
||||
d._contentType = 'application/xml';
|
||||
}
|
||||
|
||||
return d;
|
||||
},
|
||||
|
||||
createHTMLDocument: function createHTMLDocument(titleText) {
|
||||
var d = new Document(true, null);
|
||||
d.appendChild(new DocumentType(d, 'html'));
|
||||
var html = d.createElement('html');
|
||||
d.appendChild(html);
|
||||
var head = d.createElement('head');
|
||||
html.appendChild(head);
|
||||
if (titleText !== undefined) {
|
||||
var title = d.createElement('title');
|
||||
head.appendChild(title);
|
||||
title.appendChild(d.createTextNode(titleText));
|
||||
}
|
||||
html.appendChild(d.createElement('body'));
|
||||
d.modclock = 1; // Start tracking modifications
|
||||
return d;
|
||||
},
|
||||
|
||||
mozSetOutputMutationHandler: function(doc, handler) {
|
||||
doc.mutationHandler = handler;
|
||||
},
|
||||
|
||||
mozGetInputMutationHandler: function(doc) {
|
||||
utils.nyi();
|
||||
},
|
||||
|
||||
mozHTMLParser: HTMLParser,
|
||||
};
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
"use strict";
|
||||
// DOMTokenList implementation based on https://github.com/Raynos/DOM-shim
|
||||
var utils = require('./utils');
|
||||
|
||||
module.exports = DOMTokenList;
|
||||
|
||||
function DOMTokenList(getter, setter) {
|
||||
this._getString = getter;
|
||||
this._setString = setter;
|
||||
this._length = 0;
|
||||
this._lastStringValue = '';
|
||||
this._update();
|
||||
}
|
||||
|
||||
Object.defineProperties(DOMTokenList.prototype, {
|
||||
length: { get: function() { return this._length; } },
|
||||
item: { value: function(index) {
|
||||
var list = getList(this);
|
||||
if (index < 0 || index >= list.length) {
|
||||
return null;
|
||||
}
|
||||
return list[index];
|
||||
}},
|
||||
|
||||
contains: { value: function(token) {
|
||||
token = String(token); // no error checking for contains()
|
||||
var list = getList(this);
|
||||
return list.indexOf(token) > -1;
|
||||
}},
|
||||
|
||||
add: { value: function() {
|
||||
var list = getList(this);
|
||||
for (var i = 0, len = arguments.length; i < len; i++) {
|
||||
var token = handleErrors(arguments[i]);
|
||||
if (list.indexOf(token) < 0) {
|
||||
list.push(token);
|
||||
}
|
||||
}
|
||||
// Note: as per spec, if handleErrors() throws any errors, we never
|
||||
// make it here and none of the changes take effect.
|
||||
// Also per spec: we run the "update steps" even if no change was
|
||||
// made (ie, if the token already existed)
|
||||
this._update(list);
|
||||
}},
|
||||
|
||||
remove: { value: function() {
|
||||
var list = getList(this);
|
||||
for (var i = 0, len = arguments.length; i < len; i++) {
|
||||
var token = handleErrors(arguments[i]);
|
||||
var index = list.indexOf(token);
|
||||
if (index > -1) {
|
||||
list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
// Note: as per spec, if handleErrors() throws any errors, we never
|
||||
// make it here and none of the changes take effect.
|
||||
// Also per spec: we run the "update steps" even if no change was
|
||||
// made (ie, if the token wasn't previously present)
|
||||
this._update(list);
|
||||
}},
|
||||
|
||||
toggle: { value: function toggle(token, force) {
|
||||
token = handleErrors(token);
|
||||
if (this.contains(token)) {
|
||||
if (force === undefined || force === false) {
|
||||
this.remove(token);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (force === undefined || force === true) {
|
||||
this.add(token);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}},
|
||||
|
||||
replace: { value: function replace(token, newToken) {
|
||||
// weird corner case of spec: if `token` contains whitespace, but
|
||||
// `newToken` is the empty string, we must throw SyntaxError not
|
||||
// InvalidCharacterError (sigh)
|
||||
if (String(newToken)==='') { utils.SyntaxError(); }
|
||||
token = handleErrors(token);
|
||||
newToken = handleErrors(newToken);
|
||||
var list = getList(this);
|
||||
var idx = list.indexOf(token);
|
||||
if (idx < 0) {
|
||||
// Note that, per spec, we do not run the update steps on this path.
|
||||
return false;
|
||||
}
|
||||
var idx2 = list.indexOf(newToken);
|
||||
if (idx2 < 0) {
|
||||
list[idx] = newToken;
|
||||
} else {
|
||||
// "replace the first instance of either `token` or `newToken` with
|
||||
// `newToken` and remove all other instances"
|
||||
if (idx < idx2) {
|
||||
list[idx] = newToken;
|
||||
list.splice(idx2, 1);
|
||||
} else {
|
||||
// idx2 is already `newToken`
|
||||
list.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
this._update(list);
|
||||
return true;
|
||||
}},
|
||||
|
||||
toString: { value: function() {
|
||||
return this._getString();
|
||||
}},
|
||||
|
||||
value: {
|
||||
get: function() {
|
||||
return this._getString();
|
||||
},
|
||||
set: function(v) {
|
||||
this._setString(v);
|
||||
this._update();
|
||||
}
|
||||
},
|
||||
|
||||
// Called when the setter is called from outside this interface.
|
||||
_update: { value: function(list) {
|
||||
if (list) {
|
||||
fixIndex(this, list);
|
||||
this._setString(list.join(" ").trim());
|
||||
} else {
|
||||
fixIndex(this, getList(this));
|
||||
}
|
||||
this._lastStringValue = this._getString();
|
||||
} },
|
||||
});
|
||||
|
||||
function fixIndex(clist, list) {
|
||||
var oldLength = clist._length;
|
||||
var i;
|
||||
clist._length = list.length;
|
||||
for (i = 0; i < list.length; i++) {
|
||||
clist[i] = list[i];
|
||||
}
|
||||
// Clear/free old entries.
|
||||
for (; i < oldLength; i++) {
|
||||
clist[i] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function handleErrors(token) {
|
||||
token = String(token);
|
||||
if (token === "") {
|
||||
utils.SyntaxError();
|
||||
}
|
||||
if (/[ \t\r\n\f]/.test(token)) {
|
||||
utils.InvalidCharacterError();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
function toArray(clist) {
|
||||
var length = clist._length;
|
||||
var arr = Array(length);
|
||||
for (var i = 0; i < length; i++) {
|
||||
arr[i] = clist[i];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function getList(clist) {
|
||||
var strProp = clist._getString();
|
||||
if (strProp === clist._lastStringValue) {
|
||||
return toArray(clist);
|
||||
}
|
||||
var str = strProp.replace(/(^[ \t\r\n\f]+)|([ \t\r\n\f]+$)/g, '');
|
||||
if (str === "") {
|
||||
return [];
|
||||
} else {
|
||||
var seen = Object.create(null);
|
||||
return str.split(/[ \t\r\n\f]+/g).filter(function(n) {
|
||||
var key = '$' + n;
|
||||
if (seen[key]) { return false; }
|
||||
seen[key] = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
+884
@@ -0,0 +1,884 @@
|
||||
"use strict";
|
||||
module.exports = Document;
|
||||
|
||||
var Node = require('./Node');
|
||||
var NodeList = require('./NodeList');
|
||||
var ContainerNode = require('./ContainerNode');
|
||||
var Element = require('./Element');
|
||||
var Text = require('./Text');
|
||||
var Comment = require('./Comment');
|
||||
var Event = require('./Event');
|
||||
var DocumentFragment = require('./DocumentFragment');
|
||||
var ProcessingInstruction = require('./ProcessingInstruction');
|
||||
var DOMImplementation = require('./DOMImplementation');
|
||||
var TreeWalker = require('./TreeWalker');
|
||||
var NodeIterator = require('./NodeIterator');
|
||||
var NodeFilter = require('./NodeFilter');
|
||||
var URL = require('./URL');
|
||||
var select = require('./select');
|
||||
var events = require('./events');
|
||||
var xml = require('./xmlnames');
|
||||
var html = require('./htmlelts');
|
||||
var svg = require('./svg');
|
||||
var utils = require('./utils');
|
||||
var MUTATE = require('./MutationConstants');
|
||||
var NAMESPACE = utils.NAMESPACE;
|
||||
var isApiWritable = require("./config").isApiWritable;
|
||||
|
||||
function Document(isHTML, address) {
|
||||
ContainerNode.call(this);
|
||||
this.nodeType = Node.DOCUMENT_NODE;
|
||||
this.isHTML = isHTML;
|
||||
this._address = address || 'about:blank';
|
||||
this.readyState = 'loading';
|
||||
this.implementation = new DOMImplementation(this);
|
||||
|
||||
// DOMCore says that documents are always associated with themselves
|
||||
this.ownerDocument = null; // ... but W3C tests expect null
|
||||
this._contentType = isHTML ? 'text/html' : 'application/xml';
|
||||
|
||||
// These will be initialized by our custom versions of
|
||||
// appendChild and insertBefore that override the inherited
|
||||
// Node methods.
|
||||
// XXX: override those methods!
|
||||
this.doctype = null;
|
||||
this.documentElement = null;
|
||||
|
||||
// "Associated inert template document"
|
||||
this._templateDocCache = null;
|
||||
// List of active NodeIterators, see NodeIterator#_preremove()
|
||||
this._nodeIterators = null;
|
||||
|
||||
// Documents are always rooted, by definition
|
||||
this._nid = 1;
|
||||
this._nextnid = 2; // For numbering children of the document
|
||||
this._nodes = [null, this]; // nid to node map
|
||||
|
||||
// This maintains the mapping from element ids to element nodes.
|
||||
// We may need to update this mapping every time a node is rooted
|
||||
// or uprooted, and any time an attribute is added, removed or changed
|
||||
// on a rooted element.
|
||||
this.byId = Object.create(null);
|
||||
|
||||
// This property holds a monotonically increasing value akin to
|
||||
// a timestamp used to record the last modification time of nodes
|
||||
// and their subtrees. See the lastModTime attribute and modify()
|
||||
// method of the Node class. And see FilteredElementList for an example
|
||||
// of the use of lastModTime
|
||||
this.modclock = 0;
|
||||
}
|
||||
|
||||
// Map from lowercase event category names (used as arguments to
|
||||
// createEvent()) to the property name in the impl object of the
|
||||
// event constructor.
|
||||
var supportedEvents = {
|
||||
event: 'Event',
|
||||
customevent: 'CustomEvent',
|
||||
uievent: 'UIEvent',
|
||||
mouseevent: 'MouseEvent'
|
||||
};
|
||||
|
||||
// Certain arguments to document.createEvent() must be treated specially
|
||||
var replacementEvent = {
|
||||
events: 'event',
|
||||
htmlevents: 'event',
|
||||
mouseevents: 'mouseevent',
|
||||
mutationevents: 'mutationevent',
|
||||
uievents: 'uievent'
|
||||
};
|
||||
|
||||
var mirrorAttr = function(f, name, defaultValue) {
|
||||
return {
|
||||
get: function() {
|
||||
var o = f.call(this);
|
||||
if (o) { return o[name]; }
|
||||
return defaultValue;
|
||||
},
|
||||
set: function(value) {
|
||||
var o = f.call(this);
|
||||
if (o) { o[name] = value; }
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/** @spec https://dom.spec.whatwg.org/#validate-and-extract */
|
||||
function validateAndExtract(namespace, qualifiedName) {
|
||||
var prefix, localName, pos;
|
||||
if (namespace==='') { namespace = null; }
|
||||
// See https://github.com/whatwg/dom/issues/671
|
||||
// and https://github.com/whatwg/dom/issues/319
|
||||
if (!xml.isValidQName(qualifiedName)) {
|
||||
utils.InvalidCharacterError();
|
||||
}
|
||||
prefix = null;
|
||||
localName = qualifiedName;
|
||||
|
||||
pos = qualifiedName.indexOf(':');
|
||||
if (pos >= 0) {
|
||||
prefix = qualifiedName.substring(0, pos);
|
||||
localName = qualifiedName.substring(pos+1);
|
||||
}
|
||||
if (prefix !== null && namespace === null) {
|
||||
utils.NamespaceError();
|
||||
}
|
||||
if (prefix === 'xml' && namespace !== NAMESPACE.XML) {
|
||||
utils.NamespaceError();
|
||||
}
|
||||
if ((prefix === 'xmlns' || qualifiedName === 'xmlns') &&
|
||||
namespace !== NAMESPACE.XMLNS) {
|
||||
utils.NamespaceError();
|
||||
}
|
||||
if (namespace === NAMESPACE.XMLNS && !(prefix==='xmlns' || qualifiedName==='xmlns')) {
|
||||
utils.NamespaceError();
|
||||
}
|
||||
return { namespace: namespace, prefix: prefix, localName: localName };
|
||||
}
|
||||
|
||||
Document.prototype = Object.create(ContainerNode.prototype, {
|
||||
// This method allows dom.js to communicate with a renderer
|
||||
// that displays the document in some way
|
||||
// XXX: I should probably move this to the window object
|
||||
_setMutationHandler: { value: function(handler) {
|
||||
this.mutationHandler = handler;
|
||||
}},
|
||||
|
||||
// This method allows dom.js to receive event notifications
|
||||
// from the renderer.
|
||||
// XXX: I should probably move this to the window object
|
||||
_dispatchRendererEvent: { value: function(targetNid, type, details) {
|
||||
var target = this._nodes[targetNid];
|
||||
if (!target) return;
|
||||
target._dispatchEvent(new Event(type, details), true);
|
||||
}},
|
||||
|
||||
nodeName: { value: '#document'},
|
||||
nodeValue: {
|
||||
get: function() {
|
||||
return null;
|
||||
},
|
||||
set: function() {}
|
||||
},
|
||||
|
||||
// XXX: DOMCore may remove documentURI, so it is NYI for now
|
||||
documentURI: { get: function() { return this._address; }, set: utils.nyi },
|
||||
compatMode: { get: function() {
|
||||
// The _quirks property is set by the HTML parser
|
||||
return this._quirks ? 'BackCompat' : 'CSS1Compat';
|
||||
}},
|
||||
|
||||
createTextNode: { value: function(data) {
|
||||
return new Text(this, String(data));
|
||||
}},
|
||||
createComment: { value: function(data) {
|
||||
return new Comment(this, data);
|
||||
}},
|
||||
createDocumentFragment: { value: function() {
|
||||
return new DocumentFragment(this);
|
||||
}},
|
||||
createProcessingInstruction: { value: function(target, data) {
|
||||
if (!xml.isValidName(target) || data.indexOf('?>') !== -1)
|
||||
utils.InvalidCharacterError();
|
||||
return new ProcessingInstruction(this, target, data);
|
||||
}},
|
||||
|
||||
createAttribute: { value: function(localName) {
|
||||
localName = String(localName);
|
||||
if (!xml.isValidName(localName)) utils.InvalidCharacterError();
|
||||
if (this.isHTML) {
|
||||
localName = utils.toASCIILowerCase(localName);
|
||||
}
|
||||
return new Element._Attr(null, localName, null, null, '');
|
||||
}},
|
||||
createAttributeNS: { value: function(namespace, qualifiedName) {
|
||||
// Convert parameter types according to WebIDL
|
||||
namespace =
|
||||
(namespace === null || namespace === undefined || namespace === '') ? null :
|
||||
String(namespace);
|
||||
qualifiedName = String(qualifiedName);
|
||||
var ve = validateAndExtract(namespace, qualifiedName);
|
||||
return new Element._Attr(null, ve.localName, ve.prefix, ve.namespace, '');
|
||||
}},
|
||||
|
||||
createElement: { value: function(localName) {
|
||||
localName = String(localName);
|
||||
if (!xml.isValidName(localName)) utils.InvalidCharacterError();
|
||||
// Per spec, namespace should be HTML namespace if "context object is
|
||||
// an HTML document or context object's content type is
|
||||
// "application/xhtml+xml", and null otherwise.
|
||||
if (this.isHTML) {
|
||||
if (/[A-Z]/.test(localName))
|
||||
localName = utils.toASCIILowerCase(localName);
|
||||
return html.createElement(this, localName, null);
|
||||
} else if (this.contentType === 'application/xhtml+xml') {
|
||||
return html.createElement(this, localName, null);
|
||||
} else {
|
||||
return new Element(this, localName, null, null);
|
||||
}
|
||||
}, writable: isApiWritable },
|
||||
|
||||
createElementNS: { value: function(namespace, qualifiedName) {
|
||||
// Convert parameter types according to WebIDL
|
||||
namespace =
|
||||
(namespace === null || namespace === undefined || namespace === '') ? null :
|
||||
String(namespace);
|
||||
qualifiedName = String(qualifiedName);
|
||||
var ve = validateAndExtract(namespace, qualifiedName);
|
||||
return this._createElementNS(ve.localName, ve.namespace, ve.prefix);
|
||||
}, writable: isApiWritable },
|
||||
|
||||
// This is used directly by HTML parser, which allows it to create
|
||||
// elements with localNames containing ':' and non-default namespaces
|
||||
_createElementNS: { value: function(localName, namespace, prefix) {
|
||||
if (namespace === NAMESPACE.HTML) {
|
||||
return html.createElement(this, localName, prefix);
|
||||
}
|
||||
else if (namespace === NAMESPACE.SVG) {
|
||||
return svg.createElement(this, localName, prefix);
|
||||
}
|
||||
|
||||
return new Element(this, localName, namespace, prefix);
|
||||
}},
|
||||
|
||||
createEvent: { value: function createEvent(interfaceName) {
|
||||
interfaceName = interfaceName.toLowerCase();
|
||||
var name = replacementEvent[interfaceName] || interfaceName;
|
||||
var constructor = events[supportedEvents[name]];
|
||||
|
||||
if (constructor) {
|
||||
var e = new constructor();
|
||||
e._initialized = false;
|
||||
return e;
|
||||
}
|
||||
else {
|
||||
utils.NotSupportedError();
|
||||
}
|
||||
}},
|
||||
|
||||
// See: http://www.w3.org/TR/dom/#dom-document-createtreewalker
|
||||
createTreeWalker: {value: function (root, whatToShow, filter) {
|
||||
if (!root) { throw new TypeError("root argument is required"); }
|
||||
if (!(root instanceof Node)) { throw new TypeError("root not a node"); }
|
||||
whatToShow = whatToShow === undefined ? NodeFilter.SHOW_ALL : (+whatToShow);
|
||||
filter = filter === undefined ? null : filter;
|
||||
|
||||
return new TreeWalker(root, whatToShow, filter);
|
||||
}},
|
||||
|
||||
// See: http://www.w3.org/TR/dom/#dom-document-createnodeiterator
|
||||
createNodeIterator: {value: function (root, whatToShow, filter) {
|
||||
if (!root) { throw new TypeError("root argument is required"); }
|
||||
if (!(root instanceof Node)) { throw new TypeError("root not a node"); }
|
||||
whatToShow = whatToShow === undefined ? NodeFilter.SHOW_ALL : (+whatToShow);
|
||||
filter = filter === undefined ? null : filter;
|
||||
|
||||
return new NodeIterator(root, whatToShow, filter);
|
||||
}},
|
||||
|
||||
_attachNodeIterator: { value: function(ni) {
|
||||
// XXX ideally this should be a weak reference from Document to NodeIterator
|
||||
if (!this._nodeIterators) { this._nodeIterators = []; }
|
||||
this._nodeIterators.push(ni);
|
||||
}},
|
||||
|
||||
_detachNodeIterator: { value: function(ni) {
|
||||
// ni should always be in list of node iterators
|
||||
var idx = this._nodeIterators.indexOf(ni);
|
||||
this._nodeIterators.splice(idx, 1);
|
||||
}},
|
||||
|
||||
_preremoveNodeIterators: { value: function(toBeRemoved) {
|
||||
if (this._nodeIterators) {
|
||||
this._nodeIterators.forEach(function(ni) { ni._preremove(toBeRemoved); });
|
||||
}
|
||||
}},
|
||||
|
||||
// Maintain the documentElement and
|
||||
// doctype properties of the document. Each of the following
|
||||
// methods chains to the Node implementation of the method
|
||||
// to do the actual inserting, removal or replacement.
|
||||
|
||||
_updateDocTypeElement: { value: function _updateDocTypeElement() {
|
||||
this.doctype = this.documentElement = null;
|
||||
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
|
||||
if (kid.nodeType === Node.DOCUMENT_TYPE_NODE)
|
||||
this.doctype = kid;
|
||||
else if (kid.nodeType === Node.ELEMENT_NODE)
|
||||
this.documentElement = kid;
|
||||
}
|
||||
}},
|
||||
|
||||
insertBefore: { value: function insertBefore(child, refChild) {
|
||||
Node.prototype.insertBefore.call(this, child, refChild);
|
||||
this._updateDocTypeElement();
|
||||
return child;
|
||||
}},
|
||||
|
||||
replaceChild: { value: function replaceChild(node, child) {
|
||||
Node.prototype.replaceChild.call(this, node, child);
|
||||
this._updateDocTypeElement();
|
||||
return child;
|
||||
}},
|
||||
|
||||
removeChild: { value: function removeChild(child) {
|
||||
Node.prototype.removeChild.call(this, child);
|
||||
this._updateDocTypeElement();
|
||||
return child;
|
||||
}},
|
||||
|
||||
getElementById: { value: function(id) {
|
||||
var n = this.byId[id];
|
||||
if (!n) return null;
|
||||
if (n instanceof MultiId) { // there was more than one element with this id
|
||||
return n.getFirst();
|
||||
}
|
||||
return n;
|
||||
}},
|
||||
|
||||
_hasMultipleElementsWithId: { value: function(id) {
|
||||
// Used internally by querySelectorAll optimization
|
||||
return (this.byId[id] instanceof MultiId);
|
||||
}},
|
||||
|
||||
// Just copy this method from the Element prototype
|
||||
getElementsByName: { value: Element.prototype.getElementsByName },
|
||||
getElementsByTagName: { value: Element.prototype.getElementsByTagName },
|
||||
getElementsByTagNameNS: { value: Element.prototype.getElementsByTagNameNS },
|
||||
getElementsByClassName: { value: Element.prototype.getElementsByClassName },
|
||||
|
||||
adoptNode: { value: function adoptNode(node) {
|
||||
if (node.nodeType === Node.DOCUMENT_NODE) utils.NotSupportedError();
|
||||
if (node.nodeType === Node.ATTRIBUTE_NODE) { return node; }
|
||||
|
||||
if (node.parentNode) node.parentNode.removeChild(node);
|
||||
|
||||
if (node.ownerDocument !== this)
|
||||
recursivelySetOwner(node, this);
|
||||
|
||||
return node;
|
||||
}},
|
||||
|
||||
importNode: { value: function importNode(node, deep) {
|
||||
return this.adoptNode(node.cloneNode(deep));
|
||||
}, writable: isApiWritable },
|
||||
|
||||
// The following attributes and methods are from the HTML spec
|
||||
origin: { get: function origin() { return null; } },
|
||||
characterSet: { get: function characterSet() { return "UTF-8"; } },
|
||||
contentType: { get: function contentType() { return this._contentType; } },
|
||||
URL: { get: function URL() { return this._address; } },
|
||||
domain: { get: utils.nyi, set: utils.nyi },
|
||||
referrer: { get: utils.nyi },
|
||||
cookie: { get: utils.nyi, set: utils.nyi },
|
||||
lastModified: { get: utils.nyi },
|
||||
location: {
|
||||
get: function() {
|
||||
return this.defaultView ? this.defaultView.location : null; // gh #75
|
||||
},
|
||||
set: utils.nyi
|
||||
},
|
||||
_titleElement: {
|
||||
get: function() {
|
||||
// The title element of a document is the first title element in the
|
||||
// document in tree order, if there is one, or null otherwise.
|
||||
return this.getElementsByTagName('title').item(0) || null;
|
||||
}
|
||||
},
|
||||
title: {
|
||||
get: function() {
|
||||
var elt = this._titleElement;
|
||||
// The child text content of the title element, or '' if null.
|
||||
var value = elt ? elt.textContent : '';
|
||||
// Strip and collapse whitespace in value
|
||||
return value.replace(/[ \t\n\r\f]+/g, ' ').replace(/(^ )|( $)/g, '');
|
||||
},
|
||||
set: function(value) {
|
||||
var elt = this._titleElement;
|
||||
var head = this.head;
|
||||
if (!elt && !head) { return; /* according to spec */ }
|
||||
if (!elt) {
|
||||
elt = this.createElement('title');
|
||||
head.appendChild(elt);
|
||||
}
|
||||
elt.textContent = value;
|
||||
}
|
||||
},
|
||||
dir: mirrorAttr(function() {
|
||||
var htmlElement = this.documentElement;
|
||||
if (htmlElement && htmlElement.tagName === 'HTML') { return htmlElement; }
|
||||
}, 'dir', ''),
|
||||
fgColor: mirrorAttr(function() { return this.body; }, 'text', ''),
|
||||
linkColor: mirrorAttr(function() { return this.body; }, 'link', ''),
|
||||
vlinkColor: mirrorAttr(function() { return this.body; }, 'vLink', ''),
|
||||
alinkColor: mirrorAttr(function() { return this.body; }, 'aLink', ''),
|
||||
bgColor: mirrorAttr(function() { return this.body; }, 'bgColor', ''),
|
||||
|
||||
// Historical aliases of Document#characterSet
|
||||
charset: { get: function() { return this.characterSet; } },
|
||||
inputEncoding: { get: function() { return this.characterSet; } },
|
||||
|
||||
scrollingElement: {
|
||||
get: function() {
|
||||
return this._quirks ? this.body : this.documentElement;
|
||||
}
|
||||
},
|
||||
|
||||
// Return the first <body> child of the document element.
|
||||
// XXX For now, setting this attribute is not implemented.
|
||||
body: {
|
||||
get: function() {
|
||||
return namedHTMLChild(this.documentElement, 'body');
|
||||
},
|
||||
set: utils.nyi
|
||||
},
|
||||
// Return the first <head> child of the document element.
|
||||
head: { get: function() {
|
||||
return namedHTMLChild(this.documentElement, 'head');
|
||||
}},
|
||||
images: { get: utils.nyi },
|
||||
embeds: { get: utils.nyi },
|
||||
plugins: { get: utils.nyi },
|
||||
links: { get: utils.nyi },
|
||||
forms: { get: utils.nyi },
|
||||
scripts: { get: utils.nyi },
|
||||
applets: { get: function() { return []; } },
|
||||
activeElement: { get: function() { return null; } },
|
||||
innerHTML: {
|
||||
get: function() { return this.serialize(); },
|
||||
set: utils.nyi
|
||||
},
|
||||
outerHTML: {
|
||||
get: function() { return this.serialize(); },
|
||||
set: utils.nyi
|
||||
},
|
||||
|
||||
write: { value: function(args) {
|
||||
if (!this.isHTML) utils.InvalidStateError();
|
||||
|
||||
// XXX: still have to implement the ignore part
|
||||
if (!this._parser /* && this._ignore_destructive_writes > 0 */ )
|
||||
return;
|
||||
|
||||
if (!this._parser) {
|
||||
// XXX call document.open, etc.
|
||||
}
|
||||
|
||||
var s = arguments.join('');
|
||||
|
||||
// If the Document object's reload override flag is set, then
|
||||
// append the string consisting of the concatenation of all the
|
||||
// arguments to the method to the Document's reload override
|
||||
// buffer.
|
||||
// XXX: don't know what this is about. Still have to do it
|
||||
|
||||
// If there is no pending parsing-blocking script, have the
|
||||
// tokenizer process the characters that were inserted, one at a
|
||||
// time, processing resulting tokens as they are emitted, and
|
||||
// stopping when the tokenizer reaches the insertion point or when
|
||||
// the processing of the tokenizer is aborted by the tree
|
||||
// construction stage (this can happen if a script end tag token is
|
||||
// emitted by the tokenizer).
|
||||
|
||||
// XXX: still have to do the above. Sounds as if we don't
|
||||
// always call parse() here. If we're blocked, then we just
|
||||
// insert the text into the stream but don't parse it reentrantly...
|
||||
|
||||
// Invoke the parser reentrantly
|
||||
this._parser.parse(s);
|
||||
}},
|
||||
|
||||
writeln: { value: function writeln(args) {
|
||||
this.write(Array.prototype.join.call(arguments, '') + '\n');
|
||||
}},
|
||||
|
||||
open: { value: function() {
|
||||
this.documentElement = null;
|
||||
}},
|
||||
|
||||
close: { value: function() {
|
||||
this.readyState = 'interactive';
|
||||
this._dispatchEvent(new Event('readystatechange'), true);
|
||||
this._dispatchEvent(new Event('DOMContentLoaded'), true);
|
||||
this.readyState = 'complete';
|
||||
this._dispatchEvent(new Event('readystatechange'), true);
|
||||
if (this.defaultView) {
|
||||
this.defaultView._dispatchEvent(new Event('load'), true);
|
||||
}
|
||||
}},
|
||||
|
||||
// Utility methods
|
||||
clone: { value: function clone() {
|
||||
var d = new Document(this.isHTML, this._address);
|
||||
d._quirks = this._quirks;
|
||||
d._contentType = this._contentType;
|
||||
return d;
|
||||
}},
|
||||
|
||||
// We need to adopt the nodes if we do a deep clone
|
||||
cloneNode: { value: function cloneNode(deep) {
|
||||
var clone = Node.prototype.cloneNode.call(this, false);
|
||||
if (deep) {
|
||||
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
|
||||
clone._appendChild(clone.importNode(kid, true));
|
||||
}
|
||||
}
|
||||
clone._updateDocTypeElement();
|
||||
return clone;
|
||||
}},
|
||||
|
||||
isEqual: { value: function isEqual(n) {
|
||||
// Any two documents are shallowly equal.
|
||||
// Node.isEqualNode will also test the children
|
||||
return true;
|
||||
}},
|
||||
|
||||
// Implementation-specific function. Called when a text, comment,
|
||||
// or pi value changes.
|
||||
mutateValue: { value: function(node) {
|
||||
if (this.mutationHandler) {
|
||||
this.mutationHandler({
|
||||
type: MUTATE.VALUE,
|
||||
target: node,
|
||||
data: node.data
|
||||
});
|
||||
}
|
||||
}},
|
||||
|
||||
// Invoked when an attribute's value changes. Attr holds the new
|
||||
// value. oldval is the old value. Attribute mutations can also
|
||||
// involve changes to the prefix (and therefore the qualified name)
|
||||
mutateAttr: { value: function(attr, oldval) {
|
||||
// Manage id->element mapping for getElementsById()
|
||||
// XXX: this special case id handling should not go here,
|
||||
// but in the attribute declaration for the id attribute
|
||||
/*
|
||||
if (attr.localName === 'id' && attr.namespaceURI === null) {
|
||||
if (oldval) delId(oldval, attr.ownerElement);
|
||||
addId(attr.value, attr.ownerElement);
|
||||
}
|
||||
*/
|
||||
if (this.mutationHandler) {
|
||||
this.mutationHandler({
|
||||
type: MUTATE.ATTR,
|
||||
target: attr.ownerElement,
|
||||
attr: attr
|
||||
});
|
||||
}
|
||||
}},
|
||||
|
||||
// Used by removeAttribute and removeAttributeNS for attributes.
|
||||
mutateRemoveAttr: { value: function(attr) {
|
||||
/*
|
||||
* This is now handled in Attributes.js
|
||||
// Manage id to element mapping
|
||||
if (attr.localName === 'id' && attr.namespaceURI === null) {
|
||||
this.delId(attr.value, attr.ownerElement);
|
||||
}
|
||||
*/
|
||||
if (this.mutationHandler) {
|
||||
this.mutationHandler({
|
||||
type: MUTATE.REMOVE_ATTR,
|
||||
target: attr.ownerElement,
|
||||
attr: attr
|
||||
});
|
||||
}
|
||||
}},
|
||||
|
||||
// Called by Node.removeChild, etc. to remove a rooted element from
|
||||
// the tree. Only needs to generate a single mutation event when a
|
||||
// node is removed, but must recursively mark all descendants as not
|
||||
// rooted.
|
||||
mutateRemove: { value: function(node) {
|
||||
// Send a single mutation event
|
||||
if (this.mutationHandler) {
|
||||
this.mutationHandler({
|
||||
type: MUTATE.REMOVE,
|
||||
target: node.parentNode,
|
||||
node: node
|
||||
});
|
||||
}
|
||||
|
||||
// Mark this and all descendants as not rooted
|
||||
recursivelyUproot(node);
|
||||
}},
|
||||
|
||||
// Called when a new element becomes rooted. It must recursively
|
||||
// generate mutation events for each of the children, and mark them all
|
||||
// as rooted.
|
||||
mutateInsert: { value: function(node) {
|
||||
// Mark node and its descendants as rooted
|
||||
recursivelyRoot(node);
|
||||
|
||||
// Send a single mutation event
|
||||
if (this.mutationHandler) {
|
||||
this.mutationHandler({
|
||||
type: MUTATE.INSERT,
|
||||
target: node.parentNode,
|
||||
node: node
|
||||
});
|
||||
}
|
||||
}},
|
||||
|
||||
// Called when a rooted element is moved within the document
|
||||
mutateMove: { value: function(node) {
|
||||
if (this.mutationHandler) {
|
||||
this.mutationHandler({
|
||||
type: MUTATE.MOVE,
|
||||
target: node
|
||||
});
|
||||
}
|
||||
}},
|
||||
|
||||
|
||||
// Add a mapping from id to n for n.ownerDocument
|
||||
addId: { value: function addId(id, n) {
|
||||
var val = this.byId[id];
|
||||
if (!val) {
|
||||
this.byId[id] = n;
|
||||
}
|
||||
else {
|
||||
// TODO: Add a way to opt-out console warnings
|
||||
//console.warn('Duplicate element id ' + id);
|
||||
if (!(val instanceof MultiId)) {
|
||||
val = new MultiId(val);
|
||||
this.byId[id] = val;
|
||||
}
|
||||
val.add(n);
|
||||
}
|
||||
}},
|
||||
|
||||
// Delete the mapping from id to n for n.ownerDocument
|
||||
delId: { value: function delId(id, n) {
|
||||
var val = this.byId[id];
|
||||
utils.assert(val);
|
||||
|
||||
if (val instanceof MultiId) {
|
||||
val.del(n);
|
||||
if (val.length === 1) { // convert back to a single node
|
||||
this.byId[id] = val.downgrade();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.byId[id] = undefined;
|
||||
}
|
||||
}},
|
||||
|
||||
_resolve: { value: function(href) {
|
||||
//XXX: Cache the URL
|
||||
return new URL(this._documentBaseURL).resolve(href);
|
||||
}},
|
||||
|
||||
_documentBaseURL: { get: function() {
|
||||
// XXX: This is not implemented correctly yet
|
||||
var url = this._address;
|
||||
if (url === 'about:blank') url = '/';
|
||||
|
||||
var base = this.querySelector('base[href]');
|
||||
if (base) {
|
||||
return new URL(url).resolve(base.getAttribute('href'));
|
||||
}
|
||||
return url;
|
||||
|
||||
// The document base URL of a Document object is the
|
||||
// absolute URL obtained by running these substeps:
|
||||
|
||||
// Let fallback base url be the document's address.
|
||||
|
||||
// If fallback base url is about:blank, and the
|
||||
// Document's browsing context has a creator browsing
|
||||
// context, then let fallback base url be the document
|
||||
// base URL of the creator Document instead.
|
||||
|
||||
// If the Document is an iframe srcdoc document, then
|
||||
// let fallback base url be the document base URL of
|
||||
// the Document's browsing context's browsing context
|
||||
// container's Document instead.
|
||||
|
||||
// If there is no base element that has an href
|
||||
// attribute, then the document base URL is fallback
|
||||
// base url; abort these steps. Otherwise, let url be
|
||||
// the value of the href attribute of the first such
|
||||
// element.
|
||||
|
||||
// Resolve url relative to fallback base url (thus,
|
||||
// the base href attribute isn't affected by xml:base
|
||||
// attributes).
|
||||
|
||||
// The document base URL is the result of the previous
|
||||
// step if it was successful; otherwise it is fallback
|
||||
// base url.
|
||||
}},
|
||||
|
||||
_templateDoc: { get: function() {
|
||||
if (!this._templateDocCache) {
|
||||
// "associated inert template document"
|
||||
var newDoc = new Document(this.isHTML, this._address);
|
||||
this._templateDocCache = newDoc._templateDocCache = newDoc;
|
||||
}
|
||||
return this._templateDocCache;
|
||||
}},
|
||||
|
||||
querySelector: { value: function(selector) {
|
||||
return select(selector, this)[0];
|
||||
}},
|
||||
|
||||
querySelectorAll: { value: function(selector) {
|
||||
var nodes = select(selector, this);
|
||||
return nodes.item ? nodes : new NodeList(nodes);
|
||||
}}
|
||||
|
||||
});
|
||||
|
||||
|
||||
var eventHandlerTypes = [
|
||||
'abort', 'canplay', 'canplaythrough', 'change', 'click', 'contextmenu',
|
||||
'cuechange', 'dblclick', 'drag', 'dragend', 'dragenter', 'dragleave',
|
||||
'dragover', 'dragstart', 'drop', 'durationchange', 'emptied', 'ended',
|
||||
'input', 'invalid', 'keydown', 'keypress', 'keyup', 'loadeddata',
|
||||
'loadedmetadata', 'loadstart', 'mousedown', 'mousemove', 'mouseout',
|
||||
'mouseover', 'mouseup', 'mousewheel', 'pause', 'play', 'playing',
|
||||
'progress', 'ratechange', 'readystatechange', 'reset', 'seeked',
|
||||
'seeking', 'select', 'show', 'stalled', 'submit', 'suspend',
|
||||
'timeupdate', 'volumechange', 'waiting',
|
||||
|
||||
'blur', 'error', 'focus', 'load', 'scroll'
|
||||
];
|
||||
|
||||
// Add event handler idl attribute getters and setters to Document
|
||||
eventHandlerTypes.forEach(function(type) {
|
||||
// Define the event handler registration IDL attribute for this type
|
||||
Object.defineProperty(Document.prototype, 'on' + type, {
|
||||
get: function() {
|
||||
return this._getEventHandler(type);
|
||||
},
|
||||
set: function(v) {
|
||||
this._setEventHandler(type, v);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function namedHTMLChild(parent, name) {
|
||||
if (parent && parent.isHTML) {
|
||||
for (var kid = parent.firstChild; kid !== null; kid = kid.nextSibling) {
|
||||
if (kid.nodeType === Node.ELEMENT_NODE &&
|
||||
kid.localName === name &&
|
||||
kid.namespaceURI === NAMESPACE.HTML) {
|
||||
return kid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function root(n) {
|
||||
n._nid = n.ownerDocument._nextnid++;
|
||||
n.ownerDocument._nodes[n._nid] = n;
|
||||
// Manage id to element mapping
|
||||
if (n.nodeType === Node.ELEMENT_NODE) {
|
||||
var id = n.getAttribute('id');
|
||||
if (id) n.ownerDocument.addId(id, n);
|
||||
|
||||
// Script elements need to know when they're inserted
|
||||
// into the document
|
||||
if (n._roothook) n._roothook();
|
||||
}
|
||||
}
|
||||
|
||||
function uproot(n) {
|
||||
// Manage id to element mapping
|
||||
if (n.nodeType === Node.ELEMENT_NODE) {
|
||||
var id = n.getAttribute('id');
|
||||
if (id) n.ownerDocument.delId(id, n);
|
||||
}
|
||||
n.ownerDocument._nodes[n._nid] = undefined;
|
||||
n._nid = undefined;
|
||||
}
|
||||
|
||||
function recursivelyRoot(node) {
|
||||
root(node);
|
||||
// XXX:
|
||||
// accessing childNodes on a leaf node creates a new array the
|
||||
// first time, so be careful to write this loop so that it
|
||||
// doesn't do that. node is polymorphic, so maybe this is hard to
|
||||
// optimize? Try switching on nodeType?
|
||||
/*
|
||||
if (node.hasChildNodes()) {
|
||||
var kids = node.childNodes;
|
||||
for(var i = 0, n = kids.length; i < n; i++)
|
||||
recursivelyRoot(kids[i]);
|
||||
}
|
||||
*/
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
for (var kid = node.firstChild; kid !== null; kid = kid.nextSibling)
|
||||
recursivelyRoot(kid);
|
||||
}
|
||||
}
|
||||
|
||||
function recursivelyUproot(node) {
|
||||
uproot(node);
|
||||
for (var kid = node.firstChild; kid !== null; kid = kid.nextSibling)
|
||||
recursivelyUproot(kid);
|
||||
}
|
||||
|
||||
function recursivelySetOwner(node, owner) {
|
||||
node.ownerDocument = owner;
|
||||
node._lastModTime = undefined; // mod times are document-based
|
||||
if (Object.prototype.hasOwnProperty.call(node, '_tagName')) {
|
||||
node._tagName = undefined; // Element subclasses might need to change case
|
||||
}
|
||||
for (var kid = node.firstChild; kid !== null; kid = kid.nextSibling)
|
||||
recursivelySetOwner(kid, owner);
|
||||
}
|
||||
|
||||
// A class for storing multiple nodes with the same ID
|
||||
function MultiId(node) {
|
||||
this.nodes = Object.create(null);
|
||||
this.nodes[node._nid] = node;
|
||||
this.length = 1;
|
||||
this.firstNode = undefined;
|
||||
}
|
||||
|
||||
// Add a node to the list, with O(1) time
|
||||
MultiId.prototype.add = function(node) {
|
||||
if (!this.nodes[node._nid]) {
|
||||
this.nodes[node._nid] = node;
|
||||
this.length++;
|
||||
this.firstNode = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// Remove a node from the list, with O(1) time
|
||||
MultiId.prototype.del = function(node) {
|
||||
if (this.nodes[node._nid]) {
|
||||
delete this.nodes[node._nid];
|
||||
this.length--;
|
||||
this.firstNode = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// Get the first node from the list, in the document order
|
||||
// Takes O(N) time in the size of the list, with a cache that is invalidated
|
||||
// when the list is modified.
|
||||
MultiId.prototype.getFirst = function() {
|
||||
/* jshint bitwise: false */
|
||||
if (!this.firstNode) {
|
||||
var nid;
|
||||
for (nid in this.nodes) {
|
||||
if (this.firstNode === undefined ||
|
||||
this.firstNode.compareDocumentPosition(this.nodes[nid]) & Node.DOCUMENT_POSITION_PRECEDING) {
|
||||
this.firstNode = this.nodes[nid];
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.firstNode;
|
||||
};
|
||||
|
||||
// If there is only one node left, return it. Otherwise return "this".
|
||||
MultiId.prototype.downgrade = function() {
|
||||
if (this.length === 1) {
|
||||
var nid;
|
||||
for (nid in this.nodes) {
|
||||
return this.nodes[nid];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
module.exports = DocumentFragment;
|
||||
|
||||
var Node = require('./Node');
|
||||
var NodeList = require('./NodeList');
|
||||
var ContainerNode = require('./ContainerNode');
|
||||
var Element = require('./Element');
|
||||
var select = require('./select');
|
||||
var utils = require('./utils');
|
||||
|
||||
function DocumentFragment(doc) {
|
||||
ContainerNode.call(this);
|
||||
this.nodeType = Node.DOCUMENT_FRAGMENT_NODE;
|
||||
this.ownerDocument = doc;
|
||||
}
|
||||
|
||||
DocumentFragment.prototype = Object.create(ContainerNode.prototype, {
|
||||
nodeName: { value: '#document-fragment' },
|
||||
nodeValue: {
|
||||
get: function() {
|
||||
return null;
|
||||
},
|
||||
set: function() {}
|
||||
},
|
||||
// Copy the text content getter/setter from Element
|
||||
textContent: Object.getOwnPropertyDescriptor(Element.prototype, 'textContent'),
|
||||
|
||||
// Copy the text content getter/setter from Element
|
||||
innerText: Object.getOwnPropertyDescriptor(Element.prototype, 'innerText'),
|
||||
|
||||
querySelector: { value: function(selector) {
|
||||
// implement in terms of querySelectorAll
|
||||
var nodes = this.querySelectorAll(selector);
|
||||
return nodes.length ? nodes[0] : null;
|
||||
}},
|
||||
querySelectorAll: { value: function(selector) {
|
||||
// create a context
|
||||
var context = Object.create(this);
|
||||
// add some methods to the context for zest implementation, without
|
||||
// adding them to the public DocumentFragment API
|
||||
context.isHTML = true; // in HTML namespace (case-insensitive match)
|
||||
context.getElementsByTagName = Element.prototype.getElementsByTagName;
|
||||
context.nextElement =
|
||||
Object.getOwnPropertyDescriptor(Element.prototype, 'firstElementChild').
|
||||
get;
|
||||
// invoke zest
|
||||
var nodes = select(selector, context);
|
||||
return nodes.item ? nodes : new NodeList(nodes);
|
||||
}},
|
||||
|
||||
// Utility methods
|
||||
clone: { value: function clone() {
|
||||
return new DocumentFragment(this.ownerDocument);
|
||||
}},
|
||||
isEqual: { value: function isEqual(n) {
|
||||
// Any two document fragments are shallowly equal.
|
||||
// Node.isEqualNode() will test their children for equality
|
||||
return true;
|
||||
}},
|
||||
|
||||
// Non-standard, but useful (github issue #73)
|
||||
innerHTML: {
|
||||
get: function() { return this.serialize(); },
|
||||
set: utils.nyi
|
||||
},
|
||||
outerHTML: {
|
||||
get: function() { return this.serialize(); },
|
||||
set: utils.nyi
|
||||
},
|
||||
|
||||
});
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
module.exports = DocumentType;
|
||||
|
||||
var Node = require('./Node');
|
||||
var Leaf = require('./Leaf');
|
||||
var ChildNode = require('./ChildNode');
|
||||
|
||||
function DocumentType(ownerDocument, name, publicId, systemId) {
|
||||
Leaf.call(this);
|
||||
this.nodeType = Node.DOCUMENT_TYPE_NODE;
|
||||
this.ownerDocument = ownerDocument || null;
|
||||
this.name = name;
|
||||
this.publicId = publicId || "";
|
||||
this.systemId = systemId || "";
|
||||
}
|
||||
|
||||
DocumentType.prototype = Object.create(Leaf.prototype, {
|
||||
nodeName: { get: function() { return this.name; }},
|
||||
nodeValue: {
|
||||
get: function() { return null; },
|
||||
set: function() {}
|
||||
},
|
||||
|
||||
// Utility methods
|
||||
clone: { value: function clone() {
|
||||
return new DocumentType(this.ownerDocument, this.name, this.publicId, this.systemId);
|
||||
}},
|
||||
|
||||
isEqual: { value: function isEqual(n) {
|
||||
return this.name === n.name &&
|
||||
this.publicId === n.publicId &&
|
||||
this.systemId === n.systemId;
|
||||
}}
|
||||
});
|
||||
|
||||
Object.defineProperties(DocumentType.prototype, ChildNode);
|
||||
+1228
File diff suppressed because it is too large
Load Diff
+66
@@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
module.exports = Event;
|
||||
|
||||
Event.CAPTURING_PHASE = 1;
|
||||
Event.AT_TARGET = 2;
|
||||
Event.BUBBLING_PHASE = 3;
|
||||
|
||||
function Event(type, dictionary) {
|
||||
// Initialize basic event properties
|
||||
this.type = '';
|
||||
this.target = null;
|
||||
this.currentTarget = null;
|
||||
this.eventPhase = Event.AT_TARGET;
|
||||
this.bubbles = false;
|
||||
this.cancelable = false;
|
||||
this.isTrusted = false;
|
||||
this.defaultPrevented = false;
|
||||
this.timeStamp = Date.now();
|
||||
|
||||
// Initialize internal flags
|
||||
// XXX: Would it be better to inherit these defaults from the prototype?
|
||||
this._propagationStopped = false;
|
||||
this._immediatePropagationStopped = false;
|
||||
this._initialized = true;
|
||||
this._dispatching = false;
|
||||
|
||||
// Now initialize based on the constructor arguments (if any)
|
||||
if (type) this.type = type;
|
||||
if (dictionary) {
|
||||
for(var p in dictionary) {
|
||||
this[p] = dictionary[p];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Event.prototype = Object.create(Object.prototype, {
|
||||
constructor: { value: Event },
|
||||
stopPropagation: { value: function stopPropagation() {
|
||||
this._propagationStopped = true;
|
||||
}},
|
||||
|
||||
stopImmediatePropagation: { value: function stopImmediatePropagation() {
|
||||
this._propagationStopped = true;
|
||||
this._immediatePropagationStopped = true;
|
||||
}},
|
||||
|
||||
preventDefault: { value: function preventDefault() {
|
||||
if (this.cancelable) this.defaultPrevented = true;
|
||||
}},
|
||||
|
||||
initEvent: { value: function initEvent(type, bubbles, cancelable) {
|
||||
this._initialized = true;
|
||||
if (this._dispatching) return;
|
||||
|
||||
this._propagationStopped = false;
|
||||
this._immediatePropagationStopped = false;
|
||||
this.defaultPrevented = false;
|
||||
this.isTrusted = false;
|
||||
|
||||
this.target = null;
|
||||
this.type = type;
|
||||
this.bubbles = bubbles;
|
||||
this.cancelable = cancelable;
|
||||
}},
|
||||
|
||||
});
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
"use strict";
|
||||
var Event = require('./Event');
|
||||
var MouseEvent = require('./MouseEvent');
|
||||
var utils = require('./utils');
|
||||
|
||||
module.exports = EventTarget;
|
||||
|
||||
function EventTarget() {}
|
||||
|
||||
EventTarget.prototype = {
|
||||
// XXX
|
||||
// See WebIDL §4.8 for details on object event handlers
|
||||
// and how they should behave. We actually have to accept
|
||||
// any object to addEventListener... Can't type check it.
|
||||
// on registration.
|
||||
|
||||
// XXX:
|
||||
// Capturing event listeners are sort of rare. I think I can optimize
|
||||
// them so that dispatchEvent can skip the capturing phase (or much of
|
||||
// it). Each time a capturing listener is added, increment a flag on
|
||||
// the target node and each of its ancestors. Decrement when removed.
|
||||
// And update the counter when nodes are added and removed from the
|
||||
// tree as well. Then, in dispatch event, the capturing phase can
|
||||
// abort if it sees any node with a zero count.
|
||||
addEventListener: function addEventListener(type, listener, capture) {
|
||||
if (!listener) return;
|
||||
if (capture === undefined) capture = false;
|
||||
if (!this._listeners) this._listeners = Object.create(null);
|
||||
if (!this._listeners[type]) this._listeners[type] = [];
|
||||
var list = this._listeners[type];
|
||||
|
||||
// If this listener has already been registered, just return
|
||||
for(var i = 0, n = list.length; i < n; i++) {
|
||||
var l = list[i];
|
||||
if (l.listener === listener && l.capture === capture)
|
||||
return;
|
||||
}
|
||||
|
||||
// Add an object to the list of listeners
|
||||
var obj = { listener: listener, capture: capture };
|
||||
if (typeof listener === 'function') obj.f = listener;
|
||||
list.push(obj);
|
||||
},
|
||||
|
||||
removeEventListener: function removeEventListener(type,
|
||||
listener,
|
||||
capture) {
|
||||
if (capture === undefined) capture = false;
|
||||
if (this._listeners) {
|
||||
var list = this._listeners[type];
|
||||
if (list) {
|
||||
// Find the listener in the list and remove it
|
||||
for(var i = 0, n = list.length; i < n; i++) {
|
||||
var l = list[i];
|
||||
if (l.listener === listener && l.capture === capture) {
|
||||
if (list.length === 1) {
|
||||
this._listeners[type] = undefined;
|
||||
}
|
||||
else {
|
||||
list.splice(i, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// This is the public API for dispatching untrusted public events.
|
||||
// See _dispatchEvent for the implementation
|
||||
dispatchEvent: function dispatchEvent(event) {
|
||||
// Dispatch an untrusted event
|
||||
return this._dispatchEvent(event, false);
|
||||
},
|
||||
|
||||
//
|
||||
// See DOMCore §4.4
|
||||
// XXX: I'll probably need another version of this method for
|
||||
// internal use, one that does not set isTrusted to false.
|
||||
// XXX: see Document._dispatchEvent: perhaps that and this could
|
||||
// call a common internal function with different settings of
|
||||
// a trusted boolean argument
|
||||
//
|
||||
// XXX:
|
||||
// The spec has changed in how to deal with handlers registered
|
||||
// on idl or content attributes rather than with addEventListener.
|
||||
// Used to say that they always ran first. That's how webkit does it
|
||||
// Spec now says that they run in a position determined by
|
||||
// when they were first set. FF does it that way. See:
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#event-handlers
|
||||
//
|
||||
_dispatchEvent: function _dispatchEvent(event, trusted) {
|
||||
if (typeof trusted !== 'boolean') trusted = false;
|
||||
function invoke(target, event) {
|
||||
var type = event.type, phase = event.eventPhase;
|
||||
event.currentTarget = target;
|
||||
|
||||
// If there was an individual handler defined, invoke it first
|
||||
// XXX: see comment above: this shouldn't always be first.
|
||||
if (phase !== Event.CAPTURING_PHASE &&
|
||||
target._handlers && target._handlers[type])
|
||||
{
|
||||
var handler = target._handlers[type];
|
||||
var rv;
|
||||
if (typeof handler === 'function') {
|
||||
rv=handler.call(event.currentTarget, event);
|
||||
}
|
||||
else {
|
||||
var f = handler.handleEvent;
|
||||
if (typeof f !== 'function')
|
||||
throw new TypeError('handleEvent property of ' +
|
||||
'event handler object is' +
|
||||
'not a function.');
|
||||
rv=f.call(handler, event);
|
||||
}
|
||||
|
||||
switch(event.type) {
|
||||
case 'mouseover':
|
||||
if (rv === true) // Historical baggage
|
||||
event.preventDefault();
|
||||
break;
|
||||
case 'beforeunload':
|
||||
// XXX: eventually we need a special case here
|
||||
/* falls through */
|
||||
default:
|
||||
if (rv === false)
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now invoke list list of listeners for this target and type
|
||||
var list = target._listeners && target._listeners[type];
|
||||
if (!list) return;
|
||||
list = list.slice();
|
||||
for(var i = 0, n = list.length; i < n; i++) {
|
||||
if (event._immediatePropagationStopped) return;
|
||||
var l = list[i];
|
||||
if ((phase === Event.CAPTURING_PHASE && !l.capture) ||
|
||||
(phase === Event.BUBBLING_PHASE && l.capture))
|
||||
continue;
|
||||
if (l.f) {
|
||||
l.f.call(event.currentTarget, event);
|
||||
}
|
||||
else {
|
||||
var fn = l.listener.handleEvent;
|
||||
if (typeof fn !== 'function')
|
||||
throw new TypeError('handleEvent property of event listener object is not a function.');
|
||||
fn.call(l.listener, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!event._initialized || event._dispatching) utils.InvalidStateError();
|
||||
event.isTrusted = trusted;
|
||||
|
||||
// Begin dispatching the event now
|
||||
event._dispatching = true;
|
||||
event.target = this;
|
||||
|
||||
// Build the list of targets for the capturing and bubbling phases
|
||||
// XXX: we'll eventually have to add Window to this list.
|
||||
var ancestors = [];
|
||||
for(var n = this.parentNode; n; n = n.parentNode)
|
||||
ancestors.push(n);
|
||||
|
||||
// Capturing phase
|
||||
event.eventPhase = Event.CAPTURING_PHASE;
|
||||
for(var i = ancestors.length-1; i >= 0; i--) {
|
||||
invoke(ancestors[i], event);
|
||||
if (event._propagationStopped) break;
|
||||
}
|
||||
|
||||
// At target phase
|
||||
if (!event._propagationStopped) {
|
||||
event.eventPhase = Event.AT_TARGET;
|
||||
invoke(this, event);
|
||||
}
|
||||
|
||||
// Bubbling phase
|
||||
if (event.bubbles && !event._propagationStopped) {
|
||||
event.eventPhase = Event.BUBBLING_PHASE;
|
||||
for(var ii = 0, nn = ancestors.length; ii < nn; ii++) {
|
||||
invoke(ancestors[ii], event);
|
||||
if (event._propagationStopped) break;
|
||||
}
|
||||
}
|
||||
|
||||
event._dispatching = false;
|
||||
event.eventPhase = Event.AT_TARGET;
|
||||
event.currentTarget = null;
|
||||
|
||||
// Deal with mouse events and figure out when
|
||||
// a click has happened
|
||||
if (trusted && !event.defaultPrevented && event instanceof MouseEvent) {
|
||||
switch(event.type) {
|
||||
case 'mousedown':
|
||||
this._armed = {
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
t: event.timeStamp
|
||||
};
|
||||
break;
|
||||
case 'mouseout':
|
||||
case 'mouseover':
|
||||
this._armed = null;
|
||||
break;
|
||||
case 'mouseup':
|
||||
if (this._isClick(event)) this._doClick(event);
|
||||
this._armed = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return !event.defaultPrevented;
|
||||
},
|
||||
|
||||
// Determine whether a click occurred
|
||||
// XXX We don't support double clicks for now
|
||||
_isClick: function(event) {
|
||||
return (this._armed !== null &&
|
||||
event.type === 'mouseup' &&
|
||||
event.isTrusted &&
|
||||
event.button === 0 &&
|
||||
event.timeStamp - this._armed.t < 1000 &&
|
||||
Math.abs(event.clientX - this._armed.x) < 10 &&
|
||||
Math.abs(event.clientY - this._armed.Y) < 10);
|
||||
},
|
||||
|
||||
// Clicks are handled like this:
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#interactive-content-0
|
||||
//
|
||||
// Note that this method is similar to the HTMLElement.click() method
|
||||
// The event argument must be the trusted mouseup event
|
||||
_doClick: function(event) {
|
||||
if (this._click_in_progress) return;
|
||||
this._click_in_progress = true;
|
||||
|
||||
// Find the nearest enclosing element that is activatable
|
||||
// An element is activatable if it has a
|
||||
// _post_click_activation_steps hook
|
||||
var activated = this;
|
||||
while(activated && !activated._post_click_activation_steps)
|
||||
activated = activated.parentNode;
|
||||
|
||||
if (activated && activated._pre_click_activation_steps) {
|
||||
activated._pre_click_activation_steps();
|
||||
}
|
||||
|
||||
var click = this.ownerDocument.createEvent('MouseEvent');
|
||||
click.initMouseEvent('click', true, true,
|
||||
this.ownerDocument.defaultView, 1,
|
||||
event.screenX, event.screenY,
|
||||
event.clientX, event.clientY,
|
||||
event.ctrlKey, event.altKey,
|
||||
event.shiftKey, event.metaKey,
|
||||
event.button, null);
|
||||
|
||||
var result = this._dispatchEvent(click, true);
|
||||
|
||||
if (activated) {
|
||||
if (result) {
|
||||
// This is where hyperlinks get followed, for example.
|
||||
if (activated._post_click_activation_steps)
|
||||
activated._post_click_activation_steps(click);
|
||||
}
|
||||
else {
|
||||
if (activated._cancelled_activation_steps)
|
||||
activated._cancelled_activation_steps();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// An event handler is like an event listener, but it registered
|
||||
// by setting an IDL or content attribute like onload or onclick.
|
||||
// There can only be one of these at a time for any event type.
|
||||
// This is an internal method for the attribute accessors and
|
||||
// content attribute handlers that need to register events handlers.
|
||||
// The type argument is the same as in addEventListener().
|
||||
// The handler argument is the same as listeners in addEventListener:
|
||||
// it can be a function or an object. Pass null to remove any existing
|
||||
// handler. Handlers are always invoked before any listeners of
|
||||
// the same type. They are not invoked during the capturing phase
|
||||
// of event dispatch.
|
||||
//
|
||||
_setEventHandler: function _setEventHandler(type, handler) {
|
||||
if (!this._handlers) this._handlers = Object.create(null);
|
||||
this._handlers[type] = handler;
|
||||
},
|
||||
|
||||
_getEventHandler: function _getEventHandler(type) {
|
||||
return (this._handlers && this._handlers[type]) || null;
|
||||
}
|
||||
|
||||
};
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
"use strict";
|
||||
module.exports = FilteredElementList;
|
||||
|
||||
var Node = require('./Node');
|
||||
|
||||
//
|
||||
// This file defines node list implementation that lazily traverses
|
||||
// the document tree (or a subtree rooted at any element) and includes
|
||||
// only those elements for which a specified filter function returns true.
|
||||
// It is used to implement the
|
||||
// {Document,Element}.getElementsBy{TagName,ClassName}{,NS} methods.
|
||||
//
|
||||
// XXX this should inherit from NodeList
|
||||
|
||||
function FilteredElementList(root, filter) {
|
||||
this.root = root;
|
||||
this.filter = filter;
|
||||
this.lastModTime = root.lastModTime;
|
||||
this.done = false;
|
||||
this.cache = [];
|
||||
this.traverse();
|
||||
}
|
||||
|
||||
FilteredElementList.prototype = Object.create(Object.prototype, {
|
||||
length: { get: function() {
|
||||
this.checkcache();
|
||||
if (!this.done) this.traverse();
|
||||
return this.cache.length;
|
||||
} },
|
||||
|
||||
item: { value: function(n) {
|
||||
this.checkcache();
|
||||
if (!this.done && n >= this.cache.length) {
|
||||
// This can lead to O(N^2) behavior if we stop when we get to n
|
||||
// and the caller is iterating through the items in order; so
|
||||
// be sure to do the full traverse here.
|
||||
this.traverse(/*n*/);
|
||||
}
|
||||
return this.cache[n];
|
||||
} },
|
||||
|
||||
checkcache: { value: function() {
|
||||
if (this.lastModTime !== this.root.lastModTime) {
|
||||
// subtree has changed, so invalidate cache
|
||||
for (var i = this.cache.length-1; i>=0; i--) {
|
||||
this[i] = undefined;
|
||||
}
|
||||
this.cache.length = 0;
|
||||
this.done = false;
|
||||
this.lastModTime = this.root.lastModTime;
|
||||
}
|
||||
} },
|
||||
|
||||
// If n is specified, then traverse the tree until we've found the nth
|
||||
// item (or until we've found all items). If n is not specified,
|
||||
// traverse until we've found all items.
|
||||
traverse: { value: function(n) {
|
||||
// increment n so we can compare to length, and so it is never falsy
|
||||
if (n !== undefined) n++;
|
||||
|
||||
var elt;
|
||||
while ((elt = this.next()) !== null) {
|
||||
this[this.cache.length] = elt; //XXX Use proxy instead
|
||||
this.cache.push(elt);
|
||||
if (n && this.cache.length === n) return;
|
||||
}
|
||||
|
||||
// no next element, so we've found everything
|
||||
this.done = true;
|
||||
} },
|
||||
|
||||
// Return the next element under root that matches filter
|
||||
next: { value: function() {
|
||||
var start = (this.cache.length === 0) ? this.root // Start at the root or at
|
||||
: this.cache[this.cache.length-1]; // the last element we found
|
||||
|
||||
var elt;
|
||||
if (start.nodeType === Node.DOCUMENT_NODE)
|
||||
elt = start.documentElement;
|
||||
else
|
||||
elt = start.nextElement(this.root);
|
||||
|
||||
while(elt) {
|
||||
if (this.filter(elt)) {
|
||||
return elt;
|
||||
}
|
||||
|
||||
elt = elt.nextElement(this.root);
|
||||
}
|
||||
return null;
|
||||
} },
|
||||
});
|
||||
+7254
File diff suppressed because one or more lines are too long
+37
@@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
module.exports = Leaf;
|
||||
|
||||
var Node = require('./Node');
|
||||
var NodeList = require('./NodeList');
|
||||
var utils = require('./utils');
|
||||
var HierarchyRequestError = utils.HierarchyRequestError;
|
||||
var NotFoundError = utils.NotFoundError;
|
||||
|
||||
// This class defines common functionality for node subtypes that
|
||||
// can never have children
|
||||
function Leaf() {
|
||||
Node.call(this);
|
||||
}
|
||||
|
||||
Leaf.prototype = Object.create(Node.prototype, {
|
||||
hasChildNodes: { value: function() { return false; }},
|
||||
firstChild: { value: null },
|
||||
lastChild: { value: null },
|
||||
insertBefore: { value: function(node, child) {
|
||||
if (!node.nodeType) throw new TypeError('not a node');
|
||||
HierarchyRequestError();
|
||||
}},
|
||||
replaceChild: { value: function(node, child) {
|
||||
if (!node.nodeType) throw new TypeError('not a node');
|
||||
HierarchyRequestError();
|
||||
}},
|
||||
removeChild: { value: function(node) {
|
||||
if (!node.nodeType) throw new TypeError('not a node');
|
||||
NotFoundError();
|
||||
}},
|
||||
removeChildren: { value: function() { /* no op */ }},
|
||||
childNodes: { get: function() {
|
||||
if (!this._childNodes) this._childNodes = new NodeList();
|
||||
return this._childNodes;
|
||||
}}
|
||||
});
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
var utils = require('./utils');
|
||||
|
||||
var LinkedList = module.exports = {
|
||||
// basic validity tests on a circular linked list a
|
||||
valid: function(a) {
|
||||
utils.assert(a, "list falsy");
|
||||
utils.assert(a._previousSibling, "previous falsy");
|
||||
utils.assert(a._nextSibling, "next falsy");
|
||||
// xxx check that list is actually circular
|
||||
return true;
|
||||
},
|
||||
// insert a before b
|
||||
insertBefore: function(a, b) {
|
||||
utils.assert(LinkedList.valid(a) && LinkedList.valid(b));
|
||||
var a_first = a, a_last = a._previousSibling;
|
||||
var b_first = b, b_last = b._previousSibling;
|
||||
a_first._previousSibling = b_last;
|
||||
a_last._nextSibling = b_first;
|
||||
b_last._nextSibling = a_first;
|
||||
b_first._previousSibling = a_last;
|
||||
utils.assert(LinkedList.valid(a) && LinkedList.valid(b));
|
||||
},
|
||||
// replace a single node a with a list b (which could be null)
|
||||
replace: function(a, b) {
|
||||
utils.assert(LinkedList.valid(a) && (b===null || LinkedList.valid(b)));
|
||||
if (b!==null) {
|
||||
LinkedList.insertBefore(b, a);
|
||||
}
|
||||
LinkedList.remove(a);
|
||||
utils.assert(LinkedList.valid(a) && (b===null || LinkedList.valid(b)));
|
||||
},
|
||||
// remove single node a from its list
|
||||
remove: function(a) {
|
||||
utils.assert(LinkedList.valid(a));
|
||||
var prev = a._previousSibling;
|
||||
if (prev === a) { return; }
|
||||
var next = a._nextSibling;
|
||||
prev._nextSibling = next;
|
||||
next._previousSibling = prev;
|
||||
a._previousSibling = a._nextSibling = a;
|
||||
utils.assert(LinkedList.valid(a));
|
||||
}
|
||||
};
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
"use strict";
|
||||
var URL = require('./URL');
|
||||
var URLUtils = require('./URLUtils');
|
||||
|
||||
module.exports = Location;
|
||||
|
||||
function Location(window, href) {
|
||||
this._window = window;
|
||||
this._href = href;
|
||||
}
|
||||
|
||||
Location.prototype = Object.create(URLUtils.prototype, {
|
||||
constructor: { value: Location },
|
||||
|
||||
// Special behavior when href is set
|
||||
href: {
|
||||
get: function() { return this._href; },
|
||||
set: function(v) { this.assign(v); }
|
||||
},
|
||||
|
||||
assign: { value: function(url) {
|
||||
// Resolve the new url against the current one
|
||||
// XXX:
|
||||
// This is not actually correct. It should be resolved against
|
||||
// the URL of the document of the script. For now, though, I only
|
||||
// support a single window and there is only one base url.
|
||||
// So this is good enough for now.
|
||||
var current = new URL(this._href);
|
||||
var newurl = current.resolve(url);
|
||||
|
||||
// Save the new url
|
||||
this._href = newurl;
|
||||
|
||||
// Start loading the new document!
|
||||
// XXX
|
||||
// This is just something hacked together.
|
||||
// The real algorithm is: http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#navigate
|
||||
}},
|
||||
|
||||
replace: { value: function(url) {
|
||||
// XXX
|
||||
// Since we aren't tracking history yet, replace is the same as assign
|
||||
this.assign(url);
|
||||
}},
|
||||
|
||||
reload: { value: function() {
|
||||
// XXX:
|
||||
// Actually, the spec is a lot more complicated than this
|
||||
this.assign(this.href);
|
||||
}},
|
||||
|
||||
toString: { value: function() {
|
||||
return this.href;
|
||||
}}
|
||||
|
||||
});
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
var UIEvent = require('./UIEvent');
|
||||
|
||||
module.exports = MouseEvent;
|
||||
|
||||
function MouseEvent() {
|
||||
// Just use the superclass constructor to initialize
|
||||
UIEvent.call(this);
|
||||
|
||||
this.screenX = this.screenY = this.clientX = this.clientY = 0;
|
||||
this.ctrlKey = this.altKey = this.shiftKey = this.metaKey = false;
|
||||
this.button = 0;
|
||||
this.buttons = 1;
|
||||
this.relatedTarget = null;
|
||||
}
|
||||
MouseEvent.prototype = Object.create(UIEvent.prototype, {
|
||||
constructor: { value: MouseEvent },
|
||||
initMouseEvent: { value: function(type, bubbles, cancelable,
|
||||
view, detail,
|
||||
screenX, screenY, clientX, clientY,
|
||||
ctrlKey, altKey, shiftKey, metaKey,
|
||||
button, relatedTarget) {
|
||||
|
||||
this.initEvent(type, bubbles, cancelable, view, detail);
|
||||
this.screenX = screenX;
|
||||
this.screenY = screenY;
|
||||
this.clientX = clientX;
|
||||
this.clientY = clientY;
|
||||
this.ctrlKey = ctrlKey;
|
||||
this.altKey = altKey;
|
||||
this.shiftKey = shiftKey;
|
||||
this.metaKey = metaKey;
|
||||
this.button = button;
|
||||
switch(button) {
|
||||
case 0: this.buttons = 1; break;
|
||||
case 1: this.buttons = 4; break;
|
||||
case 2: this.buttons = 2; break;
|
||||
default: this.buttons = 0; break;
|
||||
}
|
||||
this.relatedTarget = relatedTarget;
|
||||
}},
|
||||
|
||||
getModifierState: { value: function(key) {
|
||||
switch(key) {
|
||||
case "Alt": return this.altKey;
|
||||
case "Control": return this.ctrlKey;
|
||||
case "Shift": return this.shiftKey;
|
||||
case "Meta": return this.metaKey;
|
||||
default: return false;
|
||||
}
|
||||
}}
|
||||
});
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
module.exports = {
|
||||
VALUE: 1, // The value of a Text, Comment or PI node changed
|
||||
ATTR: 2, // A new attribute was added or an attribute value and/or prefix changed
|
||||
REMOVE_ATTR: 3, // An attribute was removed
|
||||
REMOVE: 4, // A node was removed
|
||||
MOVE: 5, // A node was moved
|
||||
INSERT: 6 // A node (or a subtree of nodes) was inserted
|
||||
};
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
module.exports = NamedNodeMap;
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
/* This is a hacky implementation of NamedNodeMap, intended primarily to
|
||||
* satisfy clients (like dompurify and the web-platform-tests) which check
|
||||
* to ensure that Node#attributes instanceof NamedNodeMap. */
|
||||
|
||||
function NamedNodeMap(element) {
|
||||
this.element = element;
|
||||
}
|
||||
Object.defineProperties(NamedNodeMap.prototype, {
|
||||
length: { get: utils.shouldOverride },
|
||||
item: { value: utils.shouldOverride },
|
||||
|
||||
getNamedItem: { value: function getNamedItem(qualifiedName) {
|
||||
return this.element.getAttributeNode(qualifiedName);
|
||||
} },
|
||||
getNamedItemNS: { value: function getNamedItemNS(namespace, localName) {
|
||||
return this.element.getAttributeNodeNS(namespace, localName);
|
||||
} },
|
||||
setNamedItem: { value: utils.nyi },
|
||||
setNamedItemNS: { value: utils.nyi },
|
||||
removeNamedItem: { value: function removeNamedItem(qualifiedName) {
|
||||
var attr = this.element.getAttributeNode(qualifiedName);
|
||||
if (attr) {
|
||||
this.element.removeAttribute(qualifiedName);
|
||||
return attr;
|
||||
}
|
||||
utils.NotFoundError();
|
||||
} },
|
||||
removeNamedItemNS: { value: function removeNamedItemNS(ns, lname) {
|
||||
var attr = this.element.getAttributeNodeNS(ns, lname);
|
||||
if (attr) {
|
||||
this.element.removeAttributeNS(ns, lname);
|
||||
return attr;
|
||||
}
|
||||
utils.NotFoundError();
|
||||
} },
|
||||
});
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#navigatorid
|
||||
var NavigatorID = Object.create(null, {
|
||||
appCodeName: { value: "Mozilla" },
|
||||
appName: { value: "Netscape" },
|
||||
appVersion: { value: "4.0" },
|
||||
platform: { value: "" },
|
||||
product: { value: "Gecko" },
|
||||
productSub: { value: "20100101" },
|
||||
userAgent: { value: "" },
|
||||
vendor: { value: "" },
|
||||
vendorSub: { value: "" },
|
||||
taintEnabled: { value: function() { return false; } }
|
||||
});
|
||||
|
||||
module.exports = NavigatorID;
|
||||
+764
@@ -0,0 +1,764 @@
|
||||
"use strict";
|
||||
module.exports = Node;
|
||||
|
||||
var EventTarget = require('./EventTarget');
|
||||
var LinkedList = require('./LinkedList');
|
||||
var NodeUtils = require('./NodeUtils');
|
||||
var utils = require('./utils');
|
||||
|
||||
// All nodes have a nodeType and an ownerDocument.
|
||||
// Once inserted, they also have a parentNode.
|
||||
// This is an abstract class; all nodes in a document are instances
|
||||
// of a subtype, so all the properties are defined by more specific
|
||||
// constructors.
|
||||
function Node() {
|
||||
EventTarget.call(this);
|
||||
this.parentNode = null;
|
||||
this._nextSibling = this._previousSibling = this;
|
||||
this._index = undefined;
|
||||
}
|
||||
|
||||
var ELEMENT_NODE = Node.ELEMENT_NODE = 1;
|
||||
var ATTRIBUTE_NODE = Node.ATTRIBUTE_NODE = 2;
|
||||
var TEXT_NODE = Node.TEXT_NODE = 3;
|
||||
var CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE = 4;
|
||||
var ENTITY_REFERENCE_NODE = Node.ENTITY_REFERENCE_NODE = 5;
|
||||
var ENTITY_NODE = Node.ENTITY_NODE = 6;
|
||||
var PROCESSING_INSTRUCTION_NODE = Node.PROCESSING_INSTRUCTION_NODE = 7;
|
||||
var COMMENT_NODE = Node.COMMENT_NODE = 8;
|
||||
var DOCUMENT_NODE = Node.DOCUMENT_NODE = 9;
|
||||
var DOCUMENT_TYPE_NODE = Node.DOCUMENT_TYPE_NODE = 10;
|
||||
var DOCUMENT_FRAGMENT_NODE = Node.DOCUMENT_FRAGMENT_NODE = 11;
|
||||
var NOTATION_NODE = Node.NOTATION_NODE = 12;
|
||||
|
||||
var DOCUMENT_POSITION_DISCONNECTED = Node.DOCUMENT_POSITION_DISCONNECTED = 0x01;
|
||||
var DOCUMENT_POSITION_PRECEDING = Node.DOCUMENT_POSITION_PRECEDING = 0x02;
|
||||
var DOCUMENT_POSITION_FOLLOWING = Node.DOCUMENT_POSITION_FOLLOWING = 0x04;
|
||||
var DOCUMENT_POSITION_CONTAINS = Node.DOCUMENT_POSITION_CONTAINS = 0x08;
|
||||
var DOCUMENT_POSITION_CONTAINED_BY = Node.DOCUMENT_POSITION_CONTAINED_BY = 0x10;
|
||||
var DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
|
||||
|
||||
Node.prototype = Object.create(EventTarget.prototype, {
|
||||
|
||||
// Node that are not inserted into the tree inherit a null parent
|
||||
|
||||
// XXX: the baseURI attribute is defined by dom core, but
|
||||
// a correct implementation of it requires HTML features, so
|
||||
// we'll come back to this later.
|
||||
baseURI: { get: utils.nyi },
|
||||
|
||||
parentElement: { get: function() {
|
||||
return (this.parentNode && this.parentNode.nodeType===ELEMENT_NODE) ? this.parentNode : null;
|
||||
}},
|
||||
|
||||
hasChildNodes: { value: utils.shouldOverride },
|
||||
|
||||
firstChild: { get: utils.shouldOverride },
|
||||
|
||||
lastChild: { get: utils.shouldOverride },
|
||||
|
||||
isConnected: {
|
||||
get: function () {
|
||||
let node = this;
|
||||
while (node != null) {
|
||||
if (node.nodeType === Node.DOCUMENT_NODE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
node = node.parentNode;
|
||||
if (node != null && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
node = node.host;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
previousSibling: { get: function() {
|
||||
var parent = this.parentNode;
|
||||
if (!parent) return null;
|
||||
if (this === parent.firstChild) return null;
|
||||
return this._previousSibling;
|
||||
}},
|
||||
|
||||
nextSibling: { get: function() {
|
||||
var parent = this.parentNode, next = this._nextSibling;
|
||||
if (!parent) return null;
|
||||
if (next === parent.firstChild) return null;
|
||||
return next;
|
||||
}},
|
||||
|
||||
textContent: {
|
||||
// Should override for DocumentFragment/Element/Attr/Text/PI/Comment
|
||||
get: function() { return null; },
|
||||
set: function(v) { /* do nothing */ },
|
||||
},
|
||||
|
||||
innerText: {
|
||||
// Should override for DocumentFragment/Element/Attr/Text/PI/Comment
|
||||
get: function() { return null; },
|
||||
set: function(v) { /* do nothing */ },
|
||||
},
|
||||
|
||||
_countChildrenOfType: { value: function(type) {
|
||||
var sum = 0;
|
||||
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
|
||||
if (kid.nodeType === type) sum++;
|
||||
}
|
||||
return sum;
|
||||
}},
|
||||
|
||||
_ensureInsertValid: { value: function _ensureInsertValid(node, child, isPreinsert) {
|
||||
var parent = this, i, kid;
|
||||
if (!node.nodeType) throw new TypeError('not a node');
|
||||
// 1. If parent is not a Document, DocumentFragment, or Element
|
||||
// node, throw a HierarchyRequestError.
|
||||
switch (parent.nodeType) {
|
||||
case DOCUMENT_NODE:
|
||||
case DOCUMENT_FRAGMENT_NODE:
|
||||
case ELEMENT_NODE:
|
||||
break;
|
||||
default: utils.HierarchyRequestError();
|
||||
}
|
||||
// 2. If node is a host-including inclusive ancestor of parent,
|
||||
// throw a HierarchyRequestError.
|
||||
if (node.isAncestor(parent)) utils.HierarchyRequestError();
|
||||
// 3. If child is not null and its parent is not parent, then
|
||||
// throw a NotFoundError. (replaceChild omits the 'child is not null'
|
||||
// and throws a TypeError here if child is null.)
|
||||
if (child !== null || !isPreinsert) {
|
||||
if (child.parentNode !== parent) utils.NotFoundError();
|
||||
}
|
||||
// 4. If node is not a DocumentFragment, DocumentType, Element,
|
||||
// Text, ProcessingInstruction, or Comment node, throw a
|
||||
// HierarchyRequestError.
|
||||
switch (node.nodeType) {
|
||||
case DOCUMENT_FRAGMENT_NODE:
|
||||
case DOCUMENT_TYPE_NODE:
|
||||
case ELEMENT_NODE:
|
||||
case TEXT_NODE:
|
||||
case PROCESSING_INSTRUCTION_NODE:
|
||||
case COMMENT_NODE:
|
||||
break;
|
||||
default: utils.HierarchyRequestError();
|
||||
}
|
||||
// 5. If either node is a Text node and parent is a document, or
|
||||
// node is a doctype and parent is not a document, throw a
|
||||
// HierarchyRequestError.
|
||||
// 6. If parent is a document, and any of the statements below, switched
|
||||
// on node, are true, throw a HierarchyRequestError.
|
||||
if (parent.nodeType === DOCUMENT_NODE) {
|
||||
switch (node.nodeType) {
|
||||
case TEXT_NODE:
|
||||
utils.HierarchyRequestError();
|
||||
break;
|
||||
case DOCUMENT_FRAGMENT_NODE:
|
||||
// 6a1. If node has more than one element child or has a Text
|
||||
// node child.
|
||||
if (node._countChildrenOfType(TEXT_NODE) > 0)
|
||||
utils.HierarchyRequestError();
|
||||
switch (node._countChildrenOfType(ELEMENT_NODE)) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
// 6a2. Otherwise, if node has one element child and either
|
||||
// parent has an element child, child is a doctype, or child
|
||||
// is not null and a doctype is following child. [preinsert]
|
||||
// 6a2. Otherwise, if node has one element child and either
|
||||
// parent has an element child that is not child or a
|
||||
// doctype is following child. [replaceWith]
|
||||
if (child !== null /* always true here for replaceWith */) {
|
||||
if (isPreinsert && child.nodeType === DOCUMENT_TYPE_NODE)
|
||||
utils.HierarchyRequestError();
|
||||
for (kid = child.nextSibling; kid !== null; kid = kid.nextSibling) {
|
||||
if (kid.nodeType === DOCUMENT_TYPE_NODE)
|
||||
utils.HierarchyRequestError();
|
||||
}
|
||||
}
|
||||
i = parent._countChildrenOfType(ELEMENT_NODE);
|
||||
if (isPreinsert) {
|
||||
// "parent has an element child"
|
||||
if (i > 0)
|
||||
utils.HierarchyRequestError();
|
||||
} else {
|
||||
// "parent has an element child that is not child"
|
||||
if (i > 1 || (i === 1 && child.nodeType !== ELEMENT_NODE))
|
||||
utils.HierarchyRequestError();
|
||||
}
|
||||
break;
|
||||
default: // 6a1, continued. (more than one Element child)
|
||||
utils.HierarchyRequestError();
|
||||
}
|
||||
break;
|
||||
case ELEMENT_NODE:
|
||||
// 6b. parent has an element child, child is a doctype, or
|
||||
// child is not null and a doctype is following child. [preinsert]
|
||||
// 6b. parent has an element child that is not child or a
|
||||
// doctype is following child. [replaceWith]
|
||||
if (child !== null /* always true here for replaceWith */) {
|
||||
if (isPreinsert && child.nodeType === DOCUMENT_TYPE_NODE)
|
||||
utils.HierarchyRequestError();
|
||||
for (kid = child.nextSibling; kid !== null; kid = kid.nextSibling) {
|
||||
if (kid.nodeType === DOCUMENT_TYPE_NODE)
|
||||
utils.HierarchyRequestError();
|
||||
}
|
||||
}
|
||||
i = parent._countChildrenOfType(ELEMENT_NODE);
|
||||
if (isPreinsert) {
|
||||
// "parent has an element child"
|
||||
if (i > 0)
|
||||
utils.HierarchyRequestError();
|
||||
} else {
|
||||
// "parent has an element child that is not child"
|
||||
if (i > 1 || (i === 1 && child.nodeType !== ELEMENT_NODE))
|
||||
utils.HierarchyRequestError();
|
||||
}
|
||||
break;
|
||||
case DOCUMENT_TYPE_NODE:
|
||||
// 6c. parent has a doctype child, child is non-null and an
|
||||
// element is preceding child, or child is null and parent has
|
||||
// an element child. [preinsert]
|
||||
// 6c. parent has a doctype child that is not child, or an
|
||||
// element is preceding child. [replaceWith]
|
||||
if (child === null) {
|
||||
if (parent._countChildrenOfType(ELEMENT_NODE))
|
||||
utils.HierarchyRequestError();
|
||||
} else {
|
||||
// child is always non-null for [replaceWith] case
|
||||
for (kid = parent.firstChild; kid !== null; kid = kid.nextSibling) {
|
||||
if (kid === child) break;
|
||||
if (kid.nodeType === ELEMENT_NODE)
|
||||
utils.HierarchyRequestError();
|
||||
}
|
||||
}
|
||||
i = parent._countChildrenOfType(DOCUMENT_TYPE_NODE);
|
||||
if (isPreinsert) {
|
||||
// "parent has an doctype child"
|
||||
if (i > 0)
|
||||
utils.HierarchyRequestError();
|
||||
} else {
|
||||
// "parent has an doctype child that is not child"
|
||||
if (i > 1 || (i === 1 && child.nodeType !== DOCUMENT_TYPE_NODE))
|
||||
utils.HierarchyRequestError();
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 5, continued: (parent is not a document)
|
||||
if (node.nodeType === DOCUMENT_TYPE_NODE) utils.HierarchyRequestError();
|
||||
}
|
||||
}},
|
||||
|
||||
insertBefore: { value: function insertBefore(node, child) {
|
||||
var parent = this;
|
||||
// 1. Ensure pre-insertion validity
|
||||
parent._ensureInsertValid(node, child, true);
|
||||
// 2. Let reference child be child.
|
||||
var refChild = child;
|
||||
// 3. If reference child is node, set it to node's next sibling
|
||||
if (refChild === node) { refChild = node.nextSibling; }
|
||||
// 4. Adopt node into parent's node document.
|
||||
parent.doc.adoptNode(node);
|
||||
// 5. Insert node into parent before reference child.
|
||||
node._insertOrReplace(parent, refChild, false);
|
||||
// 6. Return node
|
||||
return node;
|
||||
}},
|
||||
|
||||
|
||||
appendChild: { value: function(child) {
|
||||
// This invokes _appendChild after doing validity checks.
|
||||
return this.insertBefore(child, null);
|
||||
}},
|
||||
|
||||
_appendChild: { value: function(child) {
|
||||
child._insertOrReplace(this, null, false);
|
||||
}},
|
||||
|
||||
removeChild: { value: function removeChild(child) {
|
||||
var parent = this;
|
||||
if (!child.nodeType) throw new TypeError('not a node');
|
||||
if (child.parentNode !== parent) utils.NotFoundError();
|
||||
child.remove();
|
||||
return child;
|
||||
}},
|
||||
|
||||
// To replace a `child` with `node` within a `parent` (this)
|
||||
replaceChild: { value: function replaceChild(node, child) {
|
||||
var parent = this;
|
||||
// Ensure validity (slight differences from pre-insertion check)
|
||||
parent._ensureInsertValid(node, child, false);
|
||||
// Adopt node into parent's node document.
|
||||
if (node.doc !== parent.doc) {
|
||||
// XXX adoptNode has side-effect of removing node from its parent
|
||||
// and generating a mutation event, thus causing the _insertOrReplace
|
||||
// to generate two deletes and an insert instead of a 'move'
|
||||
// event. It looks like the new MutationObserver stuff avoids
|
||||
// this problem, but for now let's only adopt (ie, remove `node`
|
||||
// from its parent) here if we need to.
|
||||
parent.doc.adoptNode(node);
|
||||
}
|
||||
// Do the replace.
|
||||
node._insertOrReplace(parent, child, true);
|
||||
return child;
|
||||
}},
|
||||
|
||||
// See: http://ejohn.org/blog/comparing-document-position/
|
||||
contains: { value: function contains(node) {
|
||||
if (node === null) { return false; }
|
||||
if (this === node) { return true; /* inclusive descendant */ }
|
||||
/* jshint bitwise: false */
|
||||
return (this.compareDocumentPosition(node) &
|
||||
DOCUMENT_POSITION_CONTAINED_BY) !== 0;
|
||||
}},
|
||||
|
||||
compareDocumentPosition: { value: function compareDocumentPosition(that){
|
||||
// Basic algorithm for finding the relative position of two nodes.
|
||||
// Make a list the ancestors of each node, starting with the
|
||||
// document element and proceeding down to the nodes themselves.
|
||||
// Then, loop through the lists, looking for the first element
|
||||
// that differs. The order of those two elements give the
|
||||
// order of their descendant nodes. Or, if one list is a prefix
|
||||
// of the other one, then that node contains the other.
|
||||
|
||||
if (this === that) return 0;
|
||||
|
||||
// If they're not owned by the same document or if one is rooted
|
||||
// and one is not, then they're disconnected.
|
||||
if (this.doc !== that.doc ||
|
||||
this.rooted !== that.rooted)
|
||||
return (DOCUMENT_POSITION_DISCONNECTED +
|
||||
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
|
||||
|
||||
// Get arrays of ancestors for this and that
|
||||
var these = [], those = [];
|
||||
for(var n = this; n !== null; n = n.parentNode) these.push(n);
|
||||
for(n = that; n !== null; n = n.parentNode) those.push(n);
|
||||
these.reverse(); // So we start with the outermost
|
||||
those.reverse();
|
||||
|
||||
if (these[0] !== those[0]) // No common ancestor
|
||||
return (DOCUMENT_POSITION_DISCONNECTED +
|
||||
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
|
||||
|
||||
n = Math.min(these.length, those.length);
|
||||
for(var i = 1; i < n; i++) {
|
||||
if (these[i] !== those[i]) {
|
||||
// We found two different ancestors, so compare
|
||||
// their positions
|
||||
if (these[i].index < those[i].index)
|
||||
return DOCUMENT_POSITION_FOLLOWING;
|
||||
else
|
||||
return DOCUMENT_POSITION_PRECEDING;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get to here, then one of the nodes (the one with the
|
||||
// shorter list of ancestors) contains the other one.
|
||||
if (these.length < those.length)
|
||||
return (DOCUMENT_POSITION_FOLLOWING +
|
||||
DOCUMENT_POSITION_CONTAINED_BY);
|
||||
else
|
||||
return (DOCUMENT_POSITION_PRECEDING +
|
||||
DOCUMENT_POSITION_CONTAINS);
|
||||
}},
|
||||
|
||||
isSameNode: {value : function isSameNode(node) {
|
||||
return this === node;
|
||||
}},
|
||||
|
||||
|
||||
// This method implements the generic parts of node equality testing
|
||||
// and defers to the (non-recursive) type-specific isEqual() method
|
||||
// defined by subclasses
|
||||
isEqualNode: { value: function isEqualNode(node) {
|
||||
if (!node) return false;
|
||||
if (node.nodeType !== this.nodeType) return false;
|
||||
|
||||
// Check type-specific properties for equality
|
||||
if (!this.isEqual(node)) return false;
|
||||
|
||||
// Now check children for number and equality
|
||||
for (var c1 = this.firstChild, c2 = node.firstChild;
|
||||
c1 && c2;
|
||||
c1 = c1.nextSibling, c2 = c2.nextSibling) {
|
||||
if (!c1.isEqualNode(c2)) return false;
|
||||
}
|
||||
return c1 === null && c2 === null;
|
||||
}},
|
||||
|
||||
// This method delegates shallow cloning to a clone() method
|
||||
// that each concrete subclass must implement
|
||||
cloneNode: { value: function(deep) {
|
||||
// Clone this node
|
||||
var clone = this.clone();
|
||||
|
||||
// Handle the recursive case if necessary
|
||||
if (deep) {
|
||||
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
|
||||
clone._appendChild(kid.cloneNode(true));
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}},
|
||||
|
||||
lookupPrefix: { value: function lookupPrefix(ns) {
|
||||
var e;
|
||||
if (ns === '' || ns === null || ns === undefined) return null;
|
||||
switch(this.nodeType) {
|
||||
case ELEMENT_NODE:
|
||||
return this._lookupNamespacePrefix(ns, this);
|
||||
case DOCUMENT_NODE:
|
||||
e = this.documentElement;
|
||||
return e ? e.lookupPrefix(ns) : null;
|
||||
case ENTITY_NODE:
|
||||
case NOTATION_NODE:
|
||||
case DOCUMENT_FRAGMENT_NODE:
|
||||
case DOCUMENT_TYPE_NODE:
|
||||
return null;
|
||||
case ATTRIBUTE_NODE:
|
||||
e = this.ownerElement;
|
||||
return e ? e.lookupPrefix(ns) : null;
|
||||
default:
|
||||
e = this.parentElement;
|
||||
return e ? e.lookupPrefix(ns) : null;
|
||||
}
|
||||
}},
|
||||
|
||||
|
||||
lookupNamespaceURI: {value: function lookupNamespaceURI(prefix) {
|
||||
if (prefix === '' || prefix === undefined) { prefix = null; }
|
||||
var e;
|
||||
switch(this.nodeType) {
|
||||
case ELEMENT_NODE:
|
||||
return utils.shouldOverride();
|
||||
case DOCUMENT_NODE:
|
||||
e = this.documentElement;
|
||||
return e ? e.lookupNamespaceURI(prefix) : null;
|
||||
case ENTITY_NODE:
|
||||
case NOTATION_NODE:
|
||||
case DOCUMENT_TYPE_NODE:
|
||||
case DOCUMENT_FRAGMENT_NODE:
|
||||
return null;
|
||||
case ATTRIBUTE_NODE:
|
||||
e = this.ownerElement;
|
||||
return e ? e.lookupNamespaceURI(prefix) : null;
|
||||
default:
|
||||
e = this.parentElement;
|
||||
return e ? e.lookupNamespaceURI(prefix) : null;
|
||||
}
|
||||
}},
|
||||
|
||||
isDefaultNamespace: { value: function isDefaultNamespace(ns) {
|
||||
if (ns === '' || ns === undefined) { ns = null; }
|
||||
var defaultNamespace = this.lookupNamespaceURI(null);
|
||||
return (defaultNamespace === ns);
|
||||
}},
|
||||
|
||||
// Utility methods for nodes. Not part of the DOM
|
||||
|
||||
// Return the index of this node in its parent.
|
||||
// Throw if no parent, or if this node is not a child of its parent
|
||||
index: { get: function() {
|
||||
var parent = this.parentNode;
|
||||
if (this === parent.firstChild) return 0; // fast case
|
||||
var kids = parent.childNodes;
|
||||
if (this._index === undefined || kids[this._index] !== this) {
|
||||
// Ensure that we don't have an O(N^2) blowup if none of the
|
||||
// kids have defined indices yet and we're traversing via
|
||||
// nextSibling or previousSibling
|
||||
for (var i=0; i<kids.length; i++) {
|
||||
kids[i]._index = i;
|
||||
}
|
||||
utils.assert(kids[this._index] === this);
|
||||
}
|
||||
return this._index;
|
||||
}},
|
||||
|
||||
// Return true if this node is equal to or is an ancestor of that node
|
||||
// Note that nodes are considered to be ancestors of themselves
|
||||
isAncestor: { value: function(that) {
|
||||
// If they belong to different documents, then they're unrelated.
|
||||
if (this.doc !== that.doc) return false;
|
||||
// If one is rooted and one isn't then they're not related
|
||||
if (this.rooted !== that.rooted) return false;
|
||||
|
||||
// Otherwise check by traversing the parentNode chain
|
||||
for(var e = that; e; e = e.parentNode) {
|
||||
if (e === this) return true;
|
||||
}
|
||||
return false;
|
||||
}},
|
||||
|
||||
// DOMINO Changed the behavior to conform with the specs. See:
|
||||
// https://groups.google.com/d/topic/mozilla.dev.platform/77sIYcpdDmc/discussion
|
||||
ensureSameDoc: { value: function(that) {
|
||||
if (that.ownerDocument === null) {
|
||||
that.ownerDocument = this.doc;
|
||||
}
|
||||
else if(that.ownerDocument !== this.doc) {
|
||||
utils.WrongDocumentError();
|
||||
}
|
||||
}},
|
||||
|
||||
removeChildren: { value: utils.shouldOverride },
|
||||
|
||||
// Insert this node as a child of parent before the specified child,
|
||||
// or insert as the last child of parent if specified child is null,
|
||||
// or replace the specified child with this node, firing mutation events as
|
||||
// necessary
|
||||
_insertOrReplace: { value: function _insertOrReplace(parent, before, isReplace) {
|
||||
var child = this, before_index, i;
|
||||
|
||||
if (child.nodeType === DOCUMENT_FRAGMENT_NODE && child.rooted) {
|
||||
utils.HierarchyRequestError();
|
||||
}
|
||||
|
||||
/* Ensure index of `before` is cached before we (possibly) remove it. */
|
||||
if (parent._childNodes) {
|
||||
before_index = (before === null) ? parent._childNodes.length :
|
||||
before.index; /* ensure _index is cached */
|
||||
|
||||
// If we are already a child of the specified parent, then
|
||||
// the index may have to be adjusted.
|
||||
if (child.parentNode === parent) {
|
||||
var child_index = child.index;
|
||||
// If the child is before the spot it is to be inserted at,
|
||||
// then when it is removed, the index of that spot will be
|
||||
// reduced.
|
||||
if (child_index < before_index) {
|
||||
before_index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the old child
|
||||
if (isReplace) {
|
||||
if (before.rooted) before.doc.mutateRemove(before);
|
||||
before.parentNode = null;
|
||||
}
|
||||
|
||||
var n = before;
|
||||
if (n === null) { n = parent.firstChild; }
|
||||
|
||||
// If both the child and the parent are rooted, then we want to
|
||||
// transplant the child without uprooting and rerooting it.
|
||||
var bothRooted = child.rooted && parent.rooted;
|
||||
if (child.nodeType === DOCUMENT_FRAGMENT_NODE) {
|
||||
var spliceArgs = [0, isReplace ? 1 : 0], next;
|
||||
for (var kid = child.firstChild; kid !== null; kid = next) {
|
||||
next = kid.nextSibling;
|
||||
spliceArgs.push(kid);
|
||||
kid.parentNode = parent;
|
||||
}
|
||||
var len = spliceArgs.length;
|
||||
// Add all nodes to the new parent, overwriting the old child
|
||||
if (isReplace) {
|
||||
LinkedList.replace(n, len > 2 ? spliceArgs[2] : null);
|
||||
} else if (len > 2 && n !== null) {
|
||||
LinkedList.insertBefore(spliceArgs[2], n);
|
||||
}
|
||||
if (parent._childNodes) {
|
||||
spliceArgs[0] = (before === null) ?
|
||||
parent._childNodes.length : before._index;
|
||||
parent._childNodes.splice.apply(parent._childNodes, spliceArgs);
|
||||
for (i=2; i<len; i++) {
|
||||
spliceArgs[i]._index = spliceArgs[0] + (i - 2);
|
||||
}
|
||||
} else if (parent._firstChild === before) {
|
||||
if (len > 2) {
|
||||
parent._firstChild = spliceArgs[2];
|
||||
} else if (isReplace) {
|
||||
parent._firstChild = null;
|
||||
}
|
||||
}
|
||||
// Remove all nodes from the document fragment
|
||||
if (child._childNodes) {
|
||||
child._childNodes.length = 0;
|
||||
} else {
|
||||
child._firstChild = null;
|
||||
}
|
||||
// Call the mutation handlers
|
||||
// Use spliceArgs since the original array has been destroyed. The
|
||||
// liveness guarantee requires us to clone the array so that
|
||||
// references to the childNodes of the DocumentFragment will be empty
|
||||
// when the insertion handlers are called.
|
||||
if (parent.rooted) {
|
||||
parent.modify();
|
||||
for (i = 2; i < len; i++) {
|
||||
parent.doc.mutateInsert(spliceArgs[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (before === child) { return; }
|
||||
if (bothRooted) {
|
||||
// Remove the child from its current position in the tree
|
||||
// without calling remove(), since we don't want to uproot it.
|
||||
child._remove();
|
||||
} else if (child.parentNode) {
|
||||
child.remove();
|
||||
}
|
||||
|
||||
// Insert it as a child of its new parent
|
||||
child.parentNode = parent;
|
||||
if (isReplace) {
|
||||
LinkedList.replace(n, child);
|
||||
if (parent._childNodes) {
|
||||
child._index = before_index;
|
||||
parent._childNodes[before_index] = child;
|
||||
} else if (parent._firstChild === before) {
|
||||
parent._firstChild = child;
|
||||
}
|
||||
} else {
|
||||
if (n !== null) {
|
||||
LinkedList.insertBefore(child, n);
|
||||
}
|
||||
if (parent._childNodes) {
|
||||
child._index = before_index;
|
||||
parent._childNodes.splice(before_index, 0, child);
|
||||
} else if (parent._firstChild === before) {
|
||||
parent._firstChild = child;
|
||||
}
|
||||
}
|
||||
if (bothRooted) {
|
||||
parent.modify();
|
||||
// Generate a move mutation event
|
||||
parent.doc.mutateMove(child);
|
||||
} else if (parent.rooted) {
|
||||
parent.modify();
|
||||
parent.doc.mutateInsert(child);
|
||||
}
|
||||
}
|
||||
}},
|
||||
|
||||
|
||||
// Return the lastModTime value for this node. (For use as a
|
||||
// cache invalidation mechanism. If the node does not already
|
||||
// have one, initialize it from the owner document's modclock
|
||||
// property. (Note that modclock does not return the actual
|
||||
// time; it is simply a counter incremented on each document
|
||||
// modification)
|
||||
lastModTime: { get: function() {
|
||||
if (!this._lastModTime) {
|
||||
this._lastModTime = this.doc.modclock;
|
||||
}
|
||||
return this._lastModTime;
|
||||
}},
|
||||
|
||||
// Increment the owner document's modclock and use the new
|
||||
// value to update the lastModTime value for this node and
|
||||
// all of its ancestors. Nodes that have never had their
|
||||
// lastModTime value queried do not need to have a
|
||||
// lastModTime property set on them since there is no
|
||||
// previously queried value to ever compare the new value
|
||||
// against, so only update nodes that already have a
|
||||
// _lastModTime property.
|
||||
modify: { value: function() {
|
||||
if (this.doc.modclock) { // Skip while doc.modclock == 0
|
||||
var time = ++this.doc.modclock;
|
||||
for(var n = this; n; n = n.parentElement) {
|
||||
if (n._lastModTime) {
|
||||
n._lastModTime = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}},
|
||||
|
||||
// This attribute is not part of the DOM but is quite helpful.
|
||||
// It returns the document with which a node is associated. Usually
|
||||
// this is the ownerDocument. But ownerDocument is null for the
|
||||
// document object itself, so this is a handy way to get the document
|
||||
// regardless of the node type
|
||||
doc: { get: function() {
|
||||
return this.ownerDocument || this;
|
||||
}},
|
||||
|
||||
|
||||
// If the node has a nid (node id), then it is rooted in a document
|
||||
rooted: { get: function() {
|
||||
return !!this._nid;
|
||||
}},
|
||||
|
||||
normalize: { value: function() {
|
||||
var next;
|
||||
for (var child=this.firstChild; child !== null; child=next) {
|
||||
next = child.nextSibling;
|
||||
|
||||
if (child.normalize) {
|
||||
child.normalize();
|
||||
}
|
||||
|
||||
if (child.nodeType !== Node.TEXT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.nodeValue === "") {
|
||||
this.removeChild(child);
|
||||
continue;
|
||||
}
|
||||
|
||||
var prevChild = child.previousSibling;
|
||||
if (prevChild === null) {
|
||||
continue;
|
||||
} else if (prevChild.nodeType === Node.TEXT_NODE) {
|
||||
// merge this with previous and remove the child
|
||||
prevChild.appendData(child.nodeValue);
|
||||
this.removeChild(child);
|
||||
}
|
||||
}
|
||||
}},
|
||||
|
||||
// Convert the children of a node to an HTML string.
|
||||
// This is used by the innerHTML getter
|
||||
// The serialization spec is at:
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments
|
||||
//
|
||||
// The serialization logic is intentionally implemented in a separate
|
||||
// `NodeUtils` helper instead of the more obvious choice of a private
|
||||
// `_serializeOne()` method on the `Node.prototype` in order to avoid
|
||||
// the megamorphic `this._serializeOne` property access, which reduces
|
||||
// performance unnecessarily. If you need specialized behavior for a
|
||||
// certain subclass, you'll need to implement that in `NodeUtils`.
|
||||
// See https://github.com/fgnass/domino/pull/142 for more information.
|
||||
serialize: { value: function() {
|
||||
if (this._innerHTML) {
|
||||
return this._innerHTML;
|
||||
}
|
||||
var s = '';
|
||||
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
|
||||
s += NodeUtils.serializeOne(kid, this);
|
||||
}
|
||||
return s;
|
||||
}},
|
||||
|
||||
// Non-standard, but often useful for debugging.
|
||||
outerHTML: {
|
||||
get: function() {
|
||||
return NodeUtils.serializeOne(this, { nodeType: 0 });
|
||||
},
|
||||
set: utils.nyi,
|
||||
},
|
||||
|
||||
// mirror node type properties in the prototype, so they are present
|
||||
// in instances of Node (and subclasses)
|
||||
ELEMENT_NODE: { value: ELEMENT_NODE },
|
||||
ATTRIBUTE_NODE: { value: ATTRIBUTE_NODE },
|
||||
TEXT_NODE: { value: TEXT_NODE },
|
||||
CDATA_SECTION_NODE: { value: CDATA_SECTION_NODE },
|
||||
ENTITY_REFERENCE_NODE: { value: ENTITY_REFERENCE_NODE },
|
||||
ENTITY_NODE: { value: ENTITY_NODE },
|
||||
PROCESSING_INSTRUCTION_NODE: { value: PROCESSING_INSTRUCTION_NODE },
|
||||
COMMENT_NODE: { value: COMMENT_NODE },
|
||||
DOCUMENT_NODE: { value: DOCUMENT_NODE },
|
||||
DOCUMENT_TYPE_NODE: { value: DOCUMENT_TYPE_NODE },
|
||||
DOCUMENT_FRAGMENT_NODE: { value: DOCUMENT_FRAGMENT_NODE },
|
||||
NOTATION_NODE: { value: NOTATION_NODE },
|
||||
|
||||
DOCUMENT_POSITION_DISCONNECTED: { value: DOCUMENT_POSITION_DISCONNECTED },
|
||||
DOCUMENT_POSITION_PRECEDING: { value: DOCUMENT_POSITION_PRECEDING },
|
||||
DOCUMENT_POSITION_FOLLOWING: { value: DOCUMENT_POSITION_FOLLOWING },
|
||||
DOCUMENT_POSITION_CONTAINS: { value: DOCUMENT_POSITION_CONTAINS },
|
||||
DOCUMENT_POSITION_CONTAINED_BY: { value: DOCUMENT_POSITION_CONTAINED_BY },
|
||||
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: { value: DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC },
|
||||
});
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
var NodeFilter = {
|
||||
// Constants for acceptNode()
|
||||
FILTER_ACCEPT: 1,
|
||||
FILTER_REJECT: 2,
|
||||
FILTER_SKIP: 3,
|
||||
|
||||
// Constants for whatToShow
|
||||
SHOW_ALL: 0xFFFFFFFF,
|
||||
SHOW_ELEMENT: 0x1,
|
||||
SHOW_ATTRIBUTE: 0x2, // historical
|
||||
SHOW_TEXT: 0x4,
|
||||
SHOW_CDATA_SECTION: 0x8, // historical
|
||||
SHOW_ENTITY_REFERENCE: 0x10, // historical
|
||||
SHOW_ENTITY: 0x20, // historical
|
||||
SHOW_PROCESSING_INSTRUCTION: 0x40,
|
||||
SHOW_COMMENT: 0x80,
|
||||
SHOW_DOCUMENT: 0x100,
|
||||
SHOW_DOCUMENT_TYPE: 0x200,
|
||||
SHOW_DOCUMENT_FRAGMENT: 0x400,
|
||||
SHOW_NOTATION: 0x800 // historical
|
||||
};
|
||||
|
||||
module.exports = (NodeFilter.constructor = NodeFilter.prototype = NodeFilter);
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
"use strict";
|
||||
module.exports = NodeIterator;
|
||||
|
||||
var NodeFilter = require('./NodeFilter');
|
||||
var NodeTraversal = require('./NodeTraversal');
|
||||
var utils = require('./utils');
|
||||
|
||||
/* Private methods and helpers */
|
||||
|
||||
/**
|
||||
* @based on WebKit's NodeIterator::moveToNext and NodeIterator::moveToPrevious
|
||||
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeIterator.cpp?rev=186279#L51
|
||||
*/
|
||||
function move(node, stayWithin, directionIsNext) {
|
||||
if (directionIsNext) {
|
||||
return NodeTraversal.next(node, stayWithin);
|
||||
} else {
|
||||
if (node === stayWithin) {
|
||||
return null;
|
||||
}
|
||||
return NodeTraversal.previous(node, null);
|
||||
}
|
||||
}
|
||||
|
||||
function isInclusiveAncestor(node, possibleChild) {
|
||||
for ( ; possibleChild; possibleChild = possibleChild.parentNode) {
|
||||
if (node === possibleChild) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @spec http://www.w3.org/TR/dom/#concept-nodeiterator-traverse
|
||||
* @method
|
||||
* @access private
|
||||
* @param {NodeIterator} ni
|
||||
* @param {string} direction One of 'next' or 'previous'.
|
||||
* @return {Node|null}
|
||||
*/
|
||||
function traverse(ni, directionIsNext) {
|
||||
var node, beforeNode;
|
||||
node = ni._referenceNode;
|
||||
beforeNode = ni._pointerBeforeReferenceNode;
|
||||
while (true) {
|
||||
if (beforeNode === directionIsNext) {
|
||||
beforeNode = !beforeNode;
|
||||
} else {
|
||||
node = move(node, ni._root, directionIsNext);
|
||||
if (node === null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
var result = ni._internalFilter(node);
|
||||
if (result === NodeFilter.FILTER_ACCEPT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ni._referenceNode = node;
|
||||
ni._pointerBeforeReferenceNode = beforeNode;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Public API */
|
||||
|
||||
/**
|
||||
* Implemented version: http://www.w3.org/TR/2015/WD-dom-20150618/#nodeiterator
|
||||
* Latest version: http://www.w3.org/TR/dom/#nodeiterator
|
||||
*
|
||||
* @constructor
|
||||
* @param {Node} root
|
||||
* @param {number} whatToShow [optional]
|
||||
* @param {Function|NodeFilter} filter [optional]
|
||||
* @throws Error
|
||||
*/
|
||||
function NodeIterator(root, whatToShow, filter) {
|
||||
if (!root || !root.nodeType) {
|
||||
utils.NotSupportedError();
|
||||
}
|
||||
|
||||
// Read-only properties
|
||||
this._root = root;
|
||||
this._referenceNode = root;
|
||||
this._pointerBeforeReferenceNode = true;
|
||||
this._whatToShow = Number(whatToShow) || 0;
|
||||
this._filter = filter || null;
|
||||
this._active = false;
|
||||
// Record active node iterators in the document, in order to perform
|
||||
// "node iterator pre-removal steps".
|
||||
root.doc._attachNodeIterator(this);
|
||||
}
|
||||
|
||||
Object.defineProperties(NodeIterator.prototype, {
|
||||
root: { get: function root() {
|
||||
return this._root;
|
||||
} },
|
||||
referenceNode: { get: function referenceNode() {
|
||||
return this._referenceNode;
|
||||
} },
|
||||
pointerBeforeReferenceNode: { get: function pointerBeforeReferenceNode() {
|
||||
return this._pointerBeforeReferenceNode;
|
||||
} },
|
||||
whatToShow: { get: function whatToShow() {
|
||||
return this._whatToShow;
|
||||
} },
|
||||
filter: { get: function filter() {
|
||||
return this._filter;
|
||||
} },
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @param {Node} node
|
||||
* @return {Number} Constant NodeFilter.FILTER_ACCEPT,
|
||||
* NodeFilter.FILTER_REJECT or NodeFilter.FILTER_SKIP.
|
||||
*/
|
||||
_internalFilter: { value: function _internalFilter(node) {
|
||||
/* jshint bitwise: false */
|
||||
var result, filter;
|
||||
if (this._active) {
|
||||
utils.InvalidStateError();
|
||||
}
|
||||
|
||||
// Maps nodeType to whatToShow
|
||||
if (!(((1 << (node.nodeType - 1)) & this._whatToShow))) {
|
||||
return NodeFilter.FILTER_SKIP;
|
||||
}
|
||||
|
||||
filter = this._filter;
|
||||
if (filter === null) {
|
||||
result = NodeFilter.FILTER_ACCEPT;
|
||||
} else {
|
||||
this._active = true;
|
||||
try {
|
||||
if (typeof filter === 'function') {
|
||||
result = filter(node);
|
||||
} else {
|
||||
result = filter.acceptNode(node);
|
||||
}
|
||||
} finally {
|
||||
this._active = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that coercing to a number means that
|
||||
// `true` becomes `1` (which is NodeFilter.FILTER_ACCEPT)
|
||||
// `false` becomes `0` (neither accept, reject, or skip)
|
||||
return (+result);
|
||||
} },
|
||||
|
||||
/**
|
||||
* @spec https://dom.spec.whatwg.org/#nodeiterator-pre-removing-steps
|
||||
* @method
|
||||
* @return void
|
||||
*/
|
||||
_preremove: { value: function _preremove(toBeRemovedNode) {
|
||||
if (isInclusiveAncestor(toBeRemovedNode, this._root)) { return; }
|
||||
if (!isInclusiveAncestor(toBeRemovedNode, this._referenceNode)) { return; }
|
||||
if (this._pointerBeforeReferenceNode) {
|
||||
var next = toBeRemovedNode;
|
||||
while (next.lastChild) {
|
||||
next = next.lastChild;
|
||||
}
|
||||
next = NodeTraversal.next(next, this.root);
|
||||
if (next) {
|
||||
this._referenceNode = next;
|
||||
return;
|
||||
}
|
||||
this._pointerBeforeReferenceNode = false;
|
||||
// fall through
|
||||
}
|
||||
if (toBeRemovedNode.previousSibling === null) {
|
||||
this._referenceNode = toBeRemovedNode.parentNode;
|
||||
} else {
|
||||
this._referenceNode = toBeRemovedNode.previousSibling;
|
||||
var lastChild;
|
||||
for (lastChild = this._referenceNode.lastChild;
|
||||
lastChild;
|
||||
lastChild = this._referenceNode.lastChild) {
|
||||
this._referenceNode = lastChild;
|
||||
}
|
||||
}
|
||||
} },
|
||||
|
||||
/**
|
||||
* @spec http://www.w3.org/TR/dom/#dom-nodeiterator-nextnode
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
nextNode: { value: function nextNode() {
|
||||
return traverse(this, true);
|
||||
} },
|
||||
|
||||
/**
|
||||
* @spec http://www.w3.org/TR/dom/#dom-nodeiterator-previousnode
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
previousNode: { value: function previousNode() {
|
||||
return traverse(this, false);
|
||||
} },
|
||||
|
||||
/**
|
||||
* @spec http://www.w3.org/TR/dom/#dom-nodeiterator-detach
|
||||
* @method
|
||||
* @return void
|
||||
*/
|
||||
detach: { value: function detach() {
|
||||
/* "The detach() method must do nothing.
|
||||
* Its functionality (disabling a NodeIterator object) was removed,
|
||||
* but the method itself is preserved for compatibility.
|
||||
*/
|
||||
} },
|
||||
|
||||
/** For compatibility with web-platform-tests. */
|
||||
toString: { value: function toString() {
|
||||
return "[object NodeIterator]";
|
||||
} },
|
||||
});
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
// No support for subclassing array, return an actual Array object.
|
||||
function item(i) {
|
||||
/* jshint validthis: true */
|
||||
return this[i] || null;
|
||||
}
|
||||
|
||||
function NodeList(a) {
|
||||
if (!a) a = [];
|
||||
a.item = item;
|
||||
return a;
|
||||
}
|
||||
|
||||
module.exports = NodeList;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/* jshint esversion: 6 */
|
||||
"use strict";
|
||||
|
||||
module.exports = class NodeList extends Array {
|
||||
constructor(a) {
|
||||
super((a && a.length) || 0);
|
||||
if (a) {
|
||||
for (var idx in a) { this[idx] = a[idx]; }
|
||||
}
|
||||
}
|
||||
item(i) { return this[i] || null; }
|
||||
};
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
var NodeList;
|
||||
|
||||
try {
|
||||
// Attempt to use ES6-style Array subclass if possible.
|
||||
NodeList = require('./NodeList.es6.js');
|
||||
} catch (e) {
|
||||
// No support for subclassing array, return an actual Array object.
|
||||
NodeList = require('./NodeList.es5.js');
|
||||
}
|
||||
|
||||
module.exports = NodeList;
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
"use strict";
|
||||
/* exported NodeTraversal */
|
||||
var NodeTraversal = module.exports = {
|
||||
nextSkippingChildren: nextSkippingChildren,
|
||||
nextAncestorSibling: nextAncestorSibling,
|
||||
next: next,
|
||||
previous: previous,
|
||||
deepLastChild: deepLastChild
|
||||
};
|
||||
|
||||
/**
|
||||
* @based on WebKit's NodeTraversal::nextSkippingChildren
|
||||
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.h?rev=179143#L109
|
||||
*/
|
||||
function nextSkippingChildren(node, stayWithin) {
|
||||
if (node === stayWithin) {
|
||||
return null;
|
||||
}
|
||||
if (node.nextSibling !== null) {
|
||||
return node.nextSibling;
|
||||
}
|
||||
return nextAncestorSibling(node, stayWithin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @based on WebKit's NodeTraversal::nextAncestorSibling
|
||||
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.cpp?rev=179143#L93
|
||||
*/
|
||||
function nextAncestorSibling(node, stayWithin) {
|
||||
for (node = node.parentNode; node !== null; node = node.parentNode) {
|
||||
if (node === stayWithin) {
|
||||
return null;
|
||||
}
|
||||
if (node.nextSibling !== null) {
|
||||
return node.nextSibling;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @based on WebKit's NodeTraversal::next
|
||||
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.h?rev=179143#L99
|
||||
*/
|
||||
function next(node, stayWithin) {
|
||||
var n;
|
||||
n = node.firstChild;
|
||||
if (n !== null) {
|
||||
return n;
|
||||
}
|
||||
if (node === stayWithin) {
|
||||
return null;
|
||||
}
|
||||
n = node.nextSibling;
|
||||
if (n !== null) {
|
||||
return n;
|
||||
}
|
||||
return nextAncestorSibling(node, stayWithin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @based on WebKit's NodeTraversal::deepLastChild
|
||||
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.cpp?rev=179143#L116
|
||||
*/
|
||||
function deepLastChild(node) {
|
||||
while (node.lastChild) {
|
||||
node = node.lastChild;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @based on WebKit's NodeTraversal::previous
|
||||
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.h?rev=179143#L121
|
||||
*/
|
||||
function previous(node, stayWithin) {
|
||||
var p;
|
||||
p = node.previousSibling;
|
||||
if (p !== null) {
|
||||
return deepLastChild(p);
|
||||
}
|
||||
p = node.parentNode;
|
||||
if (p === stayWithin) {
|
||||
return null;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
"use strict";
|
||||
module.exports = {
|
||||
// NOTE: The `serializeOne()` function used to live on the `Node.prototype`
|
||||
// as a private method `Node#_serializeOne(child)`, however that requires
|
||||
// a megamorphic property access `this._serializeOne` just to get to the
|
||||
// method, and this is being done on lots of different `Node` subclasses,
|
||||
// which puts a lot of pressure on V8's megamorphic stub cache. So by
|
||||
// moving the helper off of the `Node.prototype` and into a separate
|
||||
// function in this helper module, we get a monomorphic property access
|
||||
// `NodeUtils.serializeOne` to get to the function and reduce pressure
|
||||
// on the megamorphic stub cache.
|
||||
// See https://github.com/fgnass/domino/pull/142 for more information.
|
||||
serializeOne: serializeOne,
|
||||
|
||||
// Export util functions so that we can run extra test for them.
|
||||
// Note: we prefix function names with `ɵ`, similar to what we do
|
||||
// with internal functions in Angular packages.
|
||||
ɵescapeMatchingClosingTag: escapeMatchingClosingTag,
|
||||
ɵescapeClosingCommentTag: escapeClosingCommentTag,
|
||||
ɵescapeProcessingInstructionContent: escapeProcessingInstructionContent
|
||||
};
|
||||
|
||||
var utils = require('./utils');
|
||||
var NAMESPACE = utils.NAMESPACE;
|
||||
|
||||
var hasRawContent = {
|
||||
STYLE: true,
|
||||
SCRIPT: true,
|
||||
XMP: true,
|
||||
IFRAME: true,
|
||||
NOEMBED: true,
|
||||
NOFRAMES: true,
|
||||
PLAINTEXT: true
|
||||
};
|
||||
|
||||
var emptyElements = {
|
||||
area: true,
|
||||
base: true,
|
||||
basefont: true,
|
||||
bgsound: true,
|
||||
br: true,
|
||||
col: true,
|
||||
embed: true,
|
||||
frame: true,
|
||||
hr: true,
|
||||
img: true,
|
||||
input: true,
|
||||
keygen: true,
|
||||
link: true,
|
||||
meta: true,
|
||||
param: true,
|
||||
source: true,
|
||||
track: true,
|
||||
wbr: true
|
||||
};
|
||||
|
||||
var extraNewLine = {
|
||||
/* Removed in https://github.com/whatwg/html/issues/944
|
||||
pre: true,
|
||||
textarea: true,
|
||||
listing: true
|
||||
*/
|
||||
};
|
||||
|
||||
const ESCAPE_REGEXP = /[&<>\u00A0]/g;
|
||||
const ESCAPE_ATTR_REGEXP = /[&"<>\u00A0]/g;
|
||||
|
||||
function escape(s) {
|
||||
if (!ESCAPE_REGEXP.test(s)) {
|
||||
// nothing to do, fast path
|
||||
return s;
|
||||
}
|
||||
|
||||
return s.replace(ESCAPE_REGEXP, (c) => {
|
||||
switch (c) {
|
||||
case "&":
|
||||
return "&";
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "\u00A0":
|
||||
return " ";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function escapeAttr(s) {
|
||||
if (!ESCAPE_ATTR_REGEXP.test(s)) {
|
||||
// nothing to do, fast path
|
||||
return s;
|
||||
}
|
||||
|
||||
return s.replace(ESCAPE_ATTR_REGEXP, (c) => {
|
||||
switch (c) {
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "&":
|
||||
return "&";
|
||||
case '"':
|
||||
return """;
|
||||
case "\u00A0":
|
||||
return " ";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function attrname(a) {
|
||||
var ns = a.namespaceURI;
|
||||
if (!ns)
|
||||
return a.localName;
|
||||
if (ns === NAMESPACE.XML)
|
||||
return 'xml:' + a.localName;
|
||||
if (ns === NAMESPACE.XLINK)
|
||||
return 'xlink:' + a.localName;
|
||||
|
||||
if (ns === NAMESPACE.XMLNS) {
|
||||
if (a.localName === 'xmlns') return 'xmlns';
|
||||
else return 'xmlns:' + a.localName;
|
||||
}
|
||||
return a.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes matching closing tag in a raw text.
|
||||
*
|
||||
* For example, given `<style>#text(</style><script></script>)</style>`,
|
||||
* the parent tag would by "style" and the raw text is
|
||||
* "</style><script></script>". If we come across a matching closing tag
|
||||
* (in out case `</style>`) - replace `<` with `<` to avoid unexpected
|
||||
* and unsafe behavior after de-serialization.
|
||||
*/
|
||||
function escapeMatchingClosingTag(rawText, parentTag) {
|
||||
const parentClosingTag = '</' + parentTag;
|
||||
if (!rawText.toLowerCase().includes(parentClosingTag)) {
|
||||
return rawText; // fast path
|
||||
}
|
||||
const result = [...rawText];
|
||||
const matches = rawText.matchAll(new RegExp(parentClosingTag, 'ig'));
|
||||
for (const match of matches) {
|
||||
result[match.index] = '<';
|
||||
}
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
const CLOSING_COMMENT_REGEXP = /--!?>/;
|
||||
|
||||
/**
|
||||
* Escapes closing comment tag in a comment content.
|
||||
*
|
||||
* For example, given `#comment('-->')`, the content of a comment would be
|
||||
* updated to `-->` to avoid unexpected and unsafe behavior after
|
||||
* de-serialization.
|
||||
*/
|
||||
function escapeClosingCommentTag(rawContent) {
|
||||
if (!CLOSING_COMMENT_REGEXP.test(rawContent)) {
|
||||
return rawContent; // fast path
|
||||
}
|
||||
return rawContent.replace(/(--\!?)>/g, '$1>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes processing instruction content by replacing `>` with `>`.
|
||||
*/
|
||||
function escapeProcessingInstructionContent(rawContent) {
|
||||
return rawContent.includes('>')
|
||||
? rawContent.replaceAll('>', '>')
|
||||
: rawContent;
|
||||
}
|
||||
|
||||
function serializeOne(kid, parent) {
|
||||
var s = '';
|
||||
switch(kid.nodeType) {
|
||||
case 1: //ELEMENT_NODE
|
||||
var ns = kid.namespaceURI;
|
||||
var html = ns === NAMESPACE.HTML;
|
||||
var tagname = (html || ns === NAMESPACE.SVG || ns === NAMESPACE.MATHML) ? kid.localName : kid.tagName;
|
||||
|
||||
s += '<' + tagname;
|
||||
|
||||
for(var j = 0, k = kid._numattrs; j < k; j++) {
|
||||
var a = kid._attr(j);
|
||||
s += ' ' + attrname(a);
|
||||
if (a.value !== undefined) s += '="' + escapeAttr(a.value) + '"';
|
||||
}
|
||||
s += '>';
|
||||
|
||||
if (!(html && emptyElements[tagname])) {
|
||||
var ss = kid.serialize();
|
||||
// If an element can have raw content, this content may
|
||||
// potentially require escaping to avoid XSS.
|
||||
if (hasRawContent[tagname.toUpperCase()]) {
|
||||
ss = escapeMatchingClosingTag(ss, tagname);
|
||||
}
|
||||
if (html && extraNewLine[tagname] && ss.charAt(0)==='\n') s += '\n';
|
||||
// Serialize children and add end tag for all others
|
||||
s += ss;
|
||||
s += '</' + tagname + '>';
|
||||
}
|
||||
break;
|
||||
case 3: //TEXT_NODE
|
||||
case 4: //CDATA_SECTION_NODE
|
||||
var parenttag;
|
||||
if (parent.nodeType === 1 /*ELEMENT_NODE*/ &&
|
||||
parent.namespaceURI === NAMESPACE.HTML)
|
||||
parenttag = parent.tagName;
|
||||
else
|
||||
parenttag = '';
|
||||
|
||||
if (hasRawContent[parenttag] ||
|
||||
(parenttag==='NOSCRIPT' && parent.ownerDocument._scripting_enabled)) {
|
||||
s += kid.data;
|
||||
} else {
|
||||
s += escape(kid.data);
|
||||
}
|
||||
break;
|
||||
case 8: //COMMENT_NODE
|
||||
s += '<!--' + escapeClosingCommentTag(kid.data) + '-->';
|
||||
break;
|
||||
case 7: //PROCESSING_INSTRUCTION_NODE
|
||||
const content = escapeProcessingInstructionContent(kid.data);
|
||||
s += '<?' + kid.target + ' ' + content + '?>';
|
||||
break;
|
||||
case 10: //DOCUMENT_TYPE_NODE
|
||||
s += '<!DOCTYPE ' + kid.name;
|
||||
|
||||
if (false) {
|
||||
// Latest HTML serialization spec omits the public/system ID
|
||||
if (kid.publicID) {
|
||||
s += ' PUBLIC "' + kid.publicId + '"';
|
||||
}
|
||||
|
||||
if (kid.systemId) {
|
||||
s += ' "' + kid.systemId + '"';
|
||||
}
|
||||
}
|
||||
|
||||
s += '>';
|
||||
break;
|
||||
default:
|
||||
utils.InvalidStateError();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
var Node = require('./Node');
|
||||
|
||||
var NonDocumentTypeChildNode = {
|
||||
|
||||
nextElementSibling: { get: function() {
|
||||
if (this.parentNode) {
|
||||
for (var kid = this.nextSibling; kid !== null; kid = kid.nextSibling) {
|
||||
if (kid.nodeType === Node.ELEMENT_NODE) return kid;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}},
|
||||
|
||||
previousElementSibling: { get: function() {
|
||||
if (this.parentNode) {
|
||||
for (var kid = this.previousSibling; kid !== null; kid = kid.previousSibling) {
|
||||
if (kid.nodeType === Node.ELEMENT_NODE) return kid;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
|
||||
};
|
||||
|
||||
module.exports = NonDocumentTypeChildNode;
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
module.exports = ProcessingInstruction;
|
||||
|
||||
var Node = require('./Node');
|
||||
var CharacterData = require('./CharacterData');
|
||||
|
||||
function ProcessingInstruction(doc, target, data) {
|
||||
CharacterData.call(this);
|
||||
this.nodeType = Node.PROCESSING_INSTRUCTION_NODE;
|
||||
this.ownerDocument = doc;
|
||||
this.target = target;
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
var nodeValue = {
|
||||
get: function() { return this._data; },
|
||||
set: function(v) {
|
||||
if (v === null || v === undefined) { v = ''; } else { v = String(v); }
|
||||
this._data = v;
|
||||
if (this.rooted) this.ownerDocument.mutateValue(this);
|
||||
}
|
||||
};
|
||||
|
||||
ProcessingInstruction.prototype = Object.create(CharacterData.prototype, {
|
||||
nodeName: { get: function() { return this.target; }},
|
||||
nodeValue: nodeValue,
|
||||
textContent: nodeValue,
|
||||
innerText: nodeValue,
|
||||
data: {
|
||||
get: nodeValue.get,
|
||||
set: function(v) {
|
||||
nodeValue.set.call(this, v===null ? '' : String(v));
|
||||
},
|
||||
},
|
||||
|
||||
// Utility methods
|
||||
clone: { value: function clone() {
|
||||
return new ProcessingInstruction(this.ownerDocument, this.target, this._data);
|
||||
}},
|
||||
isEqual: { value: function isEqual(n) {
|
||||
return this.target === n.target && this._data === n._data;
|
||||
}}
|
||||
|
||||
});
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
"use strict";
|
||||
module.exports = Text;
|
||||
|
||||
var utils = require('./utils');
|
||||
var Node = require('./Node');
|
||||
var CharacterData = require('./CharacterData');
|
||||
|
||||
function Text(doc, data) {
|
||||
CharacterData.call(this);
|
||||
this.nodeType = Node.TEXT_NODE;
|
||||
this.ownerDocument = doc;
|
||||
this._data = data;
|
||||
this._index = undefined;
|
||||
}
|
||||
|
||||
var nodeValue = {
|
||||
get: function() { return this._data; },
|
||||
set: function(v) {
|
||||
if (v === null || v === undefined) { v = ''; } else { v = String(v); }
|
||||
if (v === this._data) return;
|
||||
this._data = v;
|
||||
if (this.rooted)
|
||||
this.ownerDocument.mutateValue(this);
|
||||
if (this.parentNode &&
|
||||
this.parentNode._textchangehook)
|
||||
this.parentNode._textchangehook(this);
|
||||
}
|
||||
};
|
||||
|
||||
Text.prototype = Object.create(CharacterData.prototype, {
|
||||
nodeName: { value: "#text" },
|
||||
// These three attributes are all the same.
|
||||
// The data attribute has a [TreatNullAs=EmptyString] but we'll
|
||||
// implement that at the interface level
|
||||
nodeValue: nodeValue,
|
||||
textContent: nodeValue,
|
||||
innerText: nodeValue,
|
||||
data: {
|
||||
get: nodeValue.get,
|
||||
set: function(v) {
|
||||
nodeValue.set.call(this, v===null ? '' : String(v));
|
||||
},
|
||||
},
|
||||
|
||||
splitText: { value: function splitText(offset) {
|
||||
if (offset > this._data.length || offset < 0) utils.IndexSizeError();
|
||||
|
||||
var newdata = this._data.substring(offset),
|
||||
newnode = this.ownerDocument.createTextNode(newdata);
|
||||
this.data = this.data.substring(0, offset);
|
||||
|
||||
var parent = this.parentNode;
|
||||
if (parent !== null)
|
||||
parent.insertBefore(newnode, this.nextSibling);
|
||||
|
||||
return newnode;
|
||||
}},
|
||||
|
||||
wholeText: { get: function wholeText() {
|
||||
var result = this.textContent;
|
||||
for (var next = this.nextSibling; next; next = next.nextSibling) {
|
||||
if (next.nodeType !== Node.TEXT_NODE) { break; }
|
||||
result += next.textContent;
|
||||
}
|
||||
return result;
|
||||
}},
|
||||
// Obsolete, removed from spec.
|
||||
replaceWholeText: { value: utils.nyi },
|
||||
|
||||
// Utility methods
|
||||
clone: { value: function clone() {
|
||||
return new Text(this.ownerDocument, this._data);
|
||||
}},
|
||||
|
||||
});
|
||||
+336
@@ -0,0 +1,336 @@
|
||||
"use strict";
|
||||
module.exports = TreeWalker;
|
||||
|
||||
var Node = require('./Node');
|
||||
var NodeFilter = require('./NodeFilter');
|
||||
var NodeTraversal = require('./NodeTraversal');
|
||||
var utils = require('./utils');
|
||||
|
||||
var mapChild = {
|
||||
first: 'firstChild',
|
||||
last: 'lastChild',
|
||||
next: 'firstChild',
|
||||
previous: 'lastChild'
|
||||
};
|
||||
|
||||
var mapSibling = {
|
||||
first: 'nextSibling',
|
||||
last: 'previousSibling',
|
||||
next: 'nextSibling',
|
||||
previous: 'previousSibling'
|
||||
};
|
||||
|
||||
/* Private methods and helpers */
|
||||
|
||||
/**
|
||||
* @spec https://dom.spec.whatwg.org/#concept-traverse-children
|
||||
* @method
|
||||
* @access private
|
||||
* @param {TreeWalker} tw
|
||||
* @param {string} type One of 'first' or 'last'.
|
||||
* @return {Node|null}
|
||||
*/
|
||||
function traverseChildren(tw, type) {
|
||||
var child, node, parent, result, sibling;
|
||||
node = tw._currentNode[mapChild[type]];
|
||||
while (node !== null) {
|
||||
result = tw._internalFilter(node);
|
||||
if (result === NodeFilter.FILTER_ACCEPT) {
|
||||
tw._currentNode = node;
|
||||
return node;
|
||||
}
|
||||
if (result === NodeFilter.FILTER_SKIP) {
|
||||
child = node[mapChild[type]];
|
||||
if (child !== null) {
|
||||
node = child;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while (node !== null) {
|
||||
sibling = node[mapSibling[type]];
|
||||
if (sibling !== null) {
|
||||
node = sibling;
|
||||
break;
|
||||
}
|
||||
parent = node.parentNode;
|
||||
if (parent === null || parent === tw.root || parent === tw._currentNode) {
|
||||
return null;
|
||||
} else {
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @spec https://dom.spec.whatwg.org/#concept-traverse-siblings
|
||||
* @method
|
||||
* @access private
|
||||
* @param {TreeWalker} tw
|
||||
* @param {TreeWalker} type One of 'next' or 'previous'.
|
||||
* @return {Node|nul}
|
||||
*/
|
||||
function traverseSiblings(tw, type) {
|
||||
var node, result, sibling;
|
||||
node = tw._currentNode;
|
||||
if (node === tw.root) {
|
||||
return null;
|
||||
}
|
||||
while (true) {
|
||||
sibling = node[mapSibling[type]];
|
||||
while (sibling !== null) {
|
||||
node = sibling;
|
||||
result = tw._internalFilter(node);
|
||||
if (result === NodeFilter.FILTER_ACCEPT) {
|
||||
tw._currentNode = node;
|
||||
return node;
|
||||
}
|
||||
sibling = node[mapChild[type]];
|
||||
if (result === NodeFilter.FILTER_REJECT || sibling === null) {
|
||||
sibling = node[mapSibling[type]];
|
||||
}
|
||||
}
|
||||
node = node.parentNode;
|
||||
if (node === null || node === tw.root) {
|
||||
return null;
|
||||
}
|
||||
if (tw._internalFilter(node) === NodeFilter.FILTER_ACCEPT) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Public API */
|
||||
|
||||
/**
|
||||
* Latest version: https://dom.spec.whatwg.org/#treewalker
|
||||
*
|
||||
* @constructor
|
||||
* @param {Node} root
|
||||
* @param {number} whatToShow [optional]
|
||||
* @param {Function|NodeFilter} filter [optional]
|
||||
* @throws Error
|
||||
*/
|
||||
function TreeWalker(root, whatToShow, filter) {
|
||||
if (!root || !root.nodeType) {
|
||||
utils.NotSupportedError();
|
||||
}
|
||||
|
||||
// Read-only properties
|
||||
this._root = root;
|
||||
this._whatToShow = Number(whatToShow) || 0;
|
||||
this._filter = filter || null;
|
||||
this._active = false;
|
||||
// Read-write property
|
||||
this._currentNode = root;
|
||||
}
|
||||
|
||||
Object.defineProperties(TreeWalker.prototype, {
|
||||
root: { get: function() { return this._root; } },
|
||||
whatToShow: { get: function() { return this._whatToShow; } },
|
||||
filter: { get: function() { return this._filter; } },
|
||||
|
||||
currentNode: {
|
||||
get: function currentNode() {
|
||||
return this._currentNode;
|
||||
},
|
||||
set: function setCurrentNode(v) {
|
||||
if (!(v instanceof Node)) {
|
||||
throw new TypeError("Not a Node"); // `null` is also not a node
|
||||
}
|
||||
this._currentNode = v;
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @param {Node} node
|
||||
* @return {Number} Constant NodeFilter.FILTER_ACCEPT,
|
||||
* NodeFilter.FILTER_REJECT or NodeFilter.FILTER_SKIP.
|
||||
*/
|
||||
_internalFilter: { value: function _internalFilter(node) {
|
||||
/* jshint bitwise: false */
|
||||
var result, filter;
|
||||
if (this._active) {
|
||||
utils.InvalidStateError();
|
||||
}
|
||||
|
||||
// Maps nodeType to whatToShow
|
||||
if (!(((1 << (node.nodeType - 1)) & this._whatToShow))) {
|
||||
return NodeFilter.FILTER_SKIP;
|
||||
}
|
||||
|
||||
filter = this._filter;
|
||||
if (filter === null) {
|
||||
result = NodeFilter.FILTER_ACCEPT;
|
||||
} else {
|
||||
this._active = true;
|
||||
try {
|
||||
if (typeof filter === 'function') {
|
||||
result = filter(node);
|
||||
} else {
|
||||
result = filter.acceptNode(node);
|
||||
}
|
||||
} finally {
|
||||
this._active = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that coercing to a number means that
|
||||
// `true` becomes `1` (which is NodeFilter.FILTER_ACCEPT)
|
||||
// `false` becomes `0` (neither accept, reject, or skip)
|
||||
return (+result);
|
||||
}},
|
||||
|
||||
/**
|
||||
* @spec https://dom.spec.whatwg.org/#dom-treewalker-parentnode
|
||||
* @based on WebKit's TreeWalker::parentNode
|
||||
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/dom/TreeWalker.cpp?rev=220453#L50
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
parentNode: { value: function parentNode() {
|
||||
var node = this._currentNode;
|
||||
while (node !== this.root) {
|
||||
node = node.parentNode;
|
||||
if (node === null) {
|
||||
return null;
|
||||
}
|
||||
if (this._internalFilter(node) === NodeFilter.FILTER_ACCEPT) {
|
||||
this._currentNode = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}},
|
||||
|
||||
/**
|
||||
* @spec https://dom.spec.whatwg.org/#dom-treewalker-firstchild
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
firstChild: { value: function firstChild() {
|
||||
return traverseChildren(this, 'first');
|
||||
}},
|
||||
|
||||
/**
|
||||
* @spec https://dom.spec.whatwg.org/#dom-treewalker-lastchild
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
lastChild: { value: function lastChild() {
|
||||
return traverseChildren(this, 'last');
|
||||
}},
|
||||
|
||||
/**
|
||||
* @spec http://www.w3.org/TR/dom/#dom-treewalker-previoussibling
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
previousSibling: { value: function previousSibling() {
|
||||
return traverseSiblings(this, 'previous');
|
||||
}},
|
||||
|
||||
/**
|
||||
* @spec http://www.w3.org/TR/dom/#dom-treewalker-nextsibling
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
nextSibling: { value: function nextSibling() {
|
||||
return traverseSiblings(this, 'next');
|
||||
}},
|
||||
|
||||
/**
|
||||
* @spec https://dom.spec.whatwg.org/#dom-treewalker-previousnode
|
||||
* @based on WebKit's TreeWalker::previousNode
|
||||
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/dom/TreeWalker.cpp?rev=220453#L181
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
previousNode: { value: function previousNode() {
|
||||
var node, result, previousSibling, lastChild;
|
||||
node = this._currentNode;
|
||||
while (node !== this._root) {
|
||||
for (previousSibling = node.previousSibling;
|
||||
previousSibling;
|
||||
previousSibling = node.previousSibling) {
|
||||
node = previousSibling;
|
||||
result = this._internalFilter(node);
|
||||
if (result === NodeFilter.FILTER_REJECT) {
|
||||
continue;
|
||||
}
|
||||
for (lastChild = node.lastChild;
|
||||
lastChild;
|
||||
lastChild = node.lastChild) {
|
||||
node = lastChild;
|
||||
result = this._internalFilter(node);
|
||||
if (result === NodeFilter.FILTER_REJECT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result === NodeFilter.FILTER_ACCEPT) {
|
||||
this._currentNode = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
if (node === this.root || node.parentNode === null) {
|
||||
return null;
|
||||
}
|
||||
node = node.parentNode;
|
||||
if (this._internalFilter(node) === NodeFilter.FILTER_ACCEPT) {
|
||||
this._currentNode = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}},
|
||||
|
||||
/**
|
||||
* @spec https://dom.spec.whatwg.org/#dom-treewalker-nextnode
|
||||
* @based on WebKit's TreeWalker::nextNode
|
||||
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/dom/TreeWalker.cpp?rev=220453#L228
|
||||
* @method
|
||||
* @return {Node|null}
|
||||
*/
|
||||
nextNode: { value: function nextNode() {
|
||||
var node, result, firstChild, nextSibling;
|
||||
node = this._currentNode;
|
||||
result = NodeFilter.FILTER_ACCEPT;
|
||||
|
||||
CHILDREN:
|
||||
while (true) {
|
||||
for (firstChild = node.firstChild;
|
||||
firstChild;
|
||||
firstChild = node.firstChild) {
|
||||
node = firstChild;
|
||||
result = this._internalFilter(node);
|
||||
if (result === NodeFilter.FILTER_ACCEPT) {
|
||||
this._currentNode = node;
|
||||
return node;
|
||||
} else if (result === NodeFilter.FILTER_REJECT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (nextSibling = NodeTraversal.nextSkippingChildren(node, this.root);
|
||||
nextSibling;
|
||||
nextSibling = NodeTraversal.nextSkippingChildren(node, this.root)) {
|
||||
node = nextSibling;
|
||||
result = this._internalFilter(node);
|
||||
if (result === NodeFilter.FILTER_ACCEPT) {
|
||||
this._currentNode = node;
|
||||
return node;
|
||||
} else if (result === NodeFilter.FILTER_SKIP) {
|
||||
continue CHILDREN;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}},
|
||||
|
||||
/** For compatibility with web-platform-tests. */
|
||||
toString: { value: function toString() {
|
||||
return "[object TreeWalker]";
|
||||
}},
|
||||
});
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
var Event = require('./Event');
|
||||
|
||||
module.exports = UIEvent;
|
||||
|
||||
function UIEvent() {
|
||||
// Just use the superclass constructor to initialize
|
||||
Event.call(this);
|
||||
this.view = null; // FF uses the current window
|
||||
this.detail = 0;
|
||||
}
|
||||
UIEvent.prototype = Object.create(Event.prototype, {
|
||||
constructor: { value: UIEvent },
|
||||
initUIEvent: { value: function(type, bubbles, cancelable, view, detail) {
|
||||
this.initEvent(type, bubbles, cancelable);
|
||||
this.view = view;
|
||||
this.detail = detail;
|
||||
}}
|
||||
});
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
"use strict";
|
||||
module.exports = URL;
|
||||
|
||||
function URL(url) {
|
||||
if (!url) return Object.create(URL.prototype);
|
||||
// Can't use String.trim() since it defines whitespace differently than HTML
|
||||
this.url = url.replace(/^[ \t\n\r\f]+|[ \t\n\r\f]+$/g, "");
|
||||
|
||||
// See http://tools.ietf.org/html/rfc3986#appendix-B
|
||||
// and https://url.spec.whatwg.org/#parsing
|
||||
var match = URL.pattern.exec(this.url);
|
||||
if (match) {
|
||||
if (match[2]) this.scheme = match[2];
|
||||
if (match[4]) {
|
||||
// parse username/password
|
||||
var userinfo = match[4].match(URL.userinfoPattern);
|
||||
if (userinfo) {
|
||||
this.username = userinfo[1];
|
||||
this.password = userinfo[3];
|
||||
match[4] = match[4].substring(userinfo[0].length);
|
||||
}
|
||||
if (match[4].match(URL.portPattern)) {
|
||||
var pos = match[4].lastIndexOf(':');
|
||||
this.host = match[4].substring(0, pos);
|
||||
this.port = match[4].substring(pos+1);
|
||||
}
|
||||
else {
|
||||
this.host = match[4];
|
||||
}
|
||||
}
|
||||
if (match[5]) this.path = match[5];
|
||||
if (match[6]) this.query = match[7];
|
||||
if (match[8]) this.fragment = match[9];
|
||||
}
|
||||
}
|
||||
|
||||
URL.pattern = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/;
|
||||
URL.userinfoPattern = /^([^@:]*)(:([^@]*))?@/;
|
||||
URL.portPattern = /:\d+$/;
|
||||
URL.authorityPattern = /^[^:\/?#]+:\/\//;
|
||||
URL.hierarchyPattern = /^[^:\/?#]+:\//;
|
||||
|
||||
// Return a percentEncoded version of s.
|
||||
// S should be a single-character string
|
||||
// XXX: needs to do utf-8 encoding?
|
||||
URL.percentEncode = function percentEncode(s) {
|
||||
var c = s.charCodeAt(0);
|
||||
if (c < 256) return "%" + c.toString(16);
|
||||
else throw Error("can't percent-encode codepoints > 255 yet");
|
||||
};
|
||||
|
||||
URL.prototype = {
|
||||
constructor: URL,
|
||||
|
||||
// XXX: not sure if this is the precise definition of absolute
|
||||
isAbsolute: function() { return !!this.scheme; },
|
||||
isAuthorityBased: function() {
|
||||
return URL.authorityPattern.test(this.url);
|
||||
},
|
||||
isHierarchical: function() {
|
||||
return URL.hierarchyPattern.test(this.url);
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
var s = "";
|
||||
if (this.scheme !== undefined) s += this.scheme + ":";
|
||||
if (this.isAbsolute()) {
|
||||
s += '//';
|
||||
if (this.username || this.password) {
|
||||
s += this.username || '';
|
||||
if (this.password) {
|
||||
s += ':' + this.password;
|
||||
}
|
||||
s += '@';
|
||||
}
|
||||
if (this.host) {
|
||||
s += this.host;
|
||||
}
|
||||
}
|
||||
if (this.port !== undefined) s += ":" + this.port;
|
||||
if (this.path !== undefined) s += this.path;
|
||||
if (this.query !== undefined) s += "?" + this.query;
|
||||
if (this.fragment !== undefined) s += "#" + this.fragment;
|
||||
return s;
|
||||
},
|
||||
|
||||
// See: http://tools.ietf.org/html/rfc3986#section-5.2
|
||||
// and https://url.spec.whatwg.org/#constructors
|
||||
resolve: function(relative) {
|
||||
var base = this; // The base url we're resolving against
|
||||
var r = new URL(relative); // The relative reference url to resolve
|
||||
var t = new URL(); // The absolute target url we will return
|
||||
|
||||
if (r.scheme !== undefined) {
|
||||
t.scheme = r.scheme;
|
||||
t.username = r.username;
|
||||
t.password = r.password;
|
||||
t.host = r.host;
|
||||
t.port = r.port;
|
||||
t.path = remove_dot_segments(r.path);
|
||||
t.query = r.query;
|
||||
}
|
||||
else {
|
||||
t.scheme = base.scheme;
|
||||
if (r.host !== undefined) {
|
||||
t.username = r.username;
|
||||
t.password = r.password;
|
||||
t.host = r.host;
|
||||
t.port = r.port;
|
||||
t.path = remove_dot_segments(r.path);
|
||||
t.query = r.query;
|
||||
}
|
||||
else {
|
||||
t.username = base.username;
|
||||
t.password = base.password;
|
||||
t.host = base.host;
|
||||
t.port = base.port;
|
||||
if (!r.path) { // undefined or empty
|
||||
t.path = base.path;
|
||||
if (r.query !== undefined)
|
||||
t.query = r.query;
|
||||
else
|
||||
t.query = base.query;
|
||||
}
|
||||
else {
|
||||
if (r.path.charAt(0) === "/") {
|
||||
t.path = remove_dot_segments(r.path);
|
||||
}
|
||||
else {
|
||||
t.path = merge(base.path, r.path);
|
||||
t.path = remove_dot_segments(t.path);
|
||||
}
|
||||
t.query = r.query;
|
||||
}
|
||||
}
|
||||
}
|
||||
t.fragment = r.fragment;
|
||||
|
||||
return t.toString();
|
||||
|
||||
|
||||
function merge(basepath, refpath) {
|
||||
if (base.host !== undefined && !base.path)
|
||||
return "/" + refpath;
|
||||
|
||||
var lastslash = basepath.lastIndexOf("/");
|
||||
if (lastslash === -1)
|
||||
return refpath;
|
||||
else
|
||||
return basepath.substring(0, lastslash+1) + refpath;
|
||||
}
|
||||
|
||||
function remove_dot_segments(path) {
|
||||
if (!path) return path; // For "" or undefined
|
||||
|
||||
var output = "";
|
||||
while(path.length > 0) {
|
||||
if (path === "." || path === "..") {
|
||||
path = "";
|
||||
break;
|
||||
}
|
||||
|
||||
var twochars = path.substring(0,2);
|
||||
var threechars = path.substring(0,3);
|
||||
var fourchars = path.substring(0,4);
|
||||
if (threechars === "../") {
|
||||
path = path.substring(3);
|
||||
}
|
||||
else if (twochars === "./") {
|
||||
path = path.substring(2);
|
||||
}
|
||||
else if (threechars === "/./") {
|
||||
path = "/" + path.substring(3);
|
||||
}
|
||||
else if (twochars === "/." && path.length === 2) {
|
||||
path = "/";
|
||||
}
|
||||
else if (fourchars === "/../" ||
|
||||
(threechars === "/.." && path.length === 3)) {
|
||||
path = "/" + path.substring(4);
|
||||
|
||||
output = output.replace(/\/?[^\/]*$/, "");
|
||||
}
|
||||
else {
|
||||
var segment = path.match(/(\/?([^\/]*))/)[0];
|
||||
output += segment;
|
||||
path = path.substring(segment.length);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
},
|
||||
};
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
"use strict";
|
||||
var URL = require('./URL');
|
||||
|
||||
module.exports = URLUtils;
|
||||
|
||||
// Allow the `x == null` pattern. This is eslint's "null: 'ignore'" option,
|
||||
// but jshint doesn't support this.
|
||||
/* jshint eqeqeq: false */
|
||||
|
||||
// This is an abstract superclass for Location, HTMLAnchorElement and
|
||||
// other types that have the standard complement of "URL decomposition
|
||||
// IDL attributes". This is now standardized as URLUtils, see:
|
||||
// https://url.spec.whatwg.org/#urlutils
|
||||
// Subclasses must define a getter/setter on href.
|
||||
// The getter and setter methods parse and rebuild the URL on each
|
||||
// invocation; there is no attempt to cache the value and be more efficient
|
||||
function URLUtils() {}
|
||||
URLUtils.prototype = Object.create(Object.prototype, {
|
||||
|
||||
_url: { get: function() {
|
||||
// XXX: this should do the "Reinitialize url" steps, and "null" should
|
||||
// be a valid return value.
|
||||
return new URL(this.href);
|
||||
} },
|
||||
|
||||
protocol: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
if (url && url.scheme) return url.scheme + ":";
|
||||
else return ":";
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
if (url.isAbsolute()) {
|
||||
v = v.replace(/:+$/, "");
|
||||
v = v.replace(/[^-+\.a-zA-Z0-9]/g, URL.percentEncode);
|
||||
if (v.length > 0) {
|
||||
url.scheme = v;
|
||||
output = url.toString();
|
||||
}
|
||||
}
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
host: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
if (url.isAbsolute() && url.isAuthorityBased())
|
||||
return url.host + (url.port ? (":" + url.port) : "");
|
||||
else
|
||||
return "";
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
if (url.isAbsolute() && url.isAuthorityBased()) {
|
||||
v = v.replace(/[^-+\._~!$&'()*,;:=a-zA-Z0-9]/g, URL.percentEncode);
|
||||
if (v.length > 0) {
|
||||
url.host = v;
|
||||
delete url.port;
|
||||
output = url.toString();
|
||||
}
|
||||
}
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
hostname: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
if (url.isAbsolute() && url.isAuthorityBased())
|
||||
return url.host;
|
||||
else
|
||||
return "";
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
if (url.isAbsolute() && url.isAuthorityBased()) {
|
||||
v = v.replace(/^\/+/, "");
|
||||
v = v.replace(/[^-+\._~!$&'()*,;:=a-zA-Z0-9]/g, URL.percentEncode);
|
||||
if (v.length > 0) {
|
||||
url.host = v;
|
||||
output = url.toString();
|
||||
}
|
||||
}
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
port: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
if (url.isAbsolute() && url.isAuthorityBased() && url.port!==undefined)
|
||||
return url.port;
|
||||
else
|
||||
return "";
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
if (url.isAbsolute() && url.isAuthorityBased()) {
|
||||
v = '' + v;
|
||||
v = v.replace(/[^0-9].*$/, "");
|
||||
v = v.replace(/^0+/, "");
|
||||
if (v.length === 0) v = "0";
|
||||
if (parseInt(v, 10) <= 65535) {
|
||||
url.port = v;
|
||||
output = url.toString();
|
||||
}
|
||||
}
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
pathname: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
if (url.isAbsolute() && url.isHierarchical())
|
||||
return url.path;
|
||||
else
|
||||
return "";
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
if (url.isAbsolute() && url.isHierarchical()) {
|
||||
if (v.charAt(0) !== "/")
|
||||
v = "/" + v;
|
||||
v = v.replace(/[^-+\._~!$&'()*,;:=@\/a-zA-Z0-9]/g, URL.percentEncode);
|
||||
url.path = v;
|
||||
output = url.toString();
|
||||
}
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
search: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
if (url.isAbsolute() && url.isHierarchical() && url.query!==undefined)
|
||||
return "?" + url.query;
|
||||
else
|
||||
return "";
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
if (url.isAbsolute() && url.isHierarchical()) {
|
||||
if (v.charAt(0) === "?") v = v.substring(1);
|
||||
v = v.replace(/[^-+\._~!$&'()*,;:=@\/?a-zA-Z0-9]/g, URL.percentEncode);
|
||||
url.query = v;
|
||||
output = url.toString();
|
||||
}
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
hash: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
if (url == null || url.fragment == null || url.fragment === '') {
|
||||
return "";
|
||||
} else {
|
||||
return "#" + url.fragment;
|
||||
}
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
|
||||
if (v.charAt(0) === "#") v = v.substring(1);
|
||||
v = v.replace(/[^-+\._~!$&'()*,;:=@\/?a-zA-Z0-9]/g, URL.percentEncode);
|
||||
url.fragment = v;
|
||||
output = url.toString();
|
||||
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
username: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
return url.username || '';
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
if (url.isAbsolute()) {
|
||||
v = v.replace(/[\x00-\x1F\x7F-\uFFFF "#<>?`\/@\\:]/g, URL.percentEncode);
|
||||
url.username = v;
|
||||
output = url.toString();
|
||||
}
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
password: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
return url.password || '';
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
if (url.isAbsolute()) {
|
||||
if (v==='') {
|
||||
url.password = null;
|
||||
} else {
|
||||
v = v.replace(/[\x00-\x1F\x7F-\uFFFF "#<>?`\/@\\]/g, URL.percentEncode);
|
||||
url.password = v;
|
||||
}
|
||||
output = url.toString();
|
||||
}
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
|
||||
origin: { get: function() {
|
||||
var url = this._url;
|
||||
if (url == null) { return ''; }
|
||||
var originForPort = function(defaultPort) {
|
||||
var origin = [url.scheme, url.host, +url.port || defaultPort];
|
||||
// XXX should be "unicode serialization"
|
||||
return origin[0] + '://' + origin[1] +
|
||||
(origin[2] === defaultPort ? '' : (':' + origin[2]));
|
||||
};
|
||||
switch (url.scheme) {
|
||||
case 'ftp':
|
||||
return originForPort(21);
|
||||
case 'gopher':
|
||||
return originForPort(70);
|
||||
case 'http':
|
||||
case 'ws':
|
||||
return originForPort(80);
|
||||
case 'https':
|
||||
case 'wss':
|
||||
return originForPort(443);
|
||||
default:
|
||||
// this is what chrome does
|
||||
return url.scheme + '://';
|
||||
}
|
||||
} },
|
||||
|
||||
/*
|
||||
searchParams: {
|
||||
get: function() {
|
||||
var url = this._url;
|
||||
// XXX
|
||||
},
|
||||
set: function(v) {
|
||||
var output = this.href;
|
||||
var url = new URL(output);
|
||||
// XXX
|
||||
this.href = output;
|
||||
},
|
||||
},
|
||||
*/
|
||||
});
|
||||
|
||||
URLUtils._inherit = function(proto) {
|
||||
// copy getters/setters from URLUtils to o.
|
||||
Object.getOwnPropertyNames(URLUtils.prototype).forEach(function(p) {
|
||||
if (p==='constructor' || p==='href') { return; }
|
||||
var desc = Object.getOwnPropertyDescriptor(URLUtils.prototype, p);
|
||||
Object.defineProperty(proto, p, desc);
|
||||
});
|
||||
};
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
"use strict";
|
||||
var DOMImplementation = require('./DOMImplementation');
|
||||
var EventTarget = require('./EventTarget');
|
||||
var Location = require('./Location');
|
||||
var utils = require('./utils');
|
||||
|
||||
module.exports = Window;
|
||||
|
||||
function Window(document) {
|
||||
this.document = document || new DOMImplementation(null).createHTMLDocument("");
|
||||
this.document._scripting_enabled = true;
|
||||
this.document.defaultView = this;
|
||||
this.location = new Location(this, this.document._address || 'about:blank');
|
||||
}
|
||||
|
||||
Window.prototype = Object.create(EventTarget.prototype, {
|
||||
console: { value: console },
|
||||
history: { value: {
|
||||
back: utils.nyi,
|
||||
forward: utils.nyi,
|
||||
go: utils.nyi
|
||||
}},
|
||||
navigator: { value: require("./NavigatorID") },
|
||||
|
||||
// Self-referential properties
|
||||
window: { get: function() { return this; }},
|
||||
self: { get: function() { return this; }},
|
||||
frames: { get: function() { return this; }},
|
||||
|
||||
// Self-referential properties for a top-level window
|
||||
parent: { get: function() { return this; }},
|
||||
top: { get: function() { return this; }},
|
||||
|
||||
// We don't support any other windows for now
|
||||
length: { value: 0 }, // no frames
|
||||
frameElement: { value: null }, // not part of a frame
|
||||
opener: { value: null }, // not opened by another window
|
||||
|
||||
// The onload event handler.
|
||||
// XXX: need to support a bunch of other event types, too,
|
||||
// and have them interoperate with document.body.
|
||||
|
||||
onload: {
|
||||
get: function() {
|
||||
return this._getEventHandler("load");
|
||||
},
|
||||
set: function(v) {
|
||||
this._setEventHandler("load", v);
|
||||
}
|
||||
},
|
||||
|
||||
// XXX This is a completely broken implementation
|
||||
getComputedStyle: { value: function getComputedStyle(elt) {
|
||||
return elt.style;
|
||||
}}
|
||||
|
||||
});
|
||||
|
||||
utils.expose(require('./WindowTimers'), Window);
|
||||
utils.expose(require('./impl'), Window);
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#windowtimers
|
||||
var WindowTimers = {
|
||||
setTimeout: setTimeout,
|
||||
clearTimeout: clearTimeout,
|
||||
setInterval: setInterval,
|
||||
clearInterval: clearInterval
|
||||
};
|
||||
|
||||
module.exports = WindowTimers;
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
"use strict";
|
||||
var utils = require('./utils');
|
||||
|
||||
exports.property = function(attr) {
|
||||
if (Array.isArray(attr.type)) {
|
||||
var valid = Object.create(null);
|
||||
attr.type.forEach(function(val) {
|
||||
valid[val.value || val] = val.alias || val;
|
||||
});
|
||||
var missingValueDefault = attr.missing;
|
||||
if (missingValueDefault===undefined) { missingValueDefault = null; }
|
||||
var invalidValueDefault = attr.invalid;
|
||||
if (invalidValueDefault===undefined) { invalidValueDefault = missingValueDefault; }
|
||||
return {
|
||||
get: function() {
|
||||
var v = this._getattr(attr.name);
|
||||
if (v === null) return missingValueDefault;
|
||||
|
||||
v = valid[v.toLowerCase()];
|
||||
if (v !== undefined) return v;
|
||||
if (invalidValueDefault !== null) return invalidValueDefault;
|
||||
return v;
|
||||
},
|
||||
set: function(v) {
|
||||
this._setattr(attr.name, v);
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (attr.type === Boolean) {
|
||||
return {
|
||||
get: function() {
|
||||
return this.hasAttribute(attr.name);
|
||||
},
|
||||
set: function(v) {
|
||||
if (v) {
|
||||
this._setattr(attr.name, '');
|
||||
}
|
||||
else {
|
||||
this.removeAttribute(attr.name);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (attr.type === Number ||
|
||||
attr.type === "long" ||
|
||||
attr.type === "unsigned long" ||
|
||||
attr.type === "limited unsigned long with fallback") {
|
||||
return numberPropDesc(attr);
|
||||
}
|
||||
else if (!attr.type || attr.type === String) {
|
||||
return {
|
||||
get: function() { return this._getattr(attr.name) || ''; },
|
||||
set: function(v) {
|
||||
if (attr.treatNullAsEmptyString && v === null) { v = ''; }
|
||||
this._setattr(attr.name, v);
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (typeof attr.type === 'function') {
|
||||
return attr.type(attr.name, attr);
|
||||
}
|
||||
throw new Error('Invalid attribute definition');
|
||||
};
|
||||
|
||||
// See http://www.whatwg.org/specs/web-apps/current-work/#reflect
|
||||
//
|
||||
// defval is the default value. If it is a function, then that function
|
||||
// will be invoked as a method of the element to obtain the default.
|
||||
// If no default is specified for a given attribute, then the default
|
||||
// depends on the type of the attribute, but since this function handles
|
||||
// 4 integer cases, you must specify the default value in each call
|
||||
//
|
||||
// min and max define a valid range for getting the attribute.
|
||||
//
|
||||
// setmin defines a minimum value when setting. If the value is less
|
||||
// than that, then throw INDEX_SIZE_ERR.
|
||||
//
|
||||
// Conveniently, JavaScript's parseInt function appears to be
|
||||
// compatible with HTML's 'rules for parsing integers'
|
||||
function numberPropDesc(a) {
|
||||
var def;
|
||||
if(typeof a.default === 'function') {
|
||||
def = a.default;
|
||||
}
|
||||
else if(typeof a.default === 'number') {
|
||||
def = function() { return a.default; };
|
||||
}
|
||||
else {
|
||||
def = function() { utils.assert(false, typeof a.default); };
|
||||
}
|
||||
var unsigned_long = (a.type === 'unsigned long');
|
||||
var signed_long = (a.type === 'long');
|
||||
var unsigned_fallback = (a.type === 'limited unsigned long with fallback');
|
||||
var min = a.min, max = a.max, setmin = a.setmin;
|
||||
if (min === undefined) {
|
||||
if (unsigned_long) min = 0;
|
||||
if (signed_long) min = -0x80000000;
|
||||
if (unsigned_fallback) min = 1;
|
||||
}
|
||||
if (max === undefined) {
|
||||
if (unsigned_long || signed_long || unsigned_fallback) max = 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
return {
|
||||
get: function() {
|
||||
var v = this._getattr(a.name);
|
||||
var n = a.float ? parseFloat(v) : parseInt(v, 10);
|
||||
if (v === null || !isFinite(n) || (min !== undefined && n < min) || (max !== undefined && n > max)) {
|
||||
return def.call(this);
|
||||
}
|
||||
if (unsigned_long || signed_long || unsigned_fallback) {
|
||||
if (!/^[ \t\n\f\r]*[-+]?[0-9]/.test(v)) { return def.call(this); }
|
||||
n = n|0; // jshint ignore:line
|
||||
}
|
||||
return n;
|
||||
},
|
||||
set: function(v) {
|
||||
if (!a.float) { v = Math.floor(v); }
|
||||
if (setmin !== undefined && v < setmin) {
|
||||
utils.IndexSizeError(a.name + ' set to ' + v);
|
||||
}
|
||||
if (unsigned_long) {
|
||||
v = (v < 0 || v > 0x7FFFFFFF) ? def.call(this) :
|
||||
(v|0); // jshint ignore:line
|
||||
} else if (unsigned_fallback) {
|
||||
v = (v < 1 || v > 0x7FFFFFFF) ? def.call(this) :
|
||||
(v|0); // jshint ignore:line
|
||||
} else if (signed_long) {
|
||||
v = (v < -0x80000000 || v > 0x7FFFFFFF) ? def.call(this) :
|
||||
(v|0); // jshint ignore:line
|
||||
}
|
||||
this._setattr(a.name, String(v));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// This is a utility function for setting up change handler functions
|
||||
// for attributes like 'id' that require special handling when they change.
|
||||
exports.registerChangeHandler = function(c, name, handler) {
|
||||
var p = c.prototype;
|
||||
|
||||
// If p does not already have its own _attributeChangeHandlers
|
||||
// then create one for it, inheriting from the inherited
|
||||
// _attributeChangeHandlers. At the top (for the Element class) the
|
||||
// _attributeChangeHandlers object will be created with a null prototype.
|
||||
if (!Object.prototype.hasOwnProperty.call(p, '_attributeChangeHandlers')) {
|
||||
p._attributeChangeHandlers =
|
||||
Object.create(p._attributeChangeHandlers || null);
|
||||
}
|
||||
|
||||
p._attributeChangeHandlers[name] = handler;
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* This file defines Domino behaviour that can be externally configured.
|
||||
* To change these settings, set the relevant global property *before*
|
||||
* you call `require("domino")`.
|
||||
*/
|
||||
|
||||
exports.isApiWritable = !globalThis.__domino_frozen__;
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
|
||||
var attributes = require('./attributes');
|
||||
var isApiWritable = require("./config").isApiWritable;
|
||||
|
||||
module.exports = function(spec, defaultConstructor, tagList, tagNameToImpl) {
|
||||
var c = spec.ctor;
|
||||
if (c) {
|
||||
var props = spec.props || {};
|
||||
|
||||
if (spec.attributes) {
|
||||
for (var n in spec.attributes) {
|
||||
var attr = spec.attributes[n];
|
||||
if (typeof attr !== 'object' || Array.isArray(attr)) attr = {type: attr};
|
||||
if (!attr.name) attr.name = n.toLowerCase();
|
||||
props[n] = attributes.property(attr);
|
||||
}
|
||||
}
|
||||
|
||||
props.constructor = { value : c, writable: isApiWritable };
|
||||
c.prototype = Object.create((spec.superclass || defaultConstructor).prototype, props);
|
||||
if (spec.events) {
|
||||
addEventHandlers(c, spec.events);
|
||||
}
|
||||
tagList[spec.name] = c;
|
||||
}
|
||||
else {
|
||||
c = defaultConstructor;
|
||||
}
|
||||
|
||||
(spec.tags || spec.tag && [spec.tag] || []).forEach(function(tag) {
|
||||
tagNameToImpl[tag] = c;
|
||||
});
|
||||
|
||||
return c;
|
||||
};
|
||||
|
||||
function EventHandlerBuilder(body, document, form, element) {
|
||||
this.body = body;
|
||||
this.document = document;
|
||||
this.form = form;
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
EventHandlerBuilder.prototype.build = function () {
|
||||
return () => {};
|
||||
};
|
||||
|
||||
function EventHandlerChangeHandler(elt, name, oldval, newval) {
|
||||
var doc = elt.ownerDocument || Object.create(null);
|
||||
var form = elt.form || Object.create(null);
|
||||
elt[name] = new EventHandlerBuilder(newval, doc, form, elt).build();
|
||||
}
|
||||
|
||||
function addEventHandlers(c, eventHandlerTypes) {
|
||||
var p = c.prototype;
|
||||
eventHandlerTypes.forEach(function(type) {
|
||||
// Define the event handler registration IDL attribute for this type
|
||||
Object.defineProperty(p, "on" + type, {
|
||||
get: function() {
|
||||
return this._getEventHandler(type);
|
||||
},
|
||||
set: function(v) {
|
||||
this._setEventHandler(type, v);
|
||||
},
|
||||
});
|
||||
|
||||
// Define special behavior for the content attribute as well
|
||||
attributes.registerChangeHandler(c, "on" + type, EventHandlerChangeHandler);
|
||||
});
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
module.exports = {
|
||||
Event: require('./Event'),
|
||||
UIEvent: require('./UIEvent'),
|
||||
MouseEvent: require('./MouseEvent'),
|
||||
CustomEvent: require('./CustomEvent')
|
||||
};
|
||||
+1499
File diff suppressed because it is too large
Load Diff
+27
@@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
var utils = require('./utils');
|
||||
|
||||
exports = module.exports = {
|
||||
CSSStyleDeclaration: require('./CSSStyleDeclaration'),
|
||||
CharacterData: require('./CharacterData'),
|
||||
Comment: require('./Comment'),
|
||||
DOMException: require('./DOMException'),
|
||||
DOMImplementation: require('./DOMImplementation'),
|
||||
DOMTokenList: require('./DOMTokenList'),
|
||||
Document: require('./Document'),
|
||||
DocumentFragment: require('./DocumentFragment'),
|
||||
DocumentType: require('./DocumentType'),
|
||||
Element: require('./Element'),
|
||||
HTMLParser: require('./HTMLParser'),
|
||||
NamedNodeMap: require('./NamedNodeMap'),
|
||||
Node: require('./Node'),
|
||||
NodeList: require('./NodeList'),
|
||||
NodeFilter: require('./NodeFilter'),
|
||||
ProcessingInstruction: require('./ProcessingInstruction'),
|
||||
Text: require('./Text'),
|
||||
Window: require('./Window')
|
||||
};
|
||||
|
||||
utils.merge(exports, require('./events'));
|
||||
utils.merge(exports, require('./htmlelts').elements);
|
||||
utils.merge(exports, require('./svg').elements);
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
declare module 'domino' {
|
||||
function createDOMImplementation(): DOMImplementation;
|
||||
function createDocument(html?: string, force?: boolean): Document;
|
||||
function createWindow(html?: string, address?: string): Window;
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
var DOMImplementation = require('./DOMImplementation');
|
||||
var HTMLParser = require('./HTMLParser');
|
||||
var Window = require('./Window');
|
||||
var impl = require('./impl');
|
||||
|
||||
exports.createDOMImplementation = function() {
|
||||
return new DOMImplementation(null);
|
||||
};
|
||||
|
||||
exports.createDocument = function(html, force) {
|
||||
// Previous API couldn't let you pass '' as a document, and that
|
||||
// yields a slightly different document than createHTMLDocument('')
|
||||
// does. The new `force` parameter lets you pass '' if you want to.
|
||||
if (html || force) {
|
||||
var parser = new HTMLParser();
|
||||
parser.parse(html || '', true);
|
||||
return parser.document();
|
||||
}
|
||||
return new DOMImplementation(null).createHTMLDocument("");
|
||||
};
|
||||
|
||||
exports.createIncrementalHTMLParser = function() {
|
||||
var parser = new HTMLParser();
|
||||
/** API for incremental parser. */
|
||||
return {
|
||||
/** Provide an additional chunk of text to be parsed. */
|
||||
write: function(s) {
|
||||
if (s.length > 0) {
|
||||
parser.parse(s, false, function() { return true; });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Signal that we are done providing input text, optionally
|
||||
* providing one last chunk as a parameter.
|
||||
*/
|
||||
end: function(s) {
|
||||
parser.parse(s || '', true, function() { return true; });
|
||||
},
|
||||
/**
|
||||
* Performs a chunk of parsing work, returning at the end of
|
||||
* the next token as soon as shouldPauseFunc() returns true.
|
||||
* Returns true iff there is more work to do.
|
||||
*
|
||||
* For example:
|
||||
* ```
|
||||
* var incrParser = domino.createIncrementalHTMLParser();
|
||||
* incrParser.end('...long html document...');
|
||||
* while (true) {
|
||||
* // Pause every 10ms
|
||||
* var start = Date.now();
|
||||
* var pauseIn10 = function() { return (Date.now() - start) >= 10; };
|
||||
* if (!incrParser.process(pauseIn10)) {
|
||||
* break;
|
||||
* }
|
||||
* ...yield to other tasks, do other housekeeping, etc...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
process: function(shouldPauseFunc) {
|
||||
return parser.parse('', false, shouldPauseFunc);
|
||||
},
|
||||
/**
|
||||
* Returns the result of the incremental parse. Valid after
|
||||
* `this.end()` has been called and `this.process()` has returned
|
||||
* false.
|
||||
*/
|
||||
document: function() {
|
||||
return parser.document();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
exports.createWindow = function(html, address) {
|
||||
var document = exports.createDocument(html);
|
||||
if (address !== undefined) { document._address = address; }
|
||||
return new impl.Window(document);
|
||||
};
|
||||
|
||||
exports.impl = impl;
|
||||
+933
@@ -0,0 +1,933 @@
|
||||
"use strict";
|
||||
/* jshint eqnull: true */
|
||||
/**
|
||||
* Zest (https://github.com/chjj/zest)
|
||||
* A css selector engine.
|
||||
* Copyright (c) 2011-2012, Christopher Jeffrey. (MIT Licensed)
|
||||
* Domino version based on Zest v0.1.3 with bugfixes applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
var window = Object.create(null, {
|
||||
location: { get: function() {
|
||||
throw new Error('window.location is not supported.');
|
||||
} }
|
||||
});
|
||||
|
||||
var compareDocumentPosition = function(a, b) {
|
||||
return a.compareDocumentPosition(b);
|
||||
};
|
||||
|
||||
var order = function(a, b) {
|
||||
/* jshint bitwise: false */
|
||||
return compareDocumentPosition(a, b) & 2 ? 1 : -1;
|
||||
};
|
||||
|
||||
var next = function(el) {
|
||||
while ((el = el.nextSibling)
|
||||
&& el.nodeType !== 1);
|
||||
return el;
|
||||
};
|
||||
|
||||
var prev = function(el) {
|
||||
while ((el = el.previousSibling)
|
||||
&& el.nodeType !== 1);
|
||||
return el;
|
||||
};
|
||||
|
||||
var child = function(el) {
|
||||
/*jshint -W084 */
|
||||
if (el = el.firstChild) {
|
||||
while (el.nodeType !== 1
|
||||
&& (el = el.nextSibling));
|
||||
}
|
||||
return el;
|
||||
};
|
||||
|
||||
var lastChild = function(el) {
|
||||
/*jshint -W084 */
|
||||
if (el = el.lastChild) {
|
||||
while (el.nodeType !== 1
|
||||
&& (el = el.previousSibling));
|
||||
}
|
||||
return el;
|
||||
};
|
||||
|
||||
var parentIsElement = function(n) {
|
||||
if (!n.parentNode) { return false; }
|
||||
var nodeType = n.parentNode.nodeType;
|
||||
// The root `html` element can be a first- or last-child, too.
|
||||
return nodeType === 1 || nodeType === 9;
|
||||
};
|
||||
|
||||
var unquote = function(str) {
|
||||
if (!str) return str;
|
||||
var ch = str[0];
|
||||
if (ch === '"' || ch === '\'') {
|
||||
if (str[str.length-1] === ch) {
|
||||
str = str.slice(1, -1);
|
||||
} else {
|
||||
// bad string.
|
||||
str = str.slice(1);
|
||||
}
|
||||
return str.replace(rules.str_escape, function(s) {
|
||||
var m = /^\\(?:([0-9A-Fa-f]+)|([\r\n\f]+))/.exec(s);
|
||||
if (!m) { return s.slice(1); }
|
||||
if (m[2]) { return ''; /* escaped newlines are ignored in strings. */ }
|
||||
var cp = parseInt(m[1], 16);
|
||||
return String.fromCodePoint ? String.fromCodePoint(cp) :
|
||||
// Not all JavaScript implementations have String.fromCodePoint yet.
|
||||
String.fromCharCode(cp);
|
||||
});
|
||||
} else if (rules.ident.test(str)) {
|
||||
return decodeid(str);
|
||||
} else {
|
||||
// NUMBER, PERCENTAGE, DIMENSION, etc
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
var decodeid = function(str) {
|
||||
return str.replace(rules.escape, function(s) {
|
||||
var m = /^\\([0-9A-Fa-f]+)/.exec(s);
|
||||
if (!m) { return s[1]; }
|
||||
var cp = parseInt(m[1], 16);
|
||||
return String.fromCodePoint ? String.fromCodePoint(cp) :
|
||||
// Not all JavaScript implementations have String.fromCodePoint yet.
|
||||
String.fromCharCode(cp);
|
||||
});
|
||||
};
|
||||
|
||||
var indexOf = (function() {
|
||||
if (Array.prototype.indexOf) {
|
||||
return Array.prototype.indexOf;
|
||||
}
|
||||
return function(obj, item) {
|
||||
var i = this.length;
|
||||
while (i--) {
|
||||
if (this[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
})();
|
||||
|
||||
var makeInside = function(start, end) {
|
||||
var regex = rules.inside.source
|
||||
.replace(/</g, start)
|
||||
.replace(/>/g, end);
|
||||
|
||||
return new RegExp(regex);
|
||||
};
|
||||
|
||||
var replace = function(regex, name, val) {
|
||||
regex = regex.source;
|
||||
regex = regex.replace(name, val.source || val);
|
||||
return new RegExp(regex);
|
||||
};
|
||||
|
||||
var truncateUrl = function(url, num) {
|
||||
return url
|
||||
.replace(/^(?:\w+:\/\/|\/+)/, '')
|
||||
.replace(/(?:\/+|\/*#.*?)$/, '')
|
||||
.split('/', num)
|
||||
.join('/');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle `nth` Selectors
|
||||
*/
|
||||
|
||||
var parseNth = function(param_, test) {
|
||||
var param = param_.replace(/\s+/g, '')
|
||||
, cap;
|
||||
|
||||
if (param === 'even') {
|
||||
param = '2n+0';
|
||||
} else if (param === 'odd') {
|
||||
param = '2n+1';
|
||||
} else if (param.indexOf('n') === -1) {
|
||||
param = '0n' + param;
|
||||
}
|
||||
|
||||
cap = /^([+-])?(\d+)?n([+-])?(\d+)?$/.exec(param);
|
||||
|
||||
return {
|
||||
group: cap[1] === '-'
|
||||
? -(cap[2] || 1)
|
||||
: +(cap[2] || 1),
|
||||
offset: cap[4]
|
||||
? (cap[3] === '-' ? -cap[4] : +cap[4])
|
||||
: 0
|
||||
};
|
||||
};
|
||||
|
||||
var nth = function(param_, test, last) {
|
||||
var param = parseNth(param_)
|
||||
, group = param.group
|
||||
, offset = param.offset
|
||||
, find = !last ? child : lastChild
|
||||
, advance = !last ? next : prev;
|
||||
|
||||
return function(el) {
|
||||
if (!parentIsElement(el)) return;
|
||||
|
||||
var rel = find(el.parentNode)
|
||||
, pos = 0;
|
||||
|
||||
while (rel) {
|
||||
if (test(rel, el)) pos++;
|
||||
if (rel === el) {
|
||||
pos -= offset;
|
||||
return group && pos
|
||||
? (pos % group) === 0 && (pos < 0 === group < 0)
|
||||
: !pos;
|
||||
}
|
||||
rel = advance(rel);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple Selectors
|
||||
*/
|
||||
|
||||
var selectors = {
|
||||
'*': (function() {
|
||||
if (false/*function() {
|
||||
var el = document.createElement('div');
|
||||
el.appendChild(document.createComment(''));
|
||||
return !!el.getElementsByTagName('*')[0];
|
||||
}()*/) {
|
||||
return function(el) {
|
||||
if (el.nodeType === 1) return true;
|
||||
};
|
||||
}
|
||||
return function() {
|
||||
return true;
|
||||
};
|
||||
})(),
|
||||
'type': function(type) {
|
||||
type = type.toLowerCase();
|
||||
return function(el) {
|
||||
return el.nodeName.toLowerCase() === type;
|
||||
};
|
||||
},
|
||||
'attr': function(key, op, val, i) {
|
||||
op = operators[op];
|
||||
return function(el) {
|
||||
var attr;
|
||||
switch (key) {
|
||||
case 'for':
|
||||
attr = el.htmlFor;
|
||||
break;
|
||||
case 'class':
|
||||
// className is '' when non-existent
|
||||
// getAttribute('class') is null
|
||||
attr = el.className;
|
||||
if (attr === '' && el.getAttribute('class') == null) {
|
||||
attr = null;
|
||||
}
|
||||
break;
|
||||
case 'href':
|
||||
case 'src':
|
||||
attr = el.getAttribute(key, 2);
|
||||
break;
|
||||
case 'title':
|
||||
// getAttribute('title') can be '' when non-existent sometimes?
|
||||
attr = el.getAttribute('title') || null;
|
||||
break;
|
||||
// careful with attributes with special getter functions
|
||||
case 'id':
|
||||
case 'lang':
|
||||
case 'dir':
|
||||
case 'accessKey':
|
||||
case 'hidden':
|
||||
case 'tabIndex':
|
||||
case 'style':
|
||||
if (el.getAttribute) {
|
||||
attr = el.getAttribute(key);
|
||||
break;
|
||||
}
|
||||
/* falls through */
|
||||
default:
|
||||
if (el.hasAttribute && !el.hasAttribute(key)) {
|
||||
break;
|
||||
}
|
||||
attr = el[key] != null
|
||||
? el[key]
|
||||
: el.getAttribute && el.getAttribute(key);
|
||||
break;
|
||||
}
|
||||
if (attr == null) return;
|
||||
attr = attr + '';
|
||||
if (i) {
|
||||
attr = attr.toLowerCase();
|
||||
val = val.toLowerCase();
|
||||
}
|
||||
return op(attr, val);
|
||||
};
|
||||
},
|
||||
':first-child': function(el) {
|
||||
return !prev(el) && parentIsElement(el);
|
||||
},
|
||||
':last-child': function(el) {
|
||||
return !next(el) && parentIsElement(el);
|
||||
},
|
||||
':only-child': function(el) {
|
||||
return !prev(el) && !next(el) && parentIsElement(el);
|
||||
},
|
||||
':nth-child': function(param, last) {
|
||||
return nth(param, function() {
|
||||
return true;
|
||||
}, last);
|
||||
},
|
||||
':nth-last-child': function(param) {
|
||||
return selectors[':nth-child'](param, true);
|
||||
},
|
||||
':root': function(el) {
|
||||
return el.ownerDocument.documentElement === el;
|
||||
},
|
||||
':empty': function(el) {
|
||||
return !el.firstChild;
|
||||
},
|
||||
':not': function(sel) {
|
||||
var test = compileGroup(sel);
|
||||
return function(el) {
|
||||
return !test(el);
|
||||
};
|
||||
},
|
||||
':first-of-type': function(el) {
|
||||
if (!parentIsElement(el)) return;
|
||||
var type = el.nodeName;
|
||||
/*jshint -W084 */
|
||||
while (el = prev(el)) {
|
||||
if (el.nodeName === type) return;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
':last-of-type': function(el) {
|
||||
if (!parentIsElement(el)) return;
|
||||
var type = el.nodeName;
|
||||
/*jshint -W084 */
|
||||
while (el = next(el)) {
|
||||
if (el.nodeName === type) return;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
':only-of-type': function(el) {
|
||||
return selectors[':first-of-type'](el)
|
||||
&& selectors[':last-of-type'](el);
|
||||
},
|
||||
':nth-of-type': function(param, last) {
|
||||
return nth(param, function(rel, el) {
|
||||
return rel.nodeName === el.nodeName;
|
||||
}, last);
|
||||
},
|
||||
':nth-last-of-type': function(param) {
|
||||
return selectors[':nth-of-type'](param, true);
|
||||
},
|
||||
':checked': function(el) {
|
||||
return !!(el.checked || el.selected);
|
||||
},
|
||||
':indeterminate': function(el) {
|
||||
return !selectors[':checked'](el);
|
||||
},
|
||||
':enabled': function(el) {
|
||||
return !el.disabled && el.type !== 'hidden';
|
||||
},
|
||||
':disabled': function(el) {
|
||||
return !!el.disabled;
|
||||
},
|
||||
':target': function(el) {
|
||||
return el.id === window.location.hash.substring(1);
|
||||
},
|
||||
':focus': function(el) {
|
||||
return el === el.ownerDocument.activeElement;
|
||||
},
|
||||
':is': function(sel) {
|
||||
return compileGroup(sel);
|
||||
},
|
||||
// :matches is an older name for :is; see
|
||||
// https://github.com/w3c/csswg-drafts/issues/3258
|
||||
':matches': function(sel) {
|
||||
return selectors[':is'](sel);
|
||||
},
|
||||
':nth-match': function(param, last) {
|
||||
var args = param.split(/\s*,\s*/)
|
||||
, arg = args.shift()
|
||||
, test = compileGroup(args.join(','));
|
||||
|
||||
return nth(arg, test, last);
|
||||
},
|
||||
':nth-last-match': function(param) {
|
||||
return selectors[':nth-match'](param, true);
|
||||
},
|
||||
':links-here': function(el) {
|
||||
return el + '' === window.location + '';
|
||||
},
|
||||
':lang': function(param) {
|
||||
return function(el) {
|
||||
while (el) {
|
||||
if (el.lang) return el.lang.indexOf(param) === 0;
|
||||
el = el.parentNode;
|
||||
}
|
||||
};
|
||||
},
|
||||
':dir': function(param) {
|
||||
return function(el) {
|
||||
while (el) {
|
||||
if (el.dir) return el.dir === param;
|
||||
el = el.parentNode;
|
||||
}
|
||||
};
|
||||
},
|
||||
':scope': function(el, con) {
|
||||
var context = con || el.ownerDocument;
|
||||
if (context.nodeType === 9) {
|
||||
return el === context.documentElement;
|
||||
}
|
||||
return el === context;
|
||||
},
|
||||
':any-link': function(el) {
|
||||
return typeof el.href === 'string';
|
||||
},
|
||||
':local-link': function(el) {
|
||||
if (el.nodeName) {
|
||||
return el.href && el.host === window.location.host;
|
||||
}
|
||||
var param = +el + 1;
|
||||
return function(el) {
|
||||
if (!el.href) return;
|
||||
|
||||
var url = window.location + ''
|
||||
, href = el + '';
|
||||
|
||||
return truncateUrl(url, param) === truncateUrl(href, param);
|
||||
};
|
||||
},
|
||||
':default': function(el) {
|
||||
return !!el.defaultSelected;
|
||||
},
|
||||
':valid': function(el) {
|
||||
return el.willValidate || (el.validity && el.validity.valid);
|
||||
},
|
||||
':invalid': function(el) {
|
||||
return !selectors[':valid'](el);
|
||||
},
|
||||
':in-range': function(el) {
|
||||
return el.value > el.min && el.value <= el.max;
|
||||
},
|
||||
':out-of-range': function(el) {
|
||||
return !selectors[':in-range'](el);
|
||||
},
|
||||
':required': function(el) {
|
||||
return !!el.required;
|
||||
},
|
||||
':optional': function(el) {
|
||||
return !el.required;
|
||||
},
|
||||
':read-only': function(el) {
|
||||
if (el.readOnly) return true;
|
||||
|
||||
var attr = el.getAttribute('contenteditable')
|
||||
, prop = el.contentEditable
|
||||
, name = el.nodeName.toLowerCase();
|
||||
|
||||
name = name !== 'input' && name !== 'textarea';
|
||||
|
||||
return (name || el.disabled) && attr == null && prop !== 'true';
|
||||
},
|
||||
':read-write': function(el) {
|
||||
return !selectors[':read-only'](el);
|
||||
},
|
||||
':hover': function() {
|
||||
throw new Error(':hover is not supported.');
|
||||
},
|
||||
':active': function() {
|
||||
throw new Error(':active is not supported.');
|
||||
},
|
||||
':link': function() {
|
||||
throw new Error(':link is not supported.');
|
||||
},
|
||||
':visited': function() {
|
||||
throw new Error(':visited is not supported.');
|
||||
},
|
||||
':column': function() {
|
||||
throw new Error(':column is not supported.');
|
||||
},
|
||||
':nth-column': function() {
|
||||
throw new Error(':nth-column is not supported.');
|
||||
},
|
||||
':nth-last-column': function() {
|
||||
throw new Error(':nth-last-column is not supported.');
|
||||
},
|
||||
':current': function() {
|
||||
throw new Error(':current is not supported.');
|
||||
},
|
||||
':past': function() {
|
||||
throw new Error(':past is not supported.');
|
||||
},
|
||||
':future': function() {
|
||||
throw new Error(':future is not supported.');
|
||||
},
|
||||
// Non-standard, for compatibility purposes.
|
||||
':contains': function(param) {
|
||||
return function(el) {
|
||||
var text = el.innerText || el.textContent || el.value || '';
|
||||
return text.indexOf(param) !== -1;
|
||||
};
|
||||
},
|
||||
':has': function(param) {
|
||||
return function(el) {
|
||||
return find(param, el).length > 0;
|
||||
};
|
||||
}
|
||||
// Potentially add more pseudo selectors for
|
||||
// compatibility with sizzle and most other
|
||||
// selector engines (?).
|
||||
};
|
||||
|
||||
/**
|
||||
* Attribute Operators
|
||||
*/
|
||||
|
||||
var operators = {
|
||||
'-': function() {
|
||||
return true;
|
||||
},
|
||||
'=': function(attr, val) {
|
||||
return attr === val;
|
||||
},
|
||||
'*=': function(attr, val) {
|
||||
return attr.indexOf(val) !== -1;
|
||||
},
|
||||
'~=': function(attr, val) {
|
||||
var i
|
||||
, s
|
||||
, f
|
||||
, l;
|
||||
|
||||
for (s = 0; true; s = i + 1) {
|
||||
i = attr.indexOf(val, s);
|
||||
if (i === -1) return false;
|
||||
f = attr[i - 1];
|
||||
l = attr[i + val.length];
|
||||
if ((!f || f === ' ') && (!l || l === ' ')) return true;
|
||||
}
|
||||
},
|
||||
'|=': function(attr, val) {
|
||||
var i = attr.indexOf(val)
|
||||
, l;
|
||||
|
||||
if (i !== 0) return;
|
||||
l = attr[i + val.length];
|
||||
|
||||
return l === '-' || !l;
|
||||
},
|
||||
'^=': function(attr, val) {
|
||||
return attr.indexOf(val) === 0;
|
||||
},
|
||||
'$=': function(attr, val) {
|
||||
var i = attr.lastIndexOf(val);
|
||||
return i !== -1 && i + val.length === attr.length;
|
||||
},
|
||||
// non-standard
|
||||
'!=': function(attr, val) {
|
||||
return attr !== val;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Combinator Logic
|
||||
*/
|
||||
|
||||
var combinators = {
|
||||
' ': function(test) {
|
||||
return function(el) {
|
||||
/*jshint -W084 */
|
||||
while (el = el.parentNode) {
|
||||
if (test(el)) return el;
|
||||
}
|
||||
};
|
||||
},
|
||||
'>': function(test) {
|
||||
return function(el) {
|
||||
/*jshint -W084 */
|
||||
if (el = el.parentNode) {
|
||||
return test(el) && el;
|
||||
}
|
||||
};
|
||||
},
|
||||
'+': function(test) {
|
||||
return function(el) {
|
||||
/*jshint -W084 */
|
||||
if (el = prev(el)) {
|
||||
return test(el) && el;
|
||||
}
|
||||
};
|
||||
},
|
||||
'~': function(test) {
|
||||
return function(el) {
|
||||
/*jshint -W084 */
|
||||
while (el = prev(el)) {
|
||||
if (test(el)) return el;
|
||||
}
|
||||
};
|
||||
},
|
||||
'noop': function(test) {
|
||||
return function(el) {
|
||||
return test(el) && el;
|
||||
};
|
||||
},
|
||||
'ref': function(test, name) {
|
||||
var node;
|
||||
|
||||
function ref(el) {
|
||||
var doc = el.ownerDocument
|
||||
, nodes = doc.getElementsByTagName('*')
|
||||
, i = nodes.length;
|
||||
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
if (ref.test(el)) {
|
||||
node = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
node = null;
|
||||
}
|
||||
|
||||
ref.combinator = function(el) {
|
||||
if (!node || !node.getAttribute) return;
|
||||
|
||||
var attr = node.getAttribute(name) || '';
|
||||
if (attr[0] === '#') attr = attr.substring(1);
|
||||
|
||||
if (attr === el.id && test(node)) {
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
return ref;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Grammar
|
||||
*/
|
||||
|
||||
var rules = {
|
||||
escape: /\\(?:[^0-9A-Fa-f\r\n]|[0-9A-Fa-f]{1,6}[\r\n\t ]?)/g,
|
||||
str_escape: /(escape)|\\(\n|\r\n?|\f)/g,
|
||||
nonascii: /[\u00A0-\uFFFF]/,
|
||||
cssid: /(?:(?!-?[0-9])(?:escape|nonascii|[-_a-zA-Z0-9])+)/,
|
||||
qname: /^ *(cssid|\*)/,
|
||||
simple: /^(?:([.#]cssid)|pseudo|attr)/,
|
||||
ref: /^ *\/(cssid)\/ */,
|
||||
combinator: /^(?: +([^ \w*.#\\]) +|( )+|([^ \w*.#\\]))(?! *$)/,
|
||||
attr: /^\[(cssid)(?:([^\w]?=)(inside))?\]/,
|
||||
pseudo: /^(:cssid)(?:\((inside)\))?/,
|
||||
inside: /(?:"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|<[^"'>]*>|\\["'>]|[^"'>])*/,
|
||||
ident: /^(cssid)$/
|
||||
};
|
||||
|
||||
rules.cssid = replace(rules.cssid, 'nonascii', rules.nonascii);
|
||||
rules.cssid = replace(rules.cssid, 'escape', rules.escape);
|
||||
rules.qname = replace(rules.qname, 'cssid', rules.cssid);
|
||||
rules.simple = replace(rules.simple, 'cssid', rules.cssid);
|
||||
rules.ref = replace(rules.ref, 'cssid', rules.cssid);
|
||||
rules.attr = replace(rules.attr, 'cssid', rules.cssid);
|
||||
rules.pseudo = replace(rules.pseudo, 'cssid', rules.cssid);
|
||||
rules.inside = replace(rules.inside, '[^"\'>]*', rules.inside);
|
||||
rules.attr = replace(rules.attr, 'inside', makeInside('\\[', '\\]'));
|
||||
rules.pseudo = replace(rules.pseudo, 'inside', makeInside('\\(', '\\)'));
|
||||
rules.simple = replace(rules.simple, 'pseudo', rules.pseudo);
|
||||
rules.simple = replace(rules.simple, 'attr', rules.attr);
|
||||
rules.ident = replace(rules.ident, 'cssid', rules.cssid);
|
||||
rules.str_escape = replace(rules.str_escape, 'escape', rules.escape);
|
||||
|
||||
/**
|
||||
* Compiling
|
||||
*/
|
||||
|
||||
var compile = function(sel_) {
|
||||
var sel = sel_.replace(/^\s+|\s+$/g, '')
|
||||
, test
|
||||
, filter = []
|
||||
, buff = []
|
||||
, subject
|
||||
, qname
|
||||
, cap
|
||||
, op
|
||||
, ref;
|
||||
|
||||
/*jshint -W084 */
|
||||
while (sel) {
|
||||
if (cap = rules.qname.exec(sel)) {
|
||||
sel = sel.substring(cap[0].length);
|
||||
qname = decodeid(cap[1]);
|
||||
buff.push(tok(qname, true));
|
||||
} else if (cap = rules.simple.exec(sel)) {
|
||||
sel = sel.substring(cap[0].length);
|
||||
qname = '*';
|
||||
buff.push(tok(qname, true));
|
||||
buff.push(tok(cap));
|
||||
} else {
|
||||
throw new SyntaxError('Invalid selector.');
|
||||
}
|
||||
|
||||
while (cap = rules.simple.exec(sel)) {
|
||||
sel = sel.substring(cap[0].length);
|
||||
buff.push(tok(cap));
|
||||
}
|
||||
|
||||
if (sel[0] === '!') {
|
||||
sel = sel.substring(1);
|
||||
subject = makeSubject();
|
||||
subject.qname = qname;
|
||||
buff.push(subject.simple);
|
||||
}
|
||||
|
||||
if (cap = rules.ref.exec(sel)) {
|
||||
sel = sel.substring(cap[0].length);
|
||||
ref = combinators.ref(makeSimple(buff), decodeid(cap[1]));
|
||||
filter.push(ref.combinator);
|
||||
buff = [];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cap = rules.combinator.exec(sel)) {
|
||||
sel = sel.substring(cap[0].length);
|
||||
op = cap[1] || cap[2] || cap[3];
|
||||
if (op === ',') {
|
||||
filter.push(combinators.noop(makeSimple(buff)));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
op = 'noop';
|
||||
}
|
||||
|
||||
if (!combinators[op]) { throw new SyntaxError('Bad combinator.'); }
|
||||
filter.push(combinators[op](makeSimple(buff)));
|
||||
buff = [];
|
||||
}
|
||||
|
||||
test = makeTest(filter);
|
||||
test.qname = qname;
|
||||
test.sel = sel;
|
||||
|
||||
if (subject) {
|
||||
subject.lname = test.qname;
|
||||
|
||||
subject.test = test;
|
||||
subject.qname = subject.qname;
|
||||
subject.sel = test.sel;
|
||||
test = subject;
|
||||
}
|
||||
|
||||
if (ref) {
|
||||
ref.test = test;
|
||||
ref.qname = test.qname;
|
||||
ref.sel = test.sel;
|
||||
test = ref;
|
||||
}
|
||||
|
||||
return test;
|
||||
};
|
||||
|
||||
var tok = function(cap, qname) {
|
||||
// qname
|
||||
if (qname) {
|
||||
return cap === '*'
|
||||
? selectors['*']
|
||||
: selectors.type(cap);
|
||||
}
|
||||
|
||||
// class/id
|
||||
if (cap[1]) {
|
||||
return cap[1][0] === '.'
|
||||
// XXX unescape here? or in attr?
|
||||
? selectors.attr('class', '~=', decodeid(cap[1].substring(1)), false)
|
||||
: selectors.attr('id', '=', decodeid(cap[1].substring(1)), false);
|
||||
}
|
||||
|
||||
// pseudo-name
|
||||
// inside-pseudo
|
||||
if (cap[2]) {
|
||||
return cap[3]
|
||||
? selectors[decodeid(cap[2])](unquote(cap[3]))
|
||||
: selectors[decodeid(cap[2])];
|
||||
}
|
||||
|
||||
// attr name
|
||||
// attr op
|
||||
// attr value
|
||||
if (cap[4]) {
|
||||
var value = cap[6];
|
||||
var i = /["'\s]\s*I$/i.test(value);
|
||||
if (i) {
|
||||
value = value.replace(/\s*I$/i, '');
|
||||
}
|
||||
return selectors.attr(decodeid(cap[4]), cap[5] || '-', unquote(value), i);
|
||||
}
|
||||
|
||||
throw new SyntaxError('Unknown Selector.');
|
||||
};
|
||||
|
||||
var makeSimple = function(func) {
|
||||
var l = func.length
|
||||
, i;
|
||||
|
||||
// Potentially make sure
|
||||
// `el` is truthy.
|
||||
if (l < 2) return func[0];
|
||||
|
||||
return function(el) {
|
||||
if (!el) return;
|
||||
for (i = 0; i < l; i++) {
|
||||
if (!func[i](el)) return;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
var makeTest = function(func) {
|
||||
if (func.length < 2) {
|
||||
return function(el) {
|
||||
return !!func[0](el);
|
||||
};
|
||||
}
|
||||
return function(el) {
|
||||
var i = func.length;
|
||||
while (i--) {
|
||||
if (!(el = func[i](el))) return;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
var makeSubject = function() {
|
||||
var target;
|
||||
|
||||
function subject(el) {
|
||||
var node = el.ownerDocument
|
||||
, scope = node.getElementsByTagName(subject.lname)
|
||||
, i = scope.length;
|
||||
|
||||
while (i--) {
|
||||
if (subject.test(scope[i]) && target === el) {
|
||||
target = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
target = null;
|
||||
}
|
||||
|
||||
subject.simple = function(el) {
|
||||
target = el;
|
||||
return true;
|
||||
};
|
||||
|
||||
return subject;
|
||||
};
|
||||
|
||||
var compileGroup = function(sel) {
|
||||
var test = compile(sel)
|
||||
, tests = [ test ];
|
||||
|
||||
while (test.sel) {
|
||||
test = compile(test.sel);
|
||||
tests.push(test);
|
||||
}
|
||||
|
||||
if (tests.length < 2) return test;
|
||||
|
||||
return function(el) {
|
||||
var l = tests.length
|
||||
, i = 0;
|
||||
|
||||
for (; i < l; i++) {
|
||||
if (tests[i](el)) return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Selection
|
||||
*/
|
||||
|
||||
var find = function(sel, node) {
|
||||
var results = []
|
||||
, test = compile(sel)
|
||||
, scope = node.getElementsByTagName(test.qname)
|
||||
, i = 0
|
||||
, el;
|
||||
|
||||
/*jshint -W084 */
|
||||
while (el = scope[i++]) {
|
||||
if (test(el)) results.push(el);
|
||||
}
|
||||
|
||||
if (test.sel) {
|
||||
while (test.sel) {
|
||||
test = compile(test.sel);
|
||||
scope = node.getElementsByTagName(test.qname);
|
||||
i = 0;
|
||||
/*jshint -W084 */
|
||||
while (el = scope[i++]) {
|
||||
if (test(el) && indexOf.call(results, el) === -1) {
|
||||
results.push(el);
|
||||
}
|
||||
}
|
||||
}
|
||||
results.sort(order);
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = exports = function(sel, context) {
|
||||
/* when context isn't a DocumentFragment and the selector is simple: */
|
||||
var id, r;
|
||||
if (context.nodeType !== 11 && sel.indexOf(' ') === -1) {
|
||||
if (sel[0] === '#' && context.rooted && /^#[A-Z_][-A-Z0-9_]*$/i.test(sel)) {
|
||||
if (context.doc._hasMultipleElementsWithId) {
|
||||
id = sel.substring(1);
|
||||
if (!context.doc._hasMultipleElementsWithId(id)) {
|
||||
r = context.doc.getElementById(id);
|
||||
return r ? [r] : [];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sel[0] === '.' && /^\.\w+$/.test(sel)) {
|
||||
return context.getElementsByClassName(sel.substring(1));
|
||||
}
|
||||
if (/^\w+$/.test(sel)) {
|
||||
return context.getElementsByTagName(sel);
|
||||
}
|
||||
}
|
||||
/* do things the hard/slow way */
|
||||
return find(sel, context);
|
||||
};
|
||||
|
||||
exports.selectors = selectors;
|
||||
exports.operators = operators;
|
||||
exports.combinators = combinators;
|
||||
|
||||
exports.matches = function(el, sel) {
|
||||
var test = { sel: sel };
|
||||
do {
|
||||
test = compile(test.sel);
|
||||
if (test(el)) { return true; }
|
||||
} while (test.sel);
|
||||
return false;
|
||||
};
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
"use strict";
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// The below is a compiled copy of https://github.com/angular/angular/blob/92e41e9cb417223d9888a4c23b4c0e73188f87d0/packages/compiler/src/render3/view/style_parser.ts
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.hyphenate = exports.parse = void 0;
|
||||
/**
|
||||
* Parses string representation of a style and converts it into object literal.
|
||||
*
|
||||
* @param value string representation of style as used in the `style` attribute in HTML.
|
||||
* Example: `color: red; height: auto`.
|
||||
* @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
|
||||
* 'auto']`
|
||||
*/
|
||||
function parse(value) {
|
||||
// we use a string array here instead of a string map
|
||||
// because a string-map is not guaranteed to retain the
|
||||
// order of the entries whereas a string array can be
|
||||
// constructed in a [key, value, key, value] format.
|
||||
const styles = [];
|
||||
let i = 0;
|
||||
let parenDepth = 0;
|
||||
let quote = 0; /* Char.QuoteNone */
|
||||
let valueStart = 0;
|
||||
let propStart = 0;
|
||||
let currentProp = null;
|
||||
while (i < value.length) {
|
||||
const token = value.charCodeAt(i++);
|
||||
switch (token) {
|
||||
case 40 /* Char.OpenParen */:
|
||||
parenDepth++;
|
||||
break;
|
||||
case 41 /* Char.CloseParen */:
|
||||
parenDepth--;
|
||||
break;
|
||||
case 39 /* Char.QuoteSingle */:
|
||||
// valueStart needs to be there since prop values don't
|
||||
// have quotes in CSS
|
||||
if (quote === 0 /* Char.QuoteNone */) {
|
||||
quote = 39 /* Char.QuoteSingle */;
|
||||
} else if (
|
||||
quote === 39 /* Char.QuoteSingle */ &&
|
||||
value.charCodeAt(i - 1) !== 92 /* Char.BackSlash */
|
||||
) {
|
||||
quote = 0 /* Char.QuoteNone */;
|
||||
}
|
||||
break;
|
||||
case 34 /* Char.QuoteDouble */:
|
||||
// same logic as above
|
||||
if (quote === 0 /* Char.QuoteNone */) {
|
||||
quote = 34 /* Char.QuoteDouble */;
|
||||
} else if (
|
||||
quote === 34 /* Char.QuoteDouble */ &&
|
||||
value.charCodeAt(i - 1) !== 92 /* Char.BackSlash */
|
||||
) {
|
||||
quote = 0 /* Char.QuoteNone */;
|
||||
}
|
||||
break;
|
||||
case 58 /* Char.Colon */:
|
||||
if (
|
||||
!currentProp &&
|
||||
parenDepth === 0 &&
|
||||
quote === 0 /* Char.QuoteNone */
|
||||
) {
|
||||
currentProp = hyphenate(value.substring(propStart, i - 1).trim());
|
||||
valueStart = i;
|
||||
}
|
||||
break;
|
||||
case 59 /* Char.Semicolon */:
|
||||
if (
|
||||
currentProp &&
|
||||
valueStart > 0 &&
|
||||
parenDepth === 0 &&
|
||||
quote === 0 /* Char.QuoteNone */
|
||||
) {
|
||||
const styleVal = value.substring(valueStart, i - 1).trim();
|
||||
styles.push(currentProp, styleVal);
|
||||
propStart = i;
|
||||
valueStart = 0;
|
||||
currentProp = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currentProp && valueStart) {
|
||||
const styleVal = value.slice(valueStart).trim();
|
||||
styles.push(currentProp, styleVal);
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
exports.parse = parse;
|
||||
function hyphenate(value) {
|
||||
return value
|
||||
.replace(/[a-z][A-Z]/g, (v) => {
|
||||
return v.charAt(0) + "-" + v.charAt(1);
|
||||
})
|
||||
.toLowerCase();
|
||||
}
|
||||
exports.hyphenate = hyphenate;
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
"use strict";
|
||||
var Element = require('./Element');
|
||||
var defineElement = require('./defineElement');
|
||||
var utils = require('./utils');
|
||||
var CSSStyleDeclaration = require('./CSSStyleDeclaration');
|
||||
|
||||
var svgElements = exports.elements = {};
|
||||
var svgNameToImpl = Object.create(null);
|
||||
|
||||
exports.createElement = function(doc, localName, prefix) {
|
||||
var impl = svgNameToImpl[localName] || SVGElement;
|
||||
return new impl(doc, localName, prefix);
|
||||
};
|
||||
|
||||
function define(spec) {
|
||||
return defineElement(spec, SVGElement, svgElements, svgNameToImpl);
|
||||
}
|
||||
|
||||
var SVGElement = define({
|
||||
superclass: Element,
|
||||
name: 'SVGElement',
|
||||
ctor: function SVGElement(doc, localName, prefix) {
|
||||
Element.call(this, doc, localName, utils.NAMESPACE.SVG, prefix);
|
||||
},
|
||||
props: {
|
||||
style: { get: function() {
|
||||
if (!this._style)
|
||||
this._style = new CSSStyleDeclaration(this);
|
||||
return this._style;
|
||||
}}
|
||||
}
|
||||
});
|
||||
|
||||
define({
|
||||
name: 'SVGSVGElement',
|
||||
ctor: function SVGSVGElement(doc, localName, prefix) {
|
||||
SVGElement.call(this, doc, localName, prefix);
|
||||
},
|
||||
tag: 'svg',
|
||||
props: {
|
||||
createSVGRect: { value: function () {
|
||||
return exports.createElement(this.ownerDocument, 'rect', null);
|
||||
} }
|
||||
}
|
||||
});
|
||||
|
||||
define({
|
||||
tags: [
|
||||
'a', 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', 'animateMotion', 'animateTransform',
|
||||
'circle', 'clipPath', 'color-profile', 'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix',
|
||||
'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight',
|
||||
'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode',
|
||||
'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence', 'filter',
|
||||
'font', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignObject', 'g',
|
||||
'glyph', 'glyphRef', 'hkern', 'image', 'line', 'linearGradient', 'marker', 'mask', 'metadata', 'missing-glyph',
|
||||
'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'script', 'set', 'stop', 'style',
|
||||
'switch', 'symbol', 'text', 'textPath', 'title', 'tref', 'tspan', 'use', 'view', 'vkern'
|
||||
]
|
||||
});
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
var DOMException = require('./DOMException');
|
||||
var ERR = DOMException;
|
||||
var isApiWritable = require("./config").isApiWritable;
|
||||
|
||||
exports.NAMESPACE = {
|
||||
HTML: 'http://www.w3.org/1999/xhtml',
|
||||
XML: 'http://www.w3.org/XML/1998/namespace',
|
||||
XMLNS: 'http://www.w3.org/2000/xmlns/',
|
||||
MATHML: 'http://www.w3.org/1998/Math/MathML',
|
||||
SVG: 'http://www.w3.org/2000/svg',
|
||||
XLINK: 'http://www.w3.org/1999/xlink'
|
||||
};
|
||||
|
||||
//
|
||||
// Shortcut functions for throwing errors of various types.
|
||||
//
|
||||
exports.IndexSizeError = function() { throw new DOMException(ERR.INDEX_SIZE_ERR); };
|
||||
exports.HierarchyRequestError = function() { throw new DOMException(ERR.HIERARCHY_REQUEST_ERR); };
|
||||
exports.WrongDocumentError = function() { throw new DOMException(ERR.WRONG_DOCUMENT_ERR); };
|
||||
exports.InvalidCharacterError = function() { throw new DOMException(ERR.INVALID_CHARACTER_ERR); };
|
||||
exports.NoModificationAllowedError = function() { throw new DOMException(ERR.NO_MODIFICATION_ALLOWED_ERR); };
|
||||
exports.NotFoundError = function() { throw new DOMException(ERR.NOT_FOUND_ERR); };
|
||||
exports.NotSupportedError = function() { throw new DOMException(ERR.NOT_SUPPORTED_ERR); };
|
||||
exports.InvalidStateError = function() { throw new DOMException(ERR.INVALID_STATE_ERR); };
|
||||
exports.SyntaxError = function() { throw new DOMException(ERR.SYNTAX_ERR); };
|
||||
exports.InvalidModificationError = function() { throw new DOMException(ERR.INVALID_MODIFICATION_ERR); };
|
||||
exports.NamespaceError = function() { throw new DOMException(ERR.NAMESPACE_ERR); };
|
||||
exports.InvalidAccessError = function() { throw new DOMException(ERR.INVALID_ACCESS_ERR); };
|
||||
exports.TypeMismatchError = function() { throw new DOMException(ERR.TYPE_MISMATCH_ERR); };
|
||||
exports.SecurityError = function() { throw new DOMException(ERR.SECURITY_ERR); };
|
||||
exports.NetworkError = function() { throw new DOMException(ERR.NETWORK_ERR); };
|
||||
exports.AbortError = function() { throw new DOMException(ERR.ABORT_ERR); };
|
||||
exports.UrlMismatchError = function() { throw new DOMException(ERR.URL_MISMATCH_ERR); };
|
||||
exports.QuotaExceededError = function() { throw new DOMException(ERR.QUOTA_EXCEEDED_ERR); };
|
||||
exports.TimeoutError = function() { throw new DOMException(ERR.TIMEOUT_ERR); };
|
||||
exports.InvalidNodeTypeError = function() { throw new DOMException(ERR.INVALID_NODE_TYPE_ERR); };
|
||||
exports.DataCloneError = function() { throw new DOMException(ERR.DATA_CLONE_ERR); };
|
||||
|
||||
exports.nyi = function() {
|
||||
throw new Error("NotYetImplemented");
|
||||
};
|
||||
|
||||
exports.shouldOverride = function() {
|
||||
throw new Error("Abstract function; should be overriding in subclass.");
|
||||
};
|
||||
|
||||
exports.assert = function(expr, msg) {
|
||||
if (!expr) {
|
||||
throw new Error("Assertion failed: " + (msg || "") + "\n" + new Error().stack);
|
||||
}
|
||||
};
|
||||
|
||||
exports.expose = function(src, c) {
|
||||
for (var n in src) {
|
||||
Object.defineProperty(c.prototype, n, { value: src[n], writable: isApiWritable });
|
||||
}
|
||||
};
|
||||
|
||||
exports.merge = function(a, b) {
|
||||
for (var n in b) {
|
||||
a[n] = b[n];
|
||||
}
|
||||
};
|
||||
|
||||
// Compare two nodes based on their document order. This function is intended
|
||||
// to be passed to sort(). Assumes that the array being sorted does not
|
||||
// contain duplicates. And that all nodes are connected and comparable.
|
||||
// Clever code by ppk via jeresig.
|
||||
exports.documentOrder = function(n,m) {
|
||||
/* jshint bitwise: false */
|
||||
return 3 - (n.compareDocumentPosition(m) & 6);
|
||||
};
|
||||
|
||||
exports.toASCIILowerCase = function(s) {
|
||||
return s.replace(/[A-Z]+/g, function(c) {
|
||||
return c.toLowerCase();
|
||||
});
|
||||
};
|
||||
|
||||
exports.toASCIIUpperCase = function(s) {
|
||||
return s.replace(/[a-z]+/g, function(c) {
|
||||
return c.toUpperCase();
|
||||
});
|
||||
};
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
"use strict";
|
||||
// This grammar is from the XML and XML Namespace specs. It specifies whether
|
||||
// a string (such as an element or attribute name) is a valid Name or QName.
|
||||
//
|
||||
// Name ::= NameStartChar (NameChar)*
|
||||
// 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]
|
||||
//
|
||||
// NameChar ::= NameStartChar | "-" | "." | [0-9] |
|
||||
// #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
|
||||
//
|
||||
// QName ::= PrefixedName| UnprefixedName
|
||||
// PrefixedName ::= Prefix ':' LocalPart
|
||||
// UnprefixedName ::= LocalPart
|
||||
// Prefix ::= NCName
|
||||
// LocalPart ::= NCName
|
||||
// NCName ::= Name - (Char* ':' Char*)
|
||||
// # An XML Name, minus the ":"
|
||||
//
|
||||
|
||||
exports.isValidName = isValidName;
|
||||
exports.isValidQName = isValidQName;
|
||||
|
||||
// Most names will be ASCII only. Try matching against simple regexps first
|
||||
var simplename = /^[_:A-Za-z][-.:\w]+$/;
|
||||
var simpleqname = /^([_A-Za-z][-.\w]+|[_A-Za-z][-.\w]+:[_A-Za-z][-.\w]+)$/;
|
||||
|
||||
// If the regular expressions above fail, try more complex ones that work
|
||||
// for any identifiers using codepoints from the Unicode BMP
|
||||
var ncnamestartchars = "_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02ff\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
|
||||
var ncnamechars = "-._A-Za-z0-9\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02ff\u0300-\u037D\u037F-\u1FFF\u200C\u200D\u203f\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
|
||||
|
||||
var ncname = "[" + ncnamestartchars + "][" + ncnamechars + "]*";
|
||||
var namestartchars = ncnamestartchars + ":";
|
||||
var namechars = ncnamechars + ":";
|
||||
var name = new RegExp("^[" + namestartchars + "]" + "[" + namechars + "]*$");
|
||||
var qname = new RegExp("^(" + ncname + "|" + ncname + ":" + ncname + ")$");
|
||||
|
||||
// XML says that these characters are also legal:
|
||||
// [#x10000-#xEFFFF]. So if the patterns above fail, and the
|
||||
// target string includes surrogates, then try the following
|
||||
// patterns that allow surrogates and then run an extra validation
|
||||
// step to make sure that the surrogates are in valid pairs and in
|
||||
// the right range. Note that since the characters \uf0000 to \u1f0000
|
||||
// are not allowed, it means that the high surrogate can only go up to
|
||||
// \uDB7f instead of \uDBFF.
|
||||
var hassurrogates = /[\uD800-\uDB7F\uDC00-\uDFFF]/;
|
||||
var surrogatechars = /[\uD800-\uDB7F\uDC00-\uDFFF]/g;
|
||||
var surrogatepairs = /[\uD800-\uDB7F][\uDC00-\uDFFF]/g;
|
||||
|
||||
// Modify the variables above to allow surrogates
|
||||
ncnamestartchars += "\uD800-\uDB7F\uDC00-\uDFFF";
|
||||
ncnamechars += "\uD800-\uDB7F\uDC00-\uDFFF";
|
||||
ncname = "[" + ncnamestartchars + "][" + ncnamechars + "]*";
|
||||
namestartchars = ncnamestartchars + ":";
|
||||
namechars = ncnamechars + ":";
|
||||
|
||||
// Build another set of regexps that include surrogates
|
||||
var surrogatename = new RegExp("^[" + namestartchars + "]" + "[" + namechars + "]*$");
|
||||
var surrogateqname = new RegExp("^(" + ncname + "|" + ncname + ":" + ncname + ")$");
|
||||
|
||||
function isValidName(s) {
|
||||
if (simplename.test(s)) return true; // Plain ASCII
|
||||
if (name.test(s)) return true; // Unicode BMP
|
||||
|
||||
// Maybe the tests above failed because s includes surrogate pairs
|
||||
// Most likely, though, they failed for some more basic syntax problem
|
||||
if (!hassurrogates.test(s)) return false;
|
||||
|
||||
// Is the string a valid name if we allow surrogates?
|
||||
if (!surrogatename.test(s)) return false;
|
||||
|
||||
// Finally, are the surrogates all correctly paired up?
|
||||
var chars = s.match(surrogatechars), pairs = s.match(surrogatepairs);
|
||||
return pairs !== null && 2*pairs.length === chars.length;
|
||||
}
|
||||
|
||||
function isValidQName(s) {
|
||||
if (simpleqname.test(s)) return true; // Plain ASCII
|
||||
if (qname.test(s)) return true; // Unicode BMP
|
||||
|
||||
if (!hassurrogates.test(s)) return false;
|
||||
if (!surrogateqname.test(s)) return false;
|
||||
var chars = s.match(surrogatechars), pairs = s.match(surrogatepairs);
|
||||
return pairs !== null && 2*pairs.length === chars.length;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@mixmark-io/domino",
|
||||
"version": "2.2.0",
|
||||
"license": "BSD-2-Clause",
|
||||
"author": "Felix Gnass <fgnass@gmail.com>",
|
||||
"description": "Server-side DOM implementation based on Mozilla's dom.js",
|
||||
"main": "./lib",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mixmark-io/domino.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"types": "lib/index.d.ts",
|
||||
"devDependencies": {
|
||||
"jquery": "^3.5.1",
|
||||
"mocha": "^6.2.3",
|
||||
"puppeteer": "^21.3.5",
|
||||
"should": "^13.2.3"
|
||||
}
|
||||
}
|
||||
+1500
File diff suppressed because it is too large
Load Diff
+13
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1 id="lorem">Lore Ipsum</h1>
|
||||
<p>
|
||||
Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam quis risus eget urna mollis ornare vel eu leo. Donec <a href="https://github.com">git</a> ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.
|
||||
</p>
|
||||
<p class="foo">
|
||||
Cras mattis <tt class="foo">consectetur</tt> purus sit amet fermentum. Donec ullamcorper nulla non metus auctor fringilla. Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus, nisi erat porttitor <tt class="foo bar baz">ligula</tt>, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla. Donec ullamcorper nulla non metus auctor fringilla.
|
||||
</p>
|
||||
<div id="tw"><div id="hello">Hello <em id="world" title="World: The Title">World</em></div>ignore<div id="foo" title="Foo: The Title">Foo, <strong id="bar">bar</strong></div></div>
|
||||
</body>
|
||||
</html>
|
||||
+9597
File diff suppressed because it is too large
Load Diff
+9831
File diff suppressed because it is too large
Load Diff
+80692
File diff suppressed because it is too large
Load Diff
+2
@@ -0,0 +1,2 @@
|
||||
exports.w3c = require('./w3c');
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
var domino = require('../');
|
||||
var html5lib_tests = require('./html5lib-tests.json');
|
||||
|
||||
// These test cases are taken from the `html5lib/html5lib-tests` package
|
||||
// on github, in the directory `tree-construction`. The filename in that
|
||||
// directory is the name of each suite of tests.
|
||||
|
||||
function cases(filename, tc) {
|
||||
return tc.filter(function(test) {
|
||||
// We don't support some of these test cases...
|
||||
if (test.fragment && test.fragment.ns) { return false; }
|
||||
// Scripting is always enabled in domino.
|
||||
if (test.script === 'off') { return false; }
|
||||
return true;
|
||||
}).reduce(function(r, test) {
|
||||
var input = test.data, expected = test.document.html,
|
||||
fragment = test.fragment && test.fragment.name;
|
||||
// Come up with a helpful name for the testcase.
|
||||
var trimmed = input, n, candidate;
|
||||
if (trimmed==='') { trimmed = '{no input}'; }
|
||||
if (fragment) { trimmed = fragment + ':' + trimmed; }
|
||||
if (r[trimmed]) {
|
||||
//console.warn("Duplicate test in "+filename+": "+trimmed);
|
||||
}
|
||||
for (n = 40; n < trimmed.length; n += 5) {
|
||||
candidate = trimmed.slice(0, n) + '...';
|
||||
if (!r[candidate]) { trimmed = candidate; break; }
|
||||
}
|
||||
if (/\n/.test(trimmed)) {
|
||||
candidate = trimmed.split(/\n/)[0] + '...';
|
||||
if (!r[candidate]) { trimmed = candidate; }
|
||||
}
|
||||
r[trimmed] = makeOneTest(fragment, input, expected);
|
||||
return r;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function makeOneTest(fragment, input, expected) {
|
||||
return function() {
|
||||
var doc, context;
|
||||
if (fragment) {
|
||||
doc = domino.createDocument();
|
||||
context = (fragment==='body') ? doc.body : doc.createElement(fragment);
|
||||
context.innerHTML = input;
|
||||
context.innerHTML.should.equal(expected);
|
||||
} else {
|
||||
doc = domino.createDocument(input, true);
|
||||
doc.outerHTML.should.equal(expected);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.parseAlgorithm = Object.keys(html5lib_tests).reduce(function(r, file) {
|
||||
r[file] = cases(file, html5lib_tests[file]);
|
||||
return r;
|
||||
}, {});
|
||||
|
||||
// Some extra tests.
|
||||
|
||||
// https://github.com/html5lib/html5lib-tests/issues/20
|
||||
exports.parseAlgorithm['github issue #20'] = {
|
||||
'test1': makeOneTest(
|
||||
'body', '<table><li><li></table>', '<li></li><li></li><table></table>'
|
||||
)
|
||||
};
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
/**
|
||||
* Process entities.json from the HTML5 spec into an array and regular
|
||||
* expression suitable for use in domino's HTMLParser.js implementation.
|
||||
*/
|
||||
var entities_json = process.argv[2];
|
||||
var entities = require(path.resolve(__dirname, entities_json));
|
||||
var keys = Object.keys(entities).map(function(s) {
|
||||
console.assert(s[0] === '&');
|
||||
return s.slice(1); // Don't include leading '&'
|
||||
}).sort();
|
||||
|
||||
var s = '';
|
||||
s += '/*\n';
|
||||
s += ' * This table is generated with test/tools/update-entities.js\n';
|
||||
s += ' */\n';
|
||||
s += 'var namedCharRefs = {\n __proto__: null,\n';
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
if (i%2==0) { s+=' '; } else { s += ' '; }
|
||||
s += JSON.stringify(keys[i]);
|
||||
s += ':';
|
||||
var c = entities['&' + keys[i]].characters;
|
||||
if (c.length === 1) {
|
||||
s += '0x' + c.charCodeAt(0).toString(16);
|
||||
} else {
|
||||
s += '[';
|
||||
for (var j=0; j<c.length; j++) {
|
||||
if (j>0) { s+=','; }
|
||||
s += '0x' + c.charCodeAt(j).toString(16);
|
||||
}
|
||||
s += ']';
|
||||
}
|
||||
s += ',';
|
||||
if (i%2==1 || i===keys.length-1) { s+='\n'; }
|
||||
}
|
||||
s += '};\n';
|
||||
|
||||
// Construct a regular expression matching exactly the keys of this table.
|
||||
var esc = function(s) { return s.replace(/[\^\\$*+?.()|{}\[\]\/]/g, '\\$&'); };
|
||||
var prefix = function(keys) {
|
||||
console.assert(keys.length>0 && keys[0].length>0);
|
||||
var first = '', subkeys, accept;
|
||||
var result = [];
|
||||
var emit = function() {
|
||||
if (first==='') { return; }
|
||||
var sub = subkeys.length > 0 ? prefix(subkeys) : [];
|
||||
if (accept) { sub.push(''); }
|
||||
sub = sub.length>1 ? ('(?:' + sub.join('|') + ')') : sub[0];
|
||||
if (sub==='(?:;|)') { sub = ';?'; /* optimization */ }
|
||||
result.push(esc(first) + sub);
|
||||
};
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
if (keys[i][0] !== first) {
|
||||
emit();
|
||||
first = keys[i][0];
|
||||
subkeys = [];
|
||||
accept = false;
|
||||
}
|
||||
if (keys[i].length>1) {
|
||||
subkeys.push(keys[i].slice(1));
|
||||
} else {
|
||||
accept = true;
|
||||
}
|
||||
}
|
||||
emit();
|
||||
return result;
|
||||
};
|
||||
var re = prefix(keys).join('|');
|
||||
s += '/*\n';
|
||||
s += ' * This regexp is generated with test/tools/update-entities.js\n';
|
||||
s += ' * It will always match at least one character -- but note that there\n';
|
||||
s += ' * are no entities whose names are a single character long.\n';
|
||||
s += ' */\n';
|
||||
s += 'var NAMEDCHARREF = /(' + re + ')|[\\s\\S]/g;\n';
|
||||
|
||||
// Verify the property mentioned in the comment above.
|
||||
var lens = keys.map(function(s) { return s.length; });
|
||||
var minlen = Math.min.apply(Math, lens);
|
||||
var maxlen = Math.max.apply(Math, lens);
|
||||
console.assert(minlen > 1);
|
||||
|
||||
s += '\nvar NAMEDCHARREF_MAXLEN = ' + maxlen + ';\n';
|
||||
|
||||
// Emit the result
|
||||
console.log(s);
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var process = require('process');
|
||||
var domino = require('../../');
|
||||
|
||||
/** Rebuild test/html5lib-tests.json based on the test specifications in
|
||||
* the html5lib-tests submodule.
|
||||
*/
|
||||
|
||||
var NAMESPACE = {
|
||||
html: 'http://www.w3.org/1999/xhtml',
|
||||
xml: 'http://www.w3.org/XML/1998/namespace',
|
||||
xmlns: 'http://www.w3.org/2000/xmlns/',
|
||||
math: 'http://www.w3.org/1998/Math/MathML',
|
||||
svg: 'http://www.w3.org/2000/svg',
|
||||
xlink: 'http://www.w3.org/1999/xlink'
|
||||
};
|
||||
// menuitem is no longer EMPTY, see https://github.com/whatwg/html/pull/907
|
||||
// This list comes from https://html.spec.whatwg.org/multipage/syntax.html#serialising-html-fragments
|
||||
var EMPTY = {
|
||||
area: true,
|
||||
base: true,
|
||||
basefont: true,
|
||||
bgsound: true,
|
||||
br: true,
|
||||
col: true,
|
||||
embed: true,
|
||||
frame: true,
|
||||
hr: true,
|
||||
img: true,
|
||||
input: true,
|
||||
keygen: true,
|
||||
link: true,
|
||||
meta: true,
|
||||
param: true,
|
||||
source: true,
|
||||
track: true,
|
||||
wbr: true
|
||||
};
|
||||
var EXTRA_NL = {
|
||||
/* Removed in https://github.com/whatwg/html/issues/944
|
||||
pre: true,
|
||||
textarea: true,
|
||||
listing: true
|
||||
*/
|
||||
};
|
||||
var NO_ESCAPE = {
|
||||
style: true, script: true, xmp:true, iframe:true, noembed:true,
|
||||
noframes:true, plaintext:true,
|
||||
noscript: true // <- assumes that scripting is enabled.
|
||||
};
|
||||
|
||||
var localname = function(namestring) {
|
||||
return namestring.replace(/^(svg|math|xlink|xml|xmlns) /, '');
|
||||
};
|
||||
var namespace = function(namestring) {
|
||||
var m = /^(svg|math|xlink|xml|xmlns) /.exec(namestring);
|
||||
// Save some space by using 'undefined' to represent the html namespace.
|
||||
return m ? NAMESPACE[m[1]] : undefined/*NAMESPACE.html*/;
|
||||
};
|
||||
|
||||
var ParseError = function ParseError(desc, filename, input) {
|
||||
Error.call(this);
|
||||
this.name = this.constructor.name;
|
||||
this.message = desc + ' ['+filename+']: ' + JSON.stringify(input);
|
||||
};
|
||||
ParseError.prototype = Object.create(Error.prototype);
|
||||
ParseError.prototype.constructor = ParseError;
|
||||
|
||||
|
||||
var list_tests = function() {
|
||||
var base = path.join(__dirname, '..', 'html5lib-tests', 'tree-construction');
|
||||
var testfiles = fs.readdirSync(base).filter(function(filename) {
|
||||
return /\.dat$/.test(filename);
|
||||
}).map(function(f) { return path.normalize(path.join(base, f)); });
|
||||
testfiles.sort();
|
||||
return testfiles;
|
||||
};
|
||||
|
||||
var parse_test_file = function(filename) {
|
||||
var basename = path.basename(filename, '.dat');
|
||||
var cases = fs.readFileSync(filename, 'utf8').replace(/\n$/,'')
|
||||
.split(/\n\n(?=#data\n)/g);
|
||||
return cases.map(function(c) {
|
||||
return twiddle_test(basename, parse_one_test(basename, c));
|
||||
});
|
||||
};
|
||||
|
||||
var parse_one_test = function(filename, testcase) {
|
||||
var m = /^#data\n(?:([^]*?)\n)?(?:#script-(on|off)\n)?#errors\n((?:[^\n]*\n)*?)(?:#document-fragment\n([^\n]*)\n)?(?:#script-(on|off)\n)?#document\n([^]*?)$/.exec(testcase+'\n');
|
||||
if (!m) {
|
||||
throw new ParseError("Can't parse test case", filename, testcase);
|
||||
}
|
||||
// According to the README, there should always be at least two newlines
|
||||
// between #data and #errors, but some test cases have only one.
|
||||
// `data` will be null in that case.
|
||||
var fragment = m[4] ? { name: localname(m[4]), ns:namespace(m[4]) } :
|
||||
undefined;
|
||||
return {
|
||||
//file: filename,
|
||||
data: m[1] || '',
|
||||
errors: m[3].split(/\n/g).slice(0,-1),
|
||||
fragment: fragment,
|
||||
script: m[2] || m[5],
|
||||
document: serialize_doc(filename, fragment, m[6])
|
||||
};
|
||||
};
|
||||
|
||||
// Parse the node tree spec, emitting a serialized output string as well
|
||||
// as a JSON representation of the tree.
|
||||
var serialize_doc = function(filename, fragment, doc) {
|
||||
var result = "", stack = [], can_add_attr = false, props = {tags:{}};
|
||||
var root = { children: [] }, parent, obj;
|
||||
if (fragment) { root.tag = fragment.name; root.ns = fragment.ns; }
|
||||
var clear_add_attr = function() {
|
||||
if (can_add_attr) {
|
||||
result += '>';
|
||||
can_add_attr = false;
|
||||
}
|
||||
};
|
||||
var pop_stack = function() {
|
||||
clear_add_attr();
|
||||
var old = stack.pop();
|
||||
if (old.content !== true) {
|
||||
if (old.ns===namespace('html') && EMPTY[old.tag]) {
|
||||
if (old.children.length > 0) {
|
||||
throw new ParseError("Empty elements ("+old.tag+") can't have children",
|
||||
filename, doc);
|
||||
}
|
||||
} else {
|
||||
result += '</' + old.tag + '>';
|
||||
}
|
||||
}
|
||||
// save some space in the JSON output by omitting empty lists
|
||||
if (old.children.length===0) { old.children = undefined; }
|
||||
if (old.attrs && old.attrs.length===0) { old.attrs = undefined; }
|
||||
return old;
|
||||
};
|
||||
var stack_top = function() {
|
||||
if (stack.length === 0) { return root; }
|
||||
return stack[stack.length-1];
|
||||
};
|
||||
var escape = function(s) {
|
||||
return s.replace(/[&<>\u00A0]/g, function(c) {
|
||||
switch(c) {
|
||||
case '&': return '&';
|
||||
case '<': return '<';
|
||||
case '>': return '>';
|
||||
case '\u00A0': return ' ';
|
||||
}
|
||||
});
|
||||
};
|
||||
var escapeAttr = function(s) {
|
||||
return s.replace(/[&"\u00A0]/g, function(c) {
|
||||
switch(c) {
|
||||
case '&': return '&';
|
||||
case '"': return '"';
|
||||
case '\u00A0': return ' ';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
while (doc.length > 0) {
|
||||
var m = /^\| ((?: )*)(?:<([^!?>][^>]*)>|([^="\n][^=\n]*)="([^"]*)"|"((?:[^"]|"(?!\n))*)"|<!-- ((?:[^](?!-->))*) -->|<!DOCTYPE ([^>]*)>|<\?([^>]+)>|(content))\n/.exec(doc);
|
||||
if (!m) {
|
||||
throw new ParseError('Bad document line', filename, doc);
|
||||
}
|
||||
doc = doc.slice(m[0].length);
|
||||
var indent = m[1].length / 2;
|
||||
while (indent < stack.length) {
|
||||
pop_stack();
|
||||
}
|
||||
if (indent !== stack.length) {
|
||||
throw new ParseError('Indentation error', filename, doc);
|
||||
}
|
||||
var tagname = m[2], attrname = m[3], attrvalue = m[4];
|
||||
var text = m[5], comment = m[6], doctype = m[7], processing = m[8];
|
||||
var template_content = m[9];
|
||||
if (attrname !== undefined) {
|
||||
if (!can_add_attr)
|
||||
throw new ParseError('Late attribute', filename, m);
|
||||
obj = {
|
||||
name:localname(attrname),
|
||||
ns:namespace(attrname),
|
||||
value:attrvalue
|
||||
};
|
||||
if (attrvalue !== escapeAttr(attrvalue)) {
|
||||
obj.escaped = props.escaped = true;
|
||||
}
|
||||
var serializedName;
|
||||
if (obj.ns === namespace('html')) {
|
||||
serializedName = obj.name;
|
||||
} else if (obj.ns === NAMESPACE.xml) {
|
||||
serializedName = 'xml:' + obj.name;
|
||||
} else if (obj.ns == NAMESPACE.xmlns) {
|
||||
if (obj.name === 'xmlns') {
|
||||
serializedName = 'xmlns';
|
||||
} else {
|
||||
serializedName = 'xmlns:' + obj.name;
|
||||
}
|
||||
} else if (obj.ns === NAMESPACE.xlink) {
|
||||
serializedName = 'xlink:' + obj.name;
|
||||
} else {
|
||||
throw new Error("don't know what qualified name to use");
|
||||
}
|
||||
result += ' ' + serializedName + '="' + escapeAttr(obj.value) + '"';
|
||||
stack_top().attrs.push(obj);
|
||||
if (/[<"]/.test(serializedName)) {
|
||||
props.attrWithFunnyChar = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
clear_add_attr();
|
||||
if (tagname !== undefined) {
|
||||
result += '<' + localname(tagname);
|
||||
can_add_attr = true;
|
||||
props.tags[tagname] = true;
|
||||
if (/</.test(tagname)) {
|
||||
props.tagWithLt = true;
|
||||
}
|
||||
parent = stack_top();
|
||||
stack.push({
|
||||
tag: localname(tagname),
|
||||
ns: namespace(tagname),
|
||||
attrs: [],
|
||||
children: []
|
||||
});
|
||||
parent.children.push(stack_top());
|
||||
continue;
|
||||
}
|
||||
if (text !== undefined) {
|
||||
obj = { text: text };
|
||||
if (stack_top().ns === namespace('html') &&
|
||||
NO_ESCAPE[stack_top().tag]) {
|
||||
obj.no_escape = props.no_escape = true;
|
||||
}
|
||||
if (stack_top().ns === namespace('html') &&
|
||||
EXTRA_NL[stack_top().tag] &&
|
||||
stack_top().children.length === 0 &&
|
||||
/^\n/.test(text)) {
|
||||
result += '\n';
|
||||
obj.extraNL = props.extraNL = true;
|
||||
}
|
||||
if (text !== escape(text) && !obj.no_escape) {
|
||||
obj.escaped = props.escaped = true;
|
||||
}
|
||||
result += obj.no_escape ? text : escape(text);
|
||||
stack_top().children.push(obj);
|
||||
continue;
|
||||
}
|
||||
if (comment !== undefined) {
|
||||
result += '<!--' + comment + '-->';
|
||||
props.comment = true;
|
||||
stack_top().children.push({ comment: comment });
|
||||
continue;
|
||||
}
|
||||
if (doctype !== undefined) {
|
||||
// HTML serialization spec says just include the name, not the
|
||||
// public or system identifiers.
|
||||
result += '<!DOCTYPE ' + doctype.replace(/ .*$/, '') + '>';
|
||||
props.doctype = true;
|
||||
stack_top().children.push({ doctype: doctype });
|
||||
continue;
|
||||
}
|
||||
if (processing !== undefined) {
|
||||
result += '<?' + processing + '>';
|
||||
props.processing = true;
|
||||
stack_top().children.push({ processing: processing });
|
||||
continue;
|
||||
}
|
||||
if (template_content !== undefined) {
|
||||
parent = stack_top();
|
||||
stack.push({content:true, children:[]});
|
||||
parent.children.push(stack_top());
|
||||
can_add_attr = false;
|
||||
props.template = true;
|
||||
continue;
|
||||
}
|
||||
throw new ParseError("Unknown line type", filename, m);
|
||||
}
|
||||
while (stack.length > 0) {
|
||||
pop_stack();
|
||||
}
|
||||
return {
|
||||
props: props,
|
||||
tree: root.children,
|
||||
html: result
|
||||
};
|
||||
};
|
||||
|
||||
var twiddle_test = function(filename, tc) {
|
||||
// Adjust the expected HTML serialization for some tests so that
|
||||
// output attribute order always matches input attributes order.
|
||||
var expected = tc.document.html;
|
||||
|
||||
// Tweak the order of attributes:
|
||||
if (/^isindex$/.test(filename) &&
|
||||
/<isindex name="A" action="B" prompt="C" foo="D"/.test(tc.data) &&
|
||||
/<isindex action="B" foo="D" name="A" prompt="C"/.test(expected)) {
|
||||
expected = expected.replace(/<(isindex) (action="B") (foo="D") (name="A") (prompt="C")/,
|
||||
'<$1 $4 $2 $5 $3');
|
||||
}
|
||||
if (/^tests(9|10)$/.test(filename) &&
|
||||
/<(g|mi) xml:lang=en xlink:href=foo/.test(tc.data) &&
|
||||
/<(g|mi) xlink:href="foo" xml:lang="en"/.test(expected)) {
|
||||
expected = expected.replace(/<(g|mi) (xlink[^> ]+) (xml[^> ]+)/g,
|
||||
'<$1 $3 $2');
|
||||
}
|
||||
if (filename==='tests19' &&
|
||||
/<html c=d>.*<html a=b>/.test(tc.data) &&
|
||||
/<html a="b" c="d">/.test(expected)) {
|
||||
expected = expected.replace(/a="b" c="d"/, 'c="d" a="b"');
|
||||
}
|
||||
if (filename==='tests19' &&
|
||||
/http-equiv="content-type" content="[^\"]+"/.test(tc.data) &&
|
||||
/content="[^\"]+" http-equiv="content-type"/.test(expected)) {
|
||||
expected = expected.replace(/(content=[^> ]+) (http-equiv=[^> ]+)/g, '$2 $1');
|
||||
}
|
||||
if (filename==='tests23' &&
|
||||
/size=4 id=a/.test(tc.data) &&
|
||||
/id="a" size="4"/.test(expected)) {
|
||||
expected = expected.replace(/(id=[^> ]+) (size=[^> ]+)/g, '$2 $1');
|
||||
}
|
||||
if (filename==='tests26' &&
|
||||
/<code code="" x<="">/.test(expected)) {
|
||||
expected = expected.replace(/(code=[^> ]+) (x<=[^> ]+)/g, '$2 $1');
|
||||
}
|
||||
if (filename==='webkit01' &&
|
||||
/<rdar: 6869687="" problem="">/.test(expected)) {
|
||||
expected = expected.replace(/(6869687=[^> ]+) (problem=[^> ]+)/g, '$2 $1');
|
||||
}
|
||||
tc.document.html = expected;
|
||||
// Will this pass if parsed as a <body> fragment in no-quirks mode?
|
||||
// This property is used by some third-party consumers of the parsed
|
||||
// tests.
|
||||
var dd = domino.createDocument();
|
||||
dd.body.innerHTML = tc.data;
|
||||
tc.document.noQuirksBodyHtml = dd.body.innerHTML;
|
||||
|
||||
return tc;
|
||||
};
|
||||
|
||||
var result = list_tests().reduce(function(result, filename){
|
||||
result[path.basename(filename)] = parse_test_file(filename);
|
||||
return result;
|
||||
}, {});
|
||||
//console.log(JSON.stringify(result, null, 2));
|
||||
if (process.argv[2]) {
|
||||
fs.writeFileSync(process.argv[2], JSON.stringify(result, null, 2), 'utf8');
|
||||
console.warn('Wrote', process.argv[2]);
|
||||
} else {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Document Object Model (DOM) Conformance Test Suites
|
||||
|
||||
http://www.w3.org/DOM/Test/
|
||||
|
||||
Since domino doesn't implement deprecated DOM features some tests are no longer relevant. These tests have been moved to the `obsolete` directory so that they are excluded from the suite.
|
||||
|
||||
## Attributes vs. Nodes
|
||||
|
||||
The majority of the excluded level1/core tests expect Attributes to inherit from the Node interface which is no longer required in DOM level 4. Also `Element.attributes` no longer returns a NamedNodeMap but a read-only array.
|
||||
|
||||
## Obsolete Attributes
|
||||
|
||||
Another big hunk of excluded tests checks the support of reflected attributes that have become obsolete in HTML 5.
|
||||
+438
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
Copyright (c) 2001-2005 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, Institut National de
|
||||
Recherche en Informatique et en Automatique, Keio University). All
|
||||
Rights Reserved. This program is distributed under the W3C's Software
|
||||
Intellectual Property License. This program is distributed in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
PURPOSE.
|
||||
See W3C License http://www.w3.org/Consortium/Legal/ for more details.
|
||||
*/
|
||||
|
||||
function assertSize(descr, expected, actual) {
|
||||
var actualSize;
|
||||
assertNotNull(descr, actual);
|
||||
actualSize = actual.length;
|
||||
assertEquals(descr, expected, actualSize);
|
||||
}
|
||||
|
||||
function assertEqualsAutoCase(context, descr, expected, actual) {
|
||||
if (builder.contentType == "text/html") {
|
||||
if(context == "attribute") {
|
||||
assertEquals(descr, expected.toLowerCase(), actual.toLowerCase());
|
||||
}
|
||||
else {
|
||||
assertEquals(descr, expected.toUpperCase(), actual);
|
||||
}
|
||||
}
|
||||
else {
|
||||
assertEquals(descr, expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function assertEqualsCollectionAutoCase(context, descr, expected, actual) {
|
||||
//
|
||||
// if they aren't the same size, they aren't equal
|
||||
assertEquals(descr, expected.length, actual.length);
|
||||
|
||||
//
|
||||
// if there length is the same, then every entry in the expected list
|
||||
// must appear once and only once in the actual list
|
||||
var expectedLen = expected.length;
|
||||
var expectedValue;
|
||||
var actualLen = actual.length;
|
||||
var i;
|
||||
var j;
|
||||
var matches;
|
||||
for(i = 0; i < expectedLen; i++) {
|
||||
matches = 0;
|
||||
expectedValue = expected[i];
|
||||
for(j = 0; j < actualLen; j++) {
|
||||
if (builder.contentType == "text/html") {
|
||||
if (context == "attribute") {
|
||||
if (expectedValue.toLowerCase() == actual[j].toLowerCase()) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (expectedValue.toUpperCase() == actual[j]) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(expectedValue == actual[j]) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(matches == 0) {
|
||||
assert(descr + ": No match found for " + expectedValue,false);
|
||||
}
|
||||
if(matches > 1) {
|
||||
assert(descr + ": Multiple matches found for " + expectedValue, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqualsCollection(descr, expected, actual) {
|
||||
//
|
||||
// if they aren't the same size, they aren't equal
|
||||
assertEquals(descr, expected.length, actual.length);
|
||||
//
|
||||
// if there length is the same, then every entry in the expected list
|
||||
// must appear once and only once in the actual list
|
||||
var expectedLen = expected.length;
|
||||
var expectedValue;
|
||||
var actualLen = actual.length;
|
||||
var i;
|
||||
var j;
|
||||
var matches;
|
||||
for(i = 0; i < expectedLen; i++) {
|
||||
matches = 0;
|
||||
expectedValue = expected[i];
|
||||
for(j = 0; j < actualLen; j++) {
|
||||
if(expectedValue == actual[j]) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
if(matches == 0) {
|
||||
assert(descr + ": No match found for " + expectedValue,false);
|
||||
}
|
||||
if(matches > 1) {
|
||||
assert(descr + ": Multiple matches found for " + expectedValue, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function assertEqualsListAutoCase(context, descr, expected, actual) {
|
||||
var minLength = expected.length;
|
||||
if (actual.length < minLength) {
|
||||
minLength = actual.length;
|
||||
}
|
||||
//
|
||||
for(var i = 0; i < minLength; i++) {
|
||||
assertEqualsAutoCase(context, descr, expected[i], actual[i]);
|
||||
}
|
||||
//
|
||||
// if they aren't the same size, they aren't equal
|
||||
assertEquals(descr, expected.length, actual.length);
|
||||
}
|
||||
|
||||
function assertEqualsList(descr, expected, actual) {
|
||||
var minLength = expected.length;
|
||||
if (actual.length < minLength) {
|
||||
minLength = actual.length;
|
||||
}
|
||||
//
|
||||
for(var i = 0; i < minLength; i++) {
|
||||
if(expected[i] != actual[i]) {
|
||||
assertEquals(descr, expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
//
|
||||
// if they aren't the same size, they aren't equal
|
||||
assertEquals(descr, expected.length, actual.length);
|
||||
}
|
||||
|
||||
function assertInstanceOf(descr, type, obj) {
|
||||
if(type == "Attr") {
|
||||
assertEquals(descr,2,obj.nodeType);
|
||||
var specd = obj.specified;
|
||||
}
|
||||
}
|
||||
|
||||
function assertSame(descr, expected, actual) {
|
||||
if(expected != actual) {
|
||||
assertEquals(descr, expected.nodeType, actual.nodeType);
|
||||
assertEquals(descr, expected.nodeValue, actual.nodeValue);
|
||||
}
|
||||
}
|
||||
|
||||
function assertURIEquals(assertID, scheme, path, host, file, name, query, fragment, isAbsolute, actual) {
|
||||
//
|
||||
// URI must be non-null
|
||||
assertNotNull(assertID, actual);
|
||||
|
||||
var uri = actual;
|
||||
|
||||
var lastPound = actual.lastIndexOf("#");
|
||||
var actualFragment = "";
|
||||
if(lastPound != -1) {
|
||||
//
|
||||
// substring before pound
|
||||
//
|
||||
uri = actual.substring(0,lastPound);
|
||||
actualFragment = actual.substring(lastPound+1);
|
||||
}
|
||||
if(fragment != null) assertEquals(assertID,fragment, actualFragment);
|
||||
|
||||
var lastQuestion = uri.lastIndexOf("?");
|
||||
var actualQuery = "";
|
||||
if(lastQuestion != -1) {
|
||||
//
|
||||
// substring before pound
|
||||
//
|
||||
uri = actual.substring(0,lastQuestion);
|
||||
actualQuery = actual.substring(lastQuestion+1);
|
||||
}
|
||||
if(query != null) assertEquals(assertID, query, actualQuery);
|
||||
|
||||
var firstColon = uri.indexOf(":");
|
||||
var firstSlash = uri.indexOf("/");
|
||||
var actualPath = uri;
|
||||
var actualScheme = "";
|
||||
if(firstColon != -1 && firstColon < firstSlash) {
|
||||
actualScheme = uri.substring(0,firstColon);
|
||||
actualPath = uri.substring(firstColon + 1);
|
||||
}
|
||||
|
||||
if(scheme != null) {
|
||||
assertEquals(assertID, scheme, actualScheme);
|
||||
}
|
||||
|
||||
if(path != null) {
|
||||
assertEquals(assertID, path, actualPath);
|
||||
}
|
||||
|
||||
if(host != null) {
|
||||
var actualHost = "";
|
||||
if(actualPath.substring(0,2) == "//") {
|
||||
var termSlash = actualPath.substring(2).indexOf("/") + 2;
|
||||
actualHost = actualPath.substring(0,termSlash);
|
||||
}
|
||||
assertEquals(assertID, host, actualHost);
|
||||
}
|
||||
|
||||
if(file != null || name != null) {
|
||||
var actualFile = actualPath;
|
||||
var finalSlash = actualPath.lastIndexOf("/");
|
||||
if(finalSlash != -1) {
|
||||
actualFile = actualPath.substring(finalSlash+1);
|
||||
}
|
||||
if (file != null) {
|
||||
assertEquals(assertID, file, actualFile);
|
||||
}
|
||||
if (name != null) {
|
||||
var actualName = actualFile;
|
||||
var finalDot = actualFile.lastIndexOf(".");
|
||||
if (finalDot != -1) {
|
||||
actualName = actualName.substring(0, finalDot);
|
||||
}
|
||||
assertEquals(assertID, name, actualName);
|
||||
}
|
||||
}
|
||||
|
||||
if(isAbsolute != null) {
|
||||
assertEquals(assertID + ' ' + actualPath, isAbsolute, actualPath.substring(0,1) == "/");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// size() used by assertSize element
|
||||
function size(collection) {
|
||||
return collection.length;
|
||||
}
|
||||
|
||||
function same(expected, actual) {
|
||||
return expected === actual;
|
||||
}
|
||||
|
||||
function getSuffix(contentType) {
|
||||
switch(contentType) {
|
||||
case "text/html":
|
||||
return ".html";
|
||||
|
||||
case "text/xml":
|
||||
return ".xml";
|
||||
|
||||
case "application/xhtml+xml":
|
||||
return ".xhtml";
|
||||
|
||||
case "image/svg+xml":
|
||||
return ".svg";
|
||||
|
||||
case "text/mathml":
|
||||
return ".mml";
|
||||
}
|
||||
return ".html";
|
||||
}
|
||||
|
||||
function equalsAutoCase(context, expected, actual) {
|
||||
if (builder.contentType == "text/html") {
|
||||
if (context == "attribute") {
|
||||
return expected.toLowerCase() == actual;
|
||||
}
|
||||
return expected.toUpperCase() == actual;
|
||||
}
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
function catchInitializationError(blder, ex) {
|
||||
if (blder == null) {
|
||||
alert(ex);
|
||||
}
|
||||
else {
|
||||
blder.initializationError = ex;
|
||||
blder.initializationFatalError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
function checkInitialization(blder, testname) {
|
||||
if (blder.initializationError != null) {
|
||||
if (blder.skipIncompatibleTests) {
|
||||
info(testname + " not run:" + blder.initializationError);
|
||||
return blder.initializationError;
|
||||
}
|
||||
else {
|
||||
//
|
||||
// if an exception was thrown
|
||||
// rethrow it and do not run the test
|
||||
if (blder.initializationFatalError != null) {
|
||||
throw blder.initializationFatalError;
|
||||
} else {
|
||||
//
|
||||
// might be recoverable, warn but continue the test
|
||||
warn(testname + ": " + blder.initializationError);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function createTempURI(scheme) {
|
||||
if (scheme == "http") {
|
||||
return "http://localhost:8080/webdav/tmp" + Math.floor(Math.random() * 100000) + ".xml";
|
||||
}
|
||||
return "file:///tmp/domts" + Math.floor(Math.random() * 100000) + ".xml";
|
||||
}
|
||||
|
||||
|
||||
function EventMonitor() {
|
||||
this.atEvents = new Array();
|
||||
this.bubbledEvents = new Array();
|
||||
this.capturedEvents = new Array();
|
||||
this.allEvents = new Array();
|
||||
}
|
||||
|
||||
EventMonitor.prototype.handleEvent = function(evt) {
|
||||
switch(evt.eventPhase) {
|
||||
case 1:
|
||||
monitor.capturedEvents[monitor.capturedEvents.length] = evt;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
monitor.atEvents[monitor.atEvents.length] = evt;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
monitor.bubbledEvents[monitor.bubbledEvents.length] = evt;
|
||||
break;
|
||||
}
|
||||
monitor.allEvents[monitor.allEvents.length] = evt;
|
||||
}
|
||||
|
||||
function DOMErrorImpl(err) {
|
||||
this.severity = err.severity;
|
||||
this.message = err.message;
|
||||
this.type = err.type;
|
||||
this.relatedException = err.relatedException;
|
||||
this.relatedData = err.relatedData;
|
||||
this.location = err.location;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function DOMErrorMonitor() {
|
||||
this.allErrors = new Array();
|
||||
}
|
||||
|
||||
DOMErrorMonitor.prototype.handleError = function(err) {
|
||||
errorMonitor.allErrors[errorMonitor.allErrors.length] = new DOMErrorImpl(err);
|
||||
}
|
||||
|
||||
DOMErrorMonitor.prototype.assertLowerSeverity = function(id, severity) {
|
||||
var i;
|
||||
for (i = 0; i < errorMonitor.allErrors.length; i++) {
|
||||
if (errorMonitor.allErrors[i].severity >= severity) {
|
||||
assertEquals(id, severity - 1, errorMonitor.allErrors[i].severity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function UserDataNotification(operation, key, data, src, dst) {
|
||||
this.operation = operation;
|
||||
this.key = key;
|
||||
this.data = data;
|
||||
this.src = src;
|
||||
this.dst = dst;
|
||||
}
|
||||
|
||||
function UserDataMonitor() {
|
||||
this.allNotifications = new Array();
|
||||
}
|
||||
|
||||
UserDataMonitor.prototype.handle = function(operation, key, data, src, dst) {
|
||||
userDataMonitor.allNotifications[this.allNotifications.length] =
|
||||
new UserDataNotification(operation, key, data, src, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function checkFeature(feature, version)
|
||||
{
|
||||
if (!builder.hasFeature(feature, version))
|
||||
{
|
||||
//
|
||||
// don't throw exception so that users can select to ignore the precondition
|
||||
//
|
||||
builder.initializationError = "builder does not support feature " + feature + " version " + version;
|
||||
}
|
||||
}
|
||||
|
||||
function preload(frame, varname, url) {
|
||||
return builder.preload(frame, varname, url);
|
||||
}
|
||||
|
||||
function load(frame, varname, url) {
|
||||
return builder.load(frame, varname, url);
|
||||
}
|
||||
|
||||
function getImplementationAttribute(attr) {
|
||||
return builder.getImplementationAttribute(attr);
|
||||
}
|
||||
|
||||
|
||||
function setImplementationAttribute(attribute, value) {
|
||||
builder.setImplementationAttribute(attribute, value);
|
||||
}
|
||||
|
||||
function setAsynchronous(value) {
|
||||
if (builder.supportsAsyncChange) {
|
||||
builder.async = value;
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toLowerArray(src) {
|
||||
var newArray = new Array();
|
||||
var i;
|
||||
for (i = 0; i < src.length; i++) {
|
||||
newArray[i] = src[i].toLowerCase();
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
|
||||
function getImplementation() {
|
||||
return builder.getImplementation();
|
||||
}
|
||||
|
||||
function warn(msg) {
|
||||
//console.log(msg);
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
var fs = require('fs');
|
||||
var vm = require('vm');
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
var Path = require('path');
|
||||
var domino = require('../../../lib');
|
||||
var impl = domino.createDOMImplementation();
|
||||
var Window = require('../../../lib/Window');
|
||||
|
||||
var globals = {
|
||||
assertEquals: function(message, expected, actual) {
|
||||
assert.equal(actual, expected, message + ': expected ' +
|
||||
util.inspect(expected, false, 0) + ' got ' +
|
||||
util.inspect(actual, false, 0)
|
||||
);
|
||||
},
|
||||
assertTrue: function(message, actual) {
|
||||
globals.assertEquals(message, true, actual);
|
||||
},
|
||||
assertFalse: function(message, actual) {
|
||||
assert.ok(!actual, message);
|
||||
},
|
||||
assertNull: function(message, actual) {
|
||||
globals.assertEquals(message, null, actual);
|
||||
},
|
||||
assertNotNull: function(message, actual) {
|
||||
assert.notEqual(actual, null, message);
|
||||
},
|
||||
console: console
|
||||
};
|
||||
|
||||
function list(dir, re, fn) {
|
||||
dir = Path.resolve(__dirname, '..', dir);
|
||||
fs.readdirSync(dir).forEach(function(file) {
|
||||
var path = Path.join(dir, file);
|
||||
var m = re.exec(path);
|
||||
if (m) fn.apply(null, m);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function(path) {
|
||||
|
||||
function run(ctx, file) {
|
||||
vm.runInContext(fs.readFileSync(file, 'utf8'), ctx, file);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
function makeContext() {
|
||||
var ctx = vm.createContext(); // create new independent context
|
||||
Object.keys(globals).forEach(function(k) {
|
||||
ctx[k] = globals[k]; // shallow clone
|
||||
});
|
||||
|
||||
ctx.createConfiguredBuilder = function() {
|
||||
return {
|
||||
contentType: 'text/html',
|
||||
hasFeature: function(feature, version) {
|
||||
return impl.hasFeature(feature, version);
|
||||
},
|
||||
getImplementation: function() {
|
||||
return impl;
|
||||
},
|
||||
setImplementationAttribute: function(attr, value) {
|
||||
// Ignore
|
||||
},
|
||||
preload: function(docRef, name, href) {
|
||||
return 1;
|
||||
},
|
||||
load: function(docRef, name, href) {
|
||||
var doc = Path.resolve(__dirname, '..', path, 'files', href) + '.html';
|
||||
var html = fs.readFileSync(doc, 'utf8');
|
||||
var url = 'http://example.com/'+Path.join(path,'files',href)+'.html';
|
||||
|
||||
var document = domino.createDocument(html);
|
||||
document.address = url;
|
||||
var win = new Window(document);
|
||||
return win.document;
|
||||
}
|
||||
};
|
||||
};
|
||||
run(ctx, __dirname + '/DomTestCase.js');
|
||||
return ctx;
|
||||
}
|
||||
|
||||
var tests = {};
|
||||
list(path, /(.*?)\.js$/, function(file, basename) {
|
||||
tests[basename] = function() {
|
||||
var ctx = makeContext();
|
||||
run(ctx, file);
|
||||
ctx.setUpPage();
|
||||
ctx.runTest();
|
||||
};
|
||||
});
|
||||
return tests;
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
var suite = require('./harness');
|
||||
|
||||
exports.level1 = {
|
||||
core: suite('level1/core/'),
|
||||
html: suite('level1/html/')
|
||||
};
|
||||
/*
|
||||
exports.level2 = {
|
||||
core: suite('level2/core/'),
|
||||
html: suite('level2/html/')
|
||||
};
|
||||
*/
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/documentgetdoctypenodtd";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
setImplementationAttribute("validating", false);
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_nodtdstaff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "getDoctype()" method returns null for XML documents
|
||||
without a document type declaration.
|
||||
Retrieve the XML document without a DTD and invoke the
|
||||
"getDoctype()" method. It should return null.
|
||||
|
||||
* @author NIST
|
||||
* @author Mary Brady
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-B63ED1A31
|
||||
*/
|
||||
function documentgetdoctypenodtd() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "documentgetdoctypenodtd") != null) return;
|
||||
var doc;
|
||||
var docType;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_nodtdstaff");
|
||||
docType = doc.doctype;
|
||||
|
||||
assertNull("documentGetDocTypeNoDTDAssert",docType);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
documentgetdoctypenodtd();
|
||||
}
|
||||
Generated
Vendored
+143
@@ -0,0 +1,143 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/documentinvalidcharacterexceptioncreatepi";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
checkFeature("XML", null);
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "createProcessingInstruction(target,data) method
|
||||
raises an INVALID_CHARACTER_ERR DOMException if the
|
||||
specified tagName contains an invalid character.
|
||||
|
||||
* @author NIST
|
||||
* @author Mary Brady
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INVALID_CHARACTER_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-135944439
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-135944439')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INVALID_CHARACTER_ERR'])
|
||||
* @see http://www.w3.org/Bugs/Public/show_bug.cgi?id=249
|
||||
*/
|
||||
function documentinvalidcharacterexceptioncreatepi() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "documentinvalidcharacterexceptioncreatepi") != null) return;
|
||||
var doc;
|
||||
var badPI;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
|
||||
if(
|
||||
|
||||
(builder.contentType == "text/html" && false /*CSA: allowed in DOM 4*/)
|
||||
|
||||
) {
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
badPI = doc.createProcessingInstruction("foo","data");
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 9);
|
||||
}
|
||||
assertTrue("throw_NOT_SUPPORTED_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
badPI = doc.createProcessingInstruction("invalid^Name","data");
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 5);
|
||||
}
|
||||
assertTrue("throw_INVALID_CHARACTER_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
documentinvalidcharacterexceptioncreatepi();
|
||||
}
|
||||
Generated
Vendored
+140
@@ -0,0 +1,140 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/documentinvalidcharacterexceptioncreatepi1";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
checkFeature("XML", null);
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
Creating a processing instruction with an empty target should cause an INVALID_CHARACTER_ERR.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INVALID_CHARACTER_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-135944439
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-135944439')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INVALID_CHARACTER_ERR'])
|
||||
* @see http://www.w3.org/Bugs/Public/show_bug.cgi?id=525
|
||||
*/
|
||||
function documentinvalidcharacterexceptioncreatepi1() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "documentinvalidcharacterexceptioncreatepi1") != null) return;
|
||||
var doc;
|
||||
var badPI;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
|
||||
if(
|
||||
|
||||
(builder.contentType == "text/html" && false /*CSA: allowed in DOM 4*/)
|
||||
|
||||
) {
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
badPI = doc.createProcessingInstruction("foo","data");
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 9);
|
||||
}
|
||||
assertTrue("throw_NOT_SUPPORTED_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
badPI = doc.createProcessingInstruction("","data");
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 5);
|
||||
}
|
||||
assertTrue("throw_INVALID_CHARACTER_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
documentinvalidcharacterexceptioncreatepi1();
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>hc_nodtdstaff</title></head><body onload="parent.loadComplete()">
|
||||
<p>
|
||||
<em>EMP0001</em>
|
||||
<strong>Margaret Martin</strong>
|
||||
<code>Accountant</code>
|
||||
<sup>56,000</sup>
|
||||
<var>Female</var>
|
||||
<acronym title="Yes">1230 North Ave. Dallas, Texas 98551</acronym>
|
||||
</p>
|
||||
</body></html>
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||||
"http://www.w3.org/TR/html4/strict.dtd" >
|
||||
<!-- This is comment number 1.-->
|
||||
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>hc_staff</title><script type="text/javascript" src="svgunit.js"></script><script charset="UTF-8" type="text/javascript" src="svgtest.js"></script><script type='text/javascript'>function loadComplete() { startTest(); }</script></head><body onload="parent.loadComplete()">
|
||||
<p>
|
||||
<em>EMP0001</em>
|
||||
<strong>Margaret Martin</strong>
|
||||
<code>Accountant</code>
|
||||
<sup>56,000</sup>
|
||||
<var>Female</var>
|
||||
<acronym title="Yes">1230 North Ave. Dallas, Texas 98551</acronym>
|
||||
</p>
|
||||
<p>
|
||||
<em>EMP0002</em>
|
||||
<strong>Martha RaynoldsThis is a CDATASection with EntityReference number 2 &ent2;
|
||||
This is an adjacent CDATASection with a reference to a tab &tab;</strong>
|
||||
<code>Secretary</code>
|
||||
<sup>35,000</sup>
|
||||
<var>Female</var>
|
||||
<acronym title="Yes" class="Yes">β Dallas, γ
|
||||
98554</acronym>
|
||||
</p>
|
||||
<p>
|
||||
<em>EMP0003</em>
|
||||
<strong>Roger
|
||||
Jones</strong>
|
||||
<code>Department Manager</code>
|
||||
<sup>100,000</sup>
|
||||
<var>δ</var>
|
||||
<acronym title="Yes" class="No">PO Box 27 Irving, texas 98553</acronym>
|
||||
</p>
|
||||
<p>
|
||||
<em>EMP0004</em>
|
||||
<strong>Jeny Oconnor</strong>
|
||||
<code>Personnel Director</code>
|
||||
<sup>95,000</sup>
|
||||
<var>Female</var>
|
||||
<acronym title="Yes" class="Yα">27 South Road. Dallas, Texas 98556</acronym>
|
||||
</p>
|
||||
<p>
|
||||
<em>EMP0005</em>
|
||||
<strong>Robert Myers</strong>
|
||||
<code>Computer Specialist</code>
|
||||
<sup>90,000</sup>
|
||||
<var>male</var>
|
||||
<acronym title="Yes">1821 Nordic. Road, Irving Texas 98558</acronym>
|
||||
</p>
|
||||
</body></html>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<!ELEMENT employeeId (#PCDATA)>
|
||||
<!ELEMENT name (#PCDATA)>
|
||||
<!ELEMENT position (#PCDATA)>
|
||||
<!ELEMENT salary (#PCDATA)>
|
||||
<!ELEMENT address (#PCDATA)>
|
||||
<!ELEMENT entElement ( #PCDATA ) >
|
||||
<!ELEMENT gender ( #PCDATA | entElement )* >
|
||||
<!ELEMENT employee (employeeId, name, position, salary, gender, address) >
|
||||
<!ELEMENT staff (employee)+>
|
||||
<!ATTLIST entElement
|
||||
attr1 CDATA "Attr">
|
||||
<!ATTLIST address
|
||||
domestic CDATA #IMPLIED
|
||||
street CDATA "Yes">
|
||||
<!ATTLIST entElement
|
||||
domestic CDATA "MALE" >
|
||||
|
||||
Generated
Vendored
+124
@@ -0,0 +1,124 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataappenddata";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "appendData(arg)" method appends a string to the end
|
||||
of the character data of the node.
|
||||
|
||||
Retrieve the character data from the second child
|
||||
of the first employee. The appendData(arg) method is
|
||||
called with arg=", Esquire". The method should append
|
||||
the specified data to the already existing character
|
||||
data. The new value return by the "getLength()" method
|
||||
should be 24.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-72AB8359
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-32791A2F
|
||||
*/
|
||||
function hc_characterdataappenddata() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataappenddata") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childValue;
|
||||
var childLength;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("strong");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
child.appendData(", Esquire");
|
||||
childValue = child.data;
|
||||
|
||||
childLength = childValue.length;
|
||||
assertEquals("characterdataAppendDataAssert",24,childLength);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdataappenddata();
|
||||
}
|
||||
Generated
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataappenddatagetdata";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
On successful invocation of the "appendData(arg)"
|
||||
method the "getData()" method provides access to the
|
||||
concatentation of data and the specified string.
|
||||
|
||||
Retrieve the character data from the second child
|
||||
of the first employee. The appendData(arg) method is
|
||||
called with arg=", Esquire". The method should append
|
||||
the specified data to the already existing character
|
||||
data. The new value return by the "getData()" method
|
||||
should be "Margaret Martin, Esquire".
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-72AB8359
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-32791A2F
|
||||
*/
|
||||
function hc_characterdataappenddatagetdata() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataappenddatagetdata") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childData;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("strong");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
child.appendData(", Esquire");
|
||||
childData = child.data;
|
||||
|
||||
assertEquals("characterdataAppendDataGetDataAssert","Margaret Martin, Esquire",childData);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdataappenddatagetdata();
|
||||
}
|
||||
Generated
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdatadeletedatabegining";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "deleteData(offset,count)" method removes a range of
|
||||
characters from the node. Delete data at the beginning
|
||||
of the character data.
|
||||
|
||||
Retrieve the character data from the last child of the
|
||||
first employee. The "deleteData(offset,count)"
|
||||
method is then called with offset=0 and count=16.
|
||||
The method should delete the characters from position
|
||||
0 thru position 16. The new value of the character data
|
||||
should be "Dallas, Texas 98551".
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
*/
|
||||
function hc_characterdatadeletedatabegining() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdatadeletedatabegining") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childData;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
child.deleteData(0,16);
|
||||
childData = child.data;
|
||||
|
||||
assertEquals("data","Dallas, Texas 98551",childData);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdatadeletedatabegining();
|
||||
}
|
||||
Generated
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdatadeletedataend";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "deleteData(offset,count)" method removes a range of
|
||||
characters from the node. Delete data at the end
|
||||
of the character data.
|
||||
|
||||
Retrieve the character data from the last child of the
|
||||
first employee. The "deleteData(offset,count)"
|
||||
method is then called with offset=30 and count=5.
|
||||
The method should delete the characters from position
|
||||
30 thru position 35. The new value of the character data
|
||||
should be "1230 North Ave. Dallas, Texas".
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-72AB8359
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
*/
|
||||
function hc_characterdatadeletedataend() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdatadeletedataend") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childData;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
child.deleteData(30,5);
|
||||
childData = child.data;
|
||||
|
||||
assertEquals("characterdataDeleteDataEndAssert","1230 North Ave. Dallas, Texas ",childData);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdatadeletedataend();
|
||||
}
|
||||
Generated
Vendored
+125
@@ -0,0 +1,125 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdatadeletedataexceedslength";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
If the sum of the offset and count used in the
|
||||
"deleteData(offset,count) method is greater than the
|
||||
length of the character data then all the characters
|
||||
from the offset to the end of the data are deleted.
|
||||
|
||||
Retrieve the character data from the last child of the
|
||||
first employee. The "deleteData(offset,count)"
|
||||
method is then called with offset=4 and count=50.
|
||||
The method should delete the characters from position 4
|
||||
to the end of the data since the offset+count(50+4)
|
||||
is greater than the length of the character data(35).
|
||||
The new value of the character data should be "1230".
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-72AB8359
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
*/
|
||||
function hc_characterdatadeletedataexceedslength() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdatadeletedataexceedslength") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childData;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
child.deleteData(4,50);
|
||||
childData = child.data;
|
||||
|
||||
assertEquals("characterdataDeleteDataExceedsLengthAssert","1230",childData);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdatadeletedataexceedslength();
|
||||
}
|
||||
Generated
Vendored
+132
@@ -0,0 +1,132 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdatadeletedatagetlengthanddata";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
On successful invocation of the "deleteData(offset,count)"
|
||||
method, the "getData()" and "getLength()" methods reflect
|
||||
the changes.
|
||||
|
||||
Retrieve the character data from the last child of the
|
||||
first employee. The "deleteData(offset,count)"
|
||||
method is then called with offset=30 and count=5.
|
||||
The method should delete the characters from position
|
||||
30 thru position 35. The new value of the character data
|
||||
should be "1230 North Ave. Dallas, Texas" which is
|
||||
returned by the "getData()" method and "getLength()"
|
||||
method should return 30".
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-72AB8359
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7D61178C
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
*/
|
||||
function hc_characterdatadeletedatagetlengthanddata() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdatadeletedatagetlengthanddata") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childData;
|
||||
var childLength;
|
||||
var result = new Array();
|
||||
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
child.deleteData(30,5);
|
||||
childData = child.data;
|
||||
|
||||
assertEquals("data","1230 North Ave. Dallas, Texas ",childData);
|
||||
childLength = child.length;
|
||||
|
||||
assertEquals("length",30,childLength);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdatadeletedatagetlengthanddata();
|
||||
}
|
||||
Generated
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdatadeletedatamiddle";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "deleteData(offset,count)" method removes a range of
|
||||
characters from the node. Delete data in the middle
|
||||
of the character data.
|
||||
|
||||
Retrieve the character data from the last child of the
|
||||
first employee. The "deleteData(offset,count)"
|
||||
method is then called with offset=16 and count=8.
|
||||
The method should delete the characters from position
|
||||
16 thru position 24. The new value of the character data
|
||||
should be "1230 North Ave. Texas 98551".
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-72AB8359
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
*/
|
||||
function hc_characterdatadeletedatamiddle() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdatadeletedatamiddle") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childData;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
child.deleteData(16,8);
|
||||
childData = child.data;
|
||||
|
||||
assertEquals("characterdataDeleteDataMiddleAssert","1230 North Ave. Texas 98551",childData);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdatadeletedatamiddle();
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdatagetdata";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
The "getData()" method retrieves the character data
|
||||
|
||||
currently stored in the node.
|
||||
|
||||
Retrieve the character data from the second child
|
||||
|
||||
of the first employee and invoke the "getData()"
|
||||
|
||||
method. The method returns the character data
|
||||
|
||||
string.
|
||||
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-72AB8359
|
||||
*/
|
||||
function hc_characterdatagetdata() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdatagetdata") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childData;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("strong");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
childData = child.data;
|
||||
|
||||
assertEquals("characterdataGetDataAssert","Margaret Martin",childData);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdatagetdata();
|
||||
}
|
||||
Generated
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdatagetlength";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "getLength()" method returns the number of characters
|
||||
stored in this nodes data.
|
||||
Retrieve the character data from the second
|
||||
child of the first employee and examine the
|
||||
value returned by the getLength() method.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-72AB8359
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7D61178C
|
||||
*/
|
||||
function hc_characterdatagetlength() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdatagetlength") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childValue;
|
||||
var childLength;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("strong");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
childValue = child.data;
|
||||
|
||||
childLength = childValue.length;
|
||||
assertEquals("characterdataGetLengthAssert",15,childLength);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdatagetlength();
|
||||
}
|
||||
Generated
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataindexsizeerrdeletedatacountnegative";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
setImplementationAttribute("signed", true);
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "deleteData(offset,count)" method raises an
|
||||
INDEX_SIZE_ERR DOMException if the specified count
|
||||
is negative.
|
||||
|
||||
Retrieve the character data of the last child of the
|
||||
first employee and invoke its "deleteData(offset,count)"
|
||||
method with offset=10 and count=-3. It should raise the
|
||||
desired exception since the count is negative.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-6531BCCF
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-6531BCCF')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INDEX_SIZE_ERR'])
|
||||
*/
|
||||
function hc_characterdataindexsizeerrdeletedatacountnegative() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataindexsizeerrdeletedatacountnegative") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var childSubstring;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
childSubstring = child.substringData(10,-3);
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 1);
|
||||
}
|
||||
assertTrue("throws_INDEX_SIZE_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
return; // CSA: latest DOM spec doesn't throw for negative count
|
||||
hc_characterdataindexsizeerrdeletedatacountnegative();
|
||||
}
|
||||
Generated
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataindexsizeerrdeletedataoffsetgreater";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "deleteData(offset,count)" method raises an
|
||||
INDEX_SIZE_ERR DOMException if the specified offset
|
||||
is greater that the number of characters in the string.
|
||||
|
||||
Retrieve the character data of the last child of the
|
||||
first employee and invoke its "deleteData(offset,count)"
|
||||
method with offset=40 and count=3. It should raise the
|
||||
desired exception since the offset is greater than the
|
||||
number of characters in the string.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-7C603781')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
* @see http://www.w3.org/Bugs/Public/show_bug.cgi?id=249
|
||||
*/
|
||||
function hc_characterdataindexsizeerrdeletedataoffsetgreater() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataindexsizeerrdeletedataoffsetgreater") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
child.deleteData(40,3);
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 1);
|
||||
}
|
||||
assertTrue("throw_INDEX_SIZE_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdataindexsizeerrdeletedataoffsetgreater();
|
||||
}
|
||||
Generated
Vendored
+130
@@ -0,0 +1,130 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataindexsizeerrdeletedataoffsetnegative";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
setImplementationAttribute("signed", true);
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "deleteData(offset,count)" method raises an
|
||||
INDEX_SIZE_ERR DOMException if the specified offset
|
||||
is negative.
|
||||
|
||||
Retrieve the character data of the last child of the
|
||||
first employee and invoke its "deleteData(offset,count)"
|
||||
method with offset=-5 and count=3. It should raise the
|
||||
desired exception since the offset is negative.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-7C603781')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
*/
|
||||
function hc_characterdataindexsizeerrdeletedataoffsetnegative() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataindexsizeerrdeletedataoffsetnegative") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
child.deleteData(-5,3);
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 1);
|
||||
}
|
||||
assertTrue("throws_INDEX_SIZE_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdataindexsizeerrdeletedataoffsetnegative();
|
||||
}
|
||||
Generated
Vendored
+130
@@ -0,0 +1,130 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataindexsizeerrinsertdataoffsetgreater";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "insertData(offset,arg)" method raises an
|
||||
INDEX_SIZE_ERR DOMException if the specified offset
|
||||
is greater than the number of characters in the string.
|
||||
|
||||
Retrieve the character data of the last child of the
|
||||
first employee and invoke its insertData"(offset,arg)"
|
||||
method with offset=40 and arg="ABC". It should raise
|
||||
the desired exception since the offset is greater than
|
||||
the number of characters in the string.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-7C603781')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/Bugs/Public/show_bug.cgi?id=249
|
||||
*/
|
||||
function hc_characterdataindexsizeerrinsertdataoffsetgreater() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataindexsizeerrinsertdataoffsetgreater") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
child.deleteData(40,3);
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 1);
|
||||
}
|
||||
assertTrue("throw_INDEX_SIZE_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdataindexsizeerrinsertdataoffsetgreater();
|
||||
}
|
||||
Generated
Vendored
+129
@@ -0,0 +1,129 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataindexsizeerrinsertdataoffsetnegative";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
setImplementationAttribute("signed", true);
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "insertData(offset,arg)" method raises an
|
||||
INDEX_SIZE_ERR DOMException if the specified offset
|
||||
is negative.
|
||||
|
||||
Retrieve the character data of the last child of the
|
||||
first employee and invoke its insertData"(offset,arg)"
|
||||
method with offset=-5 and arg="ABC". It should raise
|
||||
the desired exception since the offset is negative.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-E5CBA7FB
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-E5CBA7FB')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INDEX_SIZE_ERR'])
|
||||
*/
|
||||
function hc_characterdataindexsizeerrinsertdataoffsetnegative() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataindexsizeerrinsertdataoffsetnegative") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
child.replaceData(-5,3,"ABC");
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 1);
|
||||
}
|
||||
assertTrue("throws_INDEX_SIZE_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdataindexsizeerrinsertdataoffsetnegative();
|
||||
}
|
||||
Generated
Vendored
+132
@@ -0,0 +1,132 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataindexsizeerrreplacedatacountnegative";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
setImplementationAttribute("signed", true);
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "replaceData(offset,count,arg)" method raises an
|
||||
INDEX_SIZE_ERR DOMException if the specified count
|
||||
is negative.
|
||||
|
||||
Retrieve the character data of the last child of the
|
||||
first employee and invoke its
|
||||
"replaceData(offset,count,arg) method with offset=10
|
||||
and count=-3 and arg="ABC". It should raise the
|
||||
desired exception since the count is negative.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-6531BCCF
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-6531BCCF')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INDEX_SIZE_ERR'])
|
||||
*/
|
||||
function hc_characterdataindexsizeerrreplacedatacountnegative() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataindexsizeerrreplacedatacountnegative") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
var badString;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
badString = child.substringData(10,-3);
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 1);
|
||||
}
|
||||
assertTrue("throws_INDEX_SIZE_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
return; // CSA: latest DOM spec doesn't throw for negative count
|
||||
hc_characterdataindexsizeerrreplacedatacountnegative();
|
||||
}
|
||||
Generated
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
|
||||
/*
|
||||
Copyright © 2001-2004 World Wide Web Consortium,
|
||||
(Massachusetts Institute of Technology, European Research Consortium
|
||||
for Informatics and Mathematics, Keio University). All
|
||||
Rights Reserved. This work is distributed under the W3C® Software License [1] in the
|
||||
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets URI that identifies the test.
|
||||
* @return uri identifier of test
|
||||
*/
|
||||
function getTargetURI() {
|
||||
return "http://www.w3.org/2001/DOM-Test-Suite/level1/core/hc_characterdataindexsizeerrreplacedataoffsetgreater";
|
||||
}
|
||||
|
||||
var docsLoaded = -1000000;
|
||||
var builder = null;
|
||||
|
||||
//
|
||||
// This function is called by the testing framework before
|
||||
// running the test suite.
|
||||
//
|
||||
// If there are no configuration exceptions, asynchronous
|
||||
// document loading is started. Otherwise, the status
|
||||
// is set to complete and the exception is immediately
|
||||
// raised when entering the body of the test.
|
||||
//
|
||||
function setUpPage() {
|
||||
setUpPageStatus = 'running';
|
||||
try {
|
||||
//
|
||||
// creates test document builder, may throw exception
|
||||
//
|
||||
builder = createConfiguredBuilder();
|
||||
|
||||
docsLoaded = 0;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
docsLoaded += preload(docRef, "doc", "hc_staff");
|
||||
|
||||
if (docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
} catch(ex) {
|
||||
catchInitializationError(builder, ex);
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This method is called on the completion of
|
||||
// each asychronous load started in setUpTests.
|
||||
//
|
||||
// When every synchronous loaded document has completed,
|
||||
// the page status is changed which allows the
|
||||
// body of the test to be executed.
|
||||
function loadComplete() {
|
||||
if (++docsLoaded == 1) {
|
||||
setUpPageStatus = 'complete';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
The "replaceData(offset,count,arg)" method raises an
|
||||
INDEX_SIZE_ERR DOMException if the specified offset
|
||||
is greater than the length of the string.
|
||||
|
||||
Retrieve the character data of the last child of the
|
||||
first employee and invoke its
|
||||
"replaceData(offset,count,arg) method with offset=40
|
||||
and count=3 and arg="ABC". It should raise the
|
||||
desired exception since the offset is greater than the
|
||||
length of the string.
|
||||
|
||||
* @author Curt Arnold
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-258A00AF')/constant[@name='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#ID-7C603781
|
||||
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#xpointer(id('ID-7C603781')/raises/exception[@name='DOMException']/descr/p[substring-before(.,':')='INDEX_SIZE_ERR'])
|
||||
* @see http://www.w3.org/Bugs/Public/show_bug.cgi?id=242
|
||||
*/
|
||||
function hc_characterdataindexsizeerrreplacedataoffsetgreater() {
|
||||
var success;
|
||||
if(checkInitialization(builder, "hc_characterdataindexsizeerrreplacedataoffsetgreater") != null) return;
|
||||
var doc;
|
||||
var elementList;
|
||||
var nameNode;
|
||||
var child;
|
||||
|
||||
var docRef = null;
|
||||
if (typeof(this.doc) != 'undefined') {
|
||||
docRef = this.doc;
|
||||
}
|
||||
doc = load(docRef, "doc", "hc_staff");
|
||||
elementList = doc.getElementsByTagName("acronym");
|
||||
nameNode = elementList.item(0);
|
||||
child = nameNode.firstChild;
|
||||
|
||||
|
||||
{
|
||||
success = false;
|
||||
try {
|
||||
child.deleteData(40,3);
|
||||
}
|
||||
catch(ex) {
|
||||
success = (typeof(ex.code) != 'undefined' && ex.code == 1);
|
||||
}
|
||||
assertTrue("throw_INDEX_SIZE_ERR",success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function runTest() {
|
||||
hc_characterdataindexsizeerrreplacedataoffsetgreater();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user