jsdom
Advanced tools
Comparing version 11.7.0 to 11.8.0
@@ -9,2 +9,3 @@ "use strict"; | ||
const Node = require("./Node.js"); | ||
const NonElementParentNode = require("./NonElementParentNode.js"); | ||
const ParentNode = require("./ParentNode.js"); | ||
@@ -33,2 +34,25 @@ | ||
DocumentFragment.prototype.getElementById = function getElementById(elementId) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
"Failed to execute 'getElementById' on 'DocumentFragment': 1 argument required, but only " + | ||
arguments.length + | ||
" present." | ||
); | ||
} | ||
const args = []; | ||
{ | ||
let curArg = arguments[0]; | ||
curArg = conversions["DOMString"](curArg, { | ||
context: "Failed to execute 'getElementById' on 'DocumentFragment': parameter 1" | ||
}); | ||
args.push(curArg); | ||
} | ||
return utils.tryWrapperForImpl(this[impl].getElementById(...args)); | ||
}; | ||
DocumentFragment.prototype.prepend = function prepend() { | ||
@@ -258,4 +282,6 @@ if (!this || !module.exports.is(this)) { | ||
NonElementParentNode._mixedIntoPredicates.push(module.exports.is); | ||
ParentNode._mixedIntoPredicates.push(module.exports.is); | ||
const Impl = require("../nodes/DocumentFragment-impl.js"); |
@@ -23,2 +23,41 @@ "use strict"; | ||
HTMLButtonElement.prototype.checkValidity = function checkValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].checkValidity(); | ||
}; | ||
HTMLButtonElement.prototype.reportValidity = function reportValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].reportValidity(); | ||
}; | ||
HTMLButtonElement.prototype.setCustomValidity = function setCustomValidity(error) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
"Failed to execute 'setCustomValidity' on 'HTMLButtonElement': 1 argument required, but only " + | ||
arguments.length + | ||
" present." | ||
); | ||
} | ||
const args = []; | ||
{ | ||
let curArg = arguments[0]; | ||
curArg = conversions["DOMString"](curArg, { | ||
context: "Failed to execute 'setCustomValidity' on 'HTMLButtonElement': parameter 1" | ||
}); | ||
args.push(curArg); | ||
} | ||
return this[impl].setCustomValidity(...args); | ||
}; | ||
Object.defineProperty(HTMLButtonElement.prototype, "autofocus", { | ||
@@ -227,2 +266,41 @@ get() { | ||
Object.defineProperty(HTMLButtonElement.prototype, "willValidate", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["willValidate"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLButtonElement.prototype, "validity", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return utils.tryWrapperForImpl(this[impl]["validity"]); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLButtonElement.prototype, "validationMessage", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["validationMessage"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLButtonElement.prototype, Symbol.toStringTag, { | ||
@@ -229,0 +307,0 @@ value: "HTMLButtonElement", |
@@ -23,2 +23,41 @@ "use strict"; | ||
HTMLFieldSetElement.prototype.checkValidity = function checkValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].checkValidity(); | ||
}; | ||
HTMLFieldSetElement.prototype.reportValidity = function reportValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].reportValidity(); | ||
}; | ||
HTMLFieldSetElement.prototype.setCustomValidity = function setCustomValidity(error) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
"Failed to execute 'setCustomValidity' on 'HTMLFieldSetElement': 1 argument required, but only " + | ||
arguments.length + | ||
" present." | ||
); | ||
} | ||
const args = []; | ||
{ | ||
let curArg = arguments[0]; | ||
curArg = conversions["DOMString"](curArg, { | ||
context: "Failed to execute 'setCustomValidity' on 'HTMLFieldSetElement': parameter 1" | ||
}); | ||
args.push(curArg); | ||
} | ||
return this[impl].setCustomValidity(...args); | ||
}; | ||
Object.defineProperty(HTMLFieldSetElement.prototype, "disabled", { | ||
@@ -92,2 +131,43 @@ get() { | ||
Object.defineProperty(HTMLFieldSetElement.prototype, "willValidate", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["willValidate"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLFieldSetElement.prototype, "validity", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return utils.getSameObject(this, "validity", () => { | ||
return utils.tryWrapperForImpl(this[impl]["validity"]); | ||
}); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLFieldSetElement.prototype, "validationMessage", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["validationMessage"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLFieldSetElement.prototype, Symbol.toStringTag, { | ||
@@ -94,0 +174,0 @@ value: "HTMLFieldSetElement", |
@@ -39,2 +39,18 @@ "use strict"; | ||
HTMLFormElement.prototype.checkValidity = function checkValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].checkValidity(); | ||
}; | ||
HTMLFormElement.prototype.reportValidity = function reportValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].reportValidity(); | ||
}; | ||
Object.defineProperty(HTMLFormElement.prototype, "acceptCharset", { | ||
@@ -41,0 +57,0 @@ get() { |
@@ -25,2 +25,41 @@ "use strict"; | ||
HTMLInputElement.prototype.checkValidity = function checkValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].checkValidity(); | ||
}; | ||
HTMLInputElement.prototype.reportValidity = function reportValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].reportValidity(); | ||
}; | ||
HTMLInputElement.prototype.setCustomValidity = function setCustomValidity(error) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
"Failed to execute 'setCustomValidity' on 'HTMLInputElement': 1 argument required, but only " + | ||
arguments.length + | ||
" present." | ||
); | ||
} | ||
const args = []; | ||
{ | ||
let curArg = arguments[0]; | ||
curArg = conversions["DOMString"](curArg, { | ||
context: "Failed to execute 'setCustomValidity' on 'HTMLInputElement': parameter 1" | ||
}); | ||
args.push(curArg); | ||
} | ||
return this[impl].setCustomValidity(...args); | ||
}; | ||
HTMLInputElement.prototype.select = function select() { | ||
@@ -944,2 +983,41 @@ if (!this || !module.exports.is(this)) { | ||
Object.defineProperty(HTMLInputElement.prototype, "willValidate", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["willValidate"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLInputElement.prototype, "validity", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return utils.tryWrapperForImpl(this[impl]["validity"]); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLInputElement.prototype, "validationMessage", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["validationMessage"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLInputElement.prototype, "selectionStart", { | ||
@@ -946,0 +1024,0 @@ get() { |
@@ -23,2 +23,41 @@ "use strict"; | ||
HTMLObjectElement.prototype.checkValidity = function checkValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].checkValidity(); | ||
}; | ||
HTMLObjectElement.prototype.reportValidity = function reportValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].reportValidity(); | ||
}; | ||
HTMLObjectElement.prototype.setCustomValidity = function setCustomValidity(error) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
"Failed to execute 'setCustomValidity' on 'HTMLObjectElement': 1 argument required, but only " + | ||
arguments.length + | ||
" present." | ||
); | ||
} | ||
const args = []; | ||
{ | ||
let curArg = arguments[0]; | ||
curArg = conversions["DOMString"](curArg, { | ||
context: "Failed to execute 'setCustomValidity' on 'HTMLObjectElement': parameter 1" | ||
}); | ||
args.push(curArg); | ||
} | ||
return this[impl].setCustomValidity(...args); | ||
}; | ||
Object.defineProperty(HTMLObjectElement.prototype, "data", { | ||
@@ -205,2 +244,41 @@ get() { | ||
Object.defineProperty(HTMLObjectElement.prototype, "willValidate", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["willValidate"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLObjectElement.prototype, "validity", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return utils.tryWrapperForImpl(this[impl]["validity"]); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLObjectElement.prototype, "validationMessage", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["validationMessage"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLObjectElement.prototype, "align", { | ||
@@ -207,0 +285,0 @@ get() { |
@@ -23,2 +23,41 @@ "use strict"; | ||
HTMLOutputElement.prototype.checkValidity = function checkValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].checkValidity(); | ||
}; | ||
HTMLOutputElement.prototype.reportValidity = function reportValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].reportValidity(); | ||
}; | ||
HTMLOutputElement.prototype.setCustomValidity = function setCustomValidity(error) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
"Failed to execute 'setCustomValidity' on 'HTMLOutputElement': 1 argument required, but only " + | ||
arguments.length + | ||
" present." | ||
); | ||
} | ||
const args = []; | ||
{ | ||
let curArg = arguments[0]; | ||
curArg = conversions["DOMString"](curArg, { | ||
context: "Failed to execute 'setCustomValidity' on 'HTMLOutputElement': parameter 1" | ||
}); | ||
args.push(curArg); | ||
} | ||
return this[impl].setCustomValidity(...args); | ||
}; | ||
Object.defineProperty(HTMLOutputElement.prototype, "name", { | ||
@@ -50,2 +89,41 @@ get() { | ||
Object.defineProperty(HTMLOutputElement.prototype, "willValidate", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["willValidate"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLOutputElement.prototype, "validity", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return utils.tryWrapperForImpl(this[impl]["validity"]); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLOutputElement.prototype, "validationMessage", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["validationMessage"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLOutputElement.prototype, Symbol.toStringTag, { | ||
@@ -52,0 +130,0 @@ value: "HTMLOutputElement", |
@@ -145,2 +145,41 @@ "use strict"; | ||
HTMLSelectElement.prototype.checkValidity = function checkValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].checkValidity(); | ||
}; | ||
HTMLSelectElement.prototype.reportValidity = function reportValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].reportValidity(); | ||
}; | ||
HTMLSelectElement.prototype.setCustomValidity = function setCustomValidity(error) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
"Failed to execute 'setCustomValidity' on 'HTMLSelectElement': 1 argument required, but only " + | ||
arguments.length + | ||
" present." | ||
); | ||
} | ||
const args = []; | ||
{ | ||
let curArg = arguments[0]; | ||
curArg = conversions["DOMString"](curArg, { | ||
context: "Failed to execute 'setCustomValidity' on 'HTMLSelectElement': parameter 1" | ||
}); | ||
args.push(curArg); | ||
} | ||
return this[impl].setCustomValidity(...args); | ||
}; | ||
Object.defineProperty(HTMLSelectElement.prototype, "autofocus", { | ||
@@ -445,2 +484,41 @@ get() { | ||
Object.defineProperty(HTMLSelectElement.prototype, "willValidate", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["willValidate"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLSelectElement.prototype, "validity", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return utils.tryWrapperForImpl(this[impl]["validity"]); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLSelectElement.prototype, "validationMessage", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["validationMessage"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLSelectElement.prototype, Symbol.toStringTag, { | ||
@@ -447,0 +525,0 @@ value: "HTMLSelectElement", |
@@ -24,2 +24,41 @@ "use strict"; | ||
HTMLTextAreaElement.prototype.checkValidity = function checkValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].checkValidity(); | ||
}; | ||
HTMLTextAreaElement.prototype.reportValidity = function reportValidity() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl].reportValidity(); | ||
}; | ||
HTMLTextAreaElement.prototype.setCustomValidity = function setCustomValidity(error) { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
"Failed to execute 'setCustomValidity' on 'HTMLTextAreaElement': 1 argument required, but only " + | ||
arguments.length + | ||
" present." | ||
); | ||
} | ||
const args = []; | ||
{ | ||
let curArg = arguments[0]; | ||
curArg = conversions["DOMString"](curArg, { | ||
context: "Failed to execute 'setCustomValidity' on 'HTMLTextAreaElement': parameter 1" | ||
}); | ||
args.push(curArg); | ||
} | ||
return this[impl].setCustomValidity(...args); | ||
}; | ||
HTMLTextAreaElement.prototype.select = function select() { | ||
@@ -624,2 +663,41 @@ if (!this || !module.exports.is(this)) { | ||
Object.defineProperty(HTMLTextAreaElement.prototype, "willValidate", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["willValidate"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLTextAreaElement.prototype, "validity", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return utils.tryWrapperForImpl(this[impl]["validity"]); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLTextAreaElement.prototype, "validationMessage", { | ||
get() { | ||
if (!this || !module.exports.is(this)) { | ||
throw new TypeError("Illegal invocation"); | ||
} | ||
return this[impl]["validationMessage"]; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(HTMLTextAreaElement.prototype, "selectionStart", { | ||
@@ -626,0 +704,0 @@ get() { |
@@ -109,3 +109,3 @@ "use strict"; | ||
const timeRe = /^([0-9]{2}):([0-9]{2})(?::([0-9]{2}(?:\.[0-9]{1,3})?))?$/; | ||
const timeRe = /^([0-9]{2}):([0-9]{2})(?::([0-9]{2}(?:\.([0-9]{1,3}))?))?$/; | ||
@@ -126,7 +126,8 @@ // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-time-string | ||
} | ||
const second = matches[3] !== undefined ? Number(matches[3]) : 0; | ||
const second = matches[3] !== undefined ? Math.trunc(Number(matches[3])) : 0; | ||
if (second < 0 || second >= 60) { | ||
return null; | ||
} | ||
return { hour, minute, second }; | ||
const millisecond = matches[4] !== undefined ? Number(matches[4]) : 0; | ||
return { hour, minute, second, millisecond }; | ||
} | ||
@@ -138,12 +139,12 @@ | ||
} | ||
function serializeTime({ hour, minute, second }) { | ||
function serializeTime({ hour, minute, second, millisecond }) { | ||
const hourStr = leftPad(`${hour}`, 2, "0"); | ||
const minuteStr = leftPad(`${minute}`, 2, "0"); | ||
const milliseconds = Math.trunc(second * 1000); | ||
if (milliseconds === 0) { | ||
if (millisecond === 0) { | ||
return `${hourStr}:${minuteStr}`; | ||
} | ||
const secondIntegerStr = leftPad(`${Math.trunc(milliseconds / 1000)}`, 2, "0"); | ||
const secondDecimalStr = `${milliseconds % 1000 / 1000}`.slice(1); | ||
return `${hourStr}:${minuteStr}:${secondIntegerStr}${secondDecimalStr}`; | ||
const secondStr = leftPad(second, 2, "0"); | ||
const millisecondStr = leftPad(millisecond, 3, "0"); | ||
return `${hourStr}:${minuteStr}:${secondStr}.${millisecondStr}`; | ||
} | ||
@@ -154,6 +155,6 @@ | ||
let separatorIdx = str.indexOf("T"); | ||
if (!normalized && separatorIdx < -1) { | ||
if (separatorIdx < 0 && !normalized) { | ||
separatorIdx = str.indexOf(" "); | ||
} | ||
if (separatorIdx < -1) { | ||
if (separatorIdx < 0) { | ||
return null; | ||
@@ -192,3 +193,3 @@ } | ||
const weekRe = /^([0-9]{4})-W([0-9]{2})$/; | ||
const weekRe = /^([0-9]{4,5})-W([0-9]{2})$/; | ||
@@ -195,0 +196,0 @@ // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-week-string |
@@ -8,3 +8,4 @@ "use strict"; | ||
stripLeadingAndTrailingASCIIWhitespace, | ||
stripNewlines | ||
stripNewlines, | ||
splitOnCommas | ||
} = require("./strings"); | ||
@@ -19,2 +20,3 @@ const { | ||
} = require("./dates-and-times"); | ||
const whatwgURL = require("whatwg-url"); | ||
@@ -63,2 +65,20 @@ const submittableLocalNames = new Set(["button", "input", "keygen", "object", "select", "textarea"]); | ||
// https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address | ||
exports.isValidEmailAddress = (emailAddress, multiple = false) => { | ||
const emailAddressRegExp = new RegExp("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9]" + | ||
"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}" + | ||
"[a-zA-Z0-9])?)*$"); | ||
// A valid e-mail address list is a set of comma-separated tokens, where each token is itself | ||
// a valid e - mail address.To obtain the list of tokens from a valid e - mail address list, | ||
// an implementation must split the string on commas. | ||
if (multiple) { | ||
return splitOnCommas(emailAddress).every(value => emailAddressRegExp.test(value)); | ||
} | ||
return emailAddressRegExp.test(emailAddress); | ||
}; | ||
exports.isValidAbsoluteURL = url => { | ||
return whatwgURL.parseURL(url) !== null; | ||
}; | ||
exports.sanitizeValueByType = (input, val) => { | ||
@@ -65,0 +85,0 @@ switch (input.type.toLowerCase()) { |
@@ -54,2 +54,3 @@ "use strict"; | ||
exports.FileList = require("./generated/FileList").interface; | ||
exports.ValidityState = require("./generated/ValidityState").interface; | ||
@@ -56,0 +57,0 @@ exports.DOMParser = require("./generated/DOMParser").interface; |
@@ -37,2 +37,3 @@ "use strict"; | ||
const DOMImplementation = require("../generated/DOMImplementation"); | ||
const NonElementParentNodeImpl = require("./NonElementParentNode-impl").implementation; | ||
const ParentNodeImpl = require("./ParentNode-impl").implementation; | ||
@@ -457,4 +458,5 @@ const Element = require("../generated/Element"); | ||
// This is implemented separately for Document (which has a _ids cache) and DocumentFragment (which does not). | ||
getElementById(id) { | ||
// return the first element | ||
// Return the first element with this ID. | ||
return this._ids[id] && this._ids[id].length > 0 ? this._ids[id][0] : null; | ||
@@ -827,2 +829,3 @@ } | ||
mixin(DocumentImpl.prototype, GlobalEventHandlersImpl.prototype); | ||
mixin(DocumentImpl.prototype, NonElementParentNodeImpl.prototype); | ||
mixin(DocumentImpl.prototype, ParentNodeImpl.prototype); | ||
@@ -829,0 +832,0 @@ |
"use strict"; | ||
const { mixin } = require("../../utils"); | ||
const { domSymbolTree } = require("../helpers/internal-constants"); | ||
const NODE_TYPE = require("../node-type"); | ||
const NodeImpl = require("./Node-impl").implementation; | ||
const NonElementParentNodeImpl = require("./NonElementParentNode-impl").implementation; | ||
const ParentNodeImpl = require("./ParentNode-impl").implementation; | ||
const NODE_TYPE = require("../node-type"); | ||
class DocumentFragmentImpl extends NodeImpl { | ||
@@ -14,4 +15,20 @@ constructor(args, privateData) { | ||
} | ||
// This is implemented separately for Document (which has a _ids cache) and DocumentFragment (which does not). | ||
getElementById(id) { | ||
if (id === "") { | ||
return null; | ||
} | ||
for (const descendant of domSymbolTree.treeIterator(this)) { | ||
if (descendant.nodeType === NODE_TYPE.ELEMENT_NODE && descendant.getAttributeNS(null, "id") === id) { | ||
return descendant; | ||
} | ||
} | ||
return null; | ||
} | ||
} | ||
mixin(DocumentFragmentImpl.prototype, NonElementParentNodeImpl.prototype); | ||
mixin(DocumentFragmentImpl.prototype, ParentNodeImpl.prototype); | ||
@@ -18,0 +35,0 @@ |
@@ -5,4 +5,13 @@ "use strict"; | ||
const { isDisabled } = require("../helpers/form-controls"); | ||
const DefaultConstraintValidationImpl = | ||
require("../constraint-validation/DefaultConstraintValidation-impl").implementation; | ||
const { mixin } = require("../../utils"); | ||
class HTMLButtonElementImpl extends HTMLElementImpl { | ||
constructor(args, privateData) { | ||
super(args, privateData); | ||
this._customValidityErrorMessage = ""; | ||
} | ||
_activationBehavior() { | ||
@@ -51,6 +60,12 @@ const { form } = this; | ||
} | ||
_barredFromConstraintValidationSpecialization() { | ||
return this.type === "reset" || this.type === "button"; | ||
} | ||
} | ||
mixin(HTMLButtonElementImpl.prototype, DefaultConstraintValidationImpl.prototype); | ||
module.exports = { | ||
implementation: HTMLButtonElementImpl | ||
}; |
"use strict"; | ||
const HTMLElementImpl = require("./HTMLElement-impl").implementation; | ||
const DefaultConstraintValidationImpl = | ||
require("../constraint-validation/DefaultConstraintValidation-impl").implementation; | ||
const { mixin } = require("../../utils"); | ||
const { closest } = require("../helpers/traversal"); | ||
@@ -9,6 +12,12 @@ | ||
} | ||
_barredFromConstraintValidationSpecialization() { | ||
return true; | ||
} | ||
} | ||
mixin(HTMLFieldSetElementImpl.prototype, DefaultConstraintValidationImpl.prototype); | ||
module.exports = { | ||
implementation: HTMLFieldSetElementImpl | ||
}; |
@@ -9,2 +9,3 @@ "use strict"; | ||
const { reflectURLAttribute } = require("../../utils"); | ||
const Event = require("../generated/Event"); | ||
@@ -14,2 +15,5 @@ // http://www.whatwg.org/specs/web-apps/current-work/#category-listed | ||
// https://html.spec.whatwg.org/multipage/forms.html#category-submit | ||
const submittableElements = new Set(["button", "input", "object", "select", "textarea"]); | ||
const encTypes = new Set([ | ||
@@ -27,2 +31,5 @@ "application/x-www-form-urlencoded", | ||
const constraintValidationPositiveResult = Symbol("positive"); | ||
const constraintValidationNegativeResult = Symbol("negative"); | ||
class HTMLFormElementImpl extends HTMLElementImpl { | ||
@@ -128,2 +135,46 @@ _descendantAdded(parent, child) { | ||
} | ||
// If the checkValidity() method is invoked, the user agent must statically validate the | ||
// constraints of the form element, and return true if the constraint validation returned | ||
// a positive result, and false if it returned a negative result. | ||
checkValidity() { | ||
return this._staticallyValidateConstraints().result === constraintValidationPositiveResult; | ||
} | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#interactively-validate-the-constraints | ||
reportValidity() { | ||
return this.checkValidity(); | ||
} | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#statically-validate-the-constraints | ||
_staticallyValidateConstraints() { | ||
const controls = []; | ||
for (const el of domSymbolTree.treeIterator(this)) { | ||
if (el.form === this && submittableElements.has(el.nodeName.toLowerCase())) { | ||
controls.push(el); | ||
} | ||
} | ||
const invalidControls = []; | ||
for (const control of controls) { | ||
if (control._isCandidateForConstraintValidation() && !control._satisfiesConstraints()) { | ||
invalidControls.push(control); | ||
} | ||
} | ||
if (invalidControls.length === 0) { | ||
return { result: constraintValidationPositiveResult }; | ||
} | ||
const unhandledInvalidControls = []; | ||
for (const invalidControl of invalidControls) { | ||
const notCancelled = invalidControl.dispatchEvent(Event.createImpl(["invalid", { cancelable: true }])); | ||
if (notCancelled) { | ||
unhandledInvalidControls.push(invalidControl); | ||
} | ||
} | ||
return { result: constraintValidationNegativeResult, unhandledInvalidControls }; | ||
} | ||
} | ||
@@ -130,0 +181,0 @@ |
"use strict"; | ||
const conversions = require("webidl-conversions"); | ||
const DOMException = require("domexception"); | ||
const Event = require("../generated/Event"); | ||
const FileList = require("../generated/FileList"); | ||
const HTMLElementImpl = require("./HTMLElement-impl").implementation; | ||
const idlUtils = require("../generated/utils"); | ||
const Event = require("../generated/Event"); | ||
const FileList = require("../generated/FileList"); | ||
const DefaultConstraintValidationImpl = | ||
require("../constraint-validation/DefaultConstraintValidation-impl").implementation; | ||
const ValidityState = require("../generated/ValidityState"); | ||
const { mixin } = require("../../utils"); | ||
const { domSymbolTree, cloningSteps } = require("../helpers/internal-constants"); | ||
const { closest } = require("../helpers/traversal"); | ||
const { isDisabled, sanitizeValueByType } = require("../helpers/form-controls"); | ||
const { parseFloatingPointNumber } = require("../helpers/strings"); | ||
const { | ||
isDisabled, | ||
isValidEmailAddress, | ||
isValidAbsoluteURL, | ||
sanitizeValueByType | ||
} = require("../helpers/form-controls"); | ||
const { | ||
parseFloatingPointNumber, | ||
asciiCaseInsensitiveMatch, | ||
splitOnCommas | ||
} = require("../helpers/strings"); | ||
const { | ||
parseDateString, | ||
@@ -29,2 +42,12 @@ parseLocalDateAndTimeString, | ||
const maxMinStepTypes = new Set(["date", "month", "week", "time", "datetime-local", "number", "range", "datetime"]); | ||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-apply | ||
const applicableTypesForAttribute = { | ||
max: maxMinStepTypes, | ||
min: maxMinStepTypes, | ||
step: maxMinStepTypes, | ||
pattern: new Set(["text", "search", "tel", "url", "email", "password"]) | ||
}; | ||
function allowSelect(type) { | ||
@@ -54,6 +77,5 @@ return selectAllowedTypes.has(type.toLowerCase()); | ||
// Necessary because Date.UTC() treats year within [0, 99] as [1900, 1999]. | ||
function getUTCMs(year, month = 1, day = 1, hour = 0, minute = 0, second = 0) { | ||
const millisecond = second - Math.trunc(second); | ||
function getUTCMs(year, month = 1, day = 1, hour = 0, minute = 0, second = 0, millisecond = 0) { | ||
if (year > 99 || year < 0) { | ||
return Date.UTC(year, month - 1, day, hour, minute, Math.trunc(second), millisecond); | ||
return Date.UTC(year, month - 1, day, hour, minute, second, millisecond); | ||
} | ||
@@ -66,3 +88,3 @@ const d = new Date(0); | ||
d.setUTCMinutes(minute); | ||
d.setUTCSeconds(Math.trunc(second), millisecond); | ||
d.setUTCSeconds(second, millisecond); | ||
return d.valueOf(); | ||
@@ -81,3 +103,3 @@ } | ||
} | ||
return getUTCMs(date.year, date.month - 1, date.day); | ||
return getUTCMs(date.year, date.month, date.day); | ||
} | ||
@@ -115,3 +137,3 @@ ], | ||
} | ||
return ((time.hours * 60 + time.minutes) * 60 + time.seconds) * 1000; | ||
return ((time.hour * 60 + time.minute) * 60 + time.second) * 1000 + time.millisecond; | ||
} | ||
@@ -126,5 +148,5 @@ ], | ||
} | ||
const { date: { year, month, day }, time: { hour, minute, second } } = dateAndTime; | ||
const { date: { year, month, day }, time: { hour, minute, second, millisecond } } = dateAndTime; | ||
// Doesn't quite matter whether or not UTC is used, since the offset from 1970-01-01 local time is returned. | ||
return getUTCMs(year, month, day, hour, minute, second); | ||
return getUTCMs(year, month, day, hour, minute, second, millisecond); | ||
} | ||
@@ -155,2 +177,4 @@ ], | ||
this.indeterminate = false; | ||
this._customValidityErrorMessage = ""; | ||
} | ||
@@ -193,3 +217,3 @@ | ||
if (this.type === "checkbox" || (this.type === "radio" && !this._preCheckedRadioState)) { | ||
const inputEvent = Event.createImpl(["input", { bubbles: true, cancelable: true }], {}); | ||
const inputEvent = Event.createImpl(["input", { isTrusted: true, bubbles: true, cancelable: true }], {}); | ||
this.dispatchEvent(inputEvent); | ||
@@ -242,9 +266,12 @@ | ||
} | ||
_removeOtherRadioCheckedness() { | ||
get _otherRadioGroupElements() { | ||
const wrapper = idlUtils.wrapperForImpl(this); | ||
const root = this._radioButtonGroupRoot; | ||
if (!root) { | ||
return; | ||
return []; | ||
} | ||
const result = []; | ||
const name = wrapper.name.toLowerCase(); | ||
@@ -264,6 +291,14 @@ | ||
if (candidate !== this) { | ||
candidate._checkedness = false; | ||
result.push(candidate); | ||
} | ||
} | ||
return result; | ||
} | ||
_removeOtherRadioCheckedness() { | ||
for (const radioGroupElement of this._otherRadioGroupElements) { | ||
radioGroupElement._checkedness = false; | ||
} | ||
} | ||
get _radioButtonGroupRoot() { | ||
@@ -286,8 +321,18 @@ const wrapper = idlUtils.wrapperForImpl(this); | ||
} | ||
_isRadioGroupChecked() { | ||
if (this.checked) { | ||
return true; | ||
} | ||
return this._otherRadioGroupElements.some(radioGroupElement => radioGroupElement.checked); | ||
} | ||
get form() { | ||
return closest(this, "form"); | ||
} | ||
get checked() { | ||
return this._checkedness; | ||
} | ||
set checked(checked) { | ||
@@ -300,2 +345,3 @@ this._checkedness = Boolean(checked); | ||
} | ||
get value() { | ||
@@ -605,2 +651,201 @@ switch (valueAttributeMode(this.type)) { | ||
get _parsedValue() { | ||
const converter = this._convertStringToNumber; | ||
if (converter !== undefined) { | ||
return converter(this.value); | ||
} | ||
return this.value; | ||
} | ||
// https://html.spec.whatwg.org/multipage/input.html#attr-input-step | ||
get _step() { | ||
let step = this._defaultStep; | ||
if (this.hasAttribute("step") && !asciiCaseInsensitiveMatch(this.getAttribute("step"), "any")) { | ||
const parsedStep = parseFloatingPointNumber(this.getAttribute("step")); | ||
if (!isNaN(parsedStep) && parsedStep > 0) { | ||
step = parsedStep; | ||
} | ||
} | ||
return step; | ||
} | ||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-step-scale | ||
get _stepScaleFactor() { | ||
const dayInMilliseconds = 24 * 60 * 60 * 1000; | ||
switch (this.type) { | ||
case "week": | ||
return 7 * dayInMilliseconds; | ||
case "date": | ||
return dayInMilliseconds; | ||
case "datetime-local": | ||
case "datetime": | ||
case "time": | ||
return 1000; | ||
} | ||
return 1; | ||
} | ||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-step-default | ||
get _defaultStep() { | ||
if (this.type === "datetime-local" || this.type === "datetime" || this.type === "time") { | ||
return 60; | ||
} | ||
return 1; | ||
} | ||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-min-zero | ||
get _stepBase() { | ||
const parseAttribute = attributeName => parseFloatingPointNumber(this.getAttribute(attributeName)); | ||
if (this.hasAttribute("min")) { | ||
const min = parseAttribute("min"); | ||
if (!isNaN(min)) { | ||
return min; | ||
} | ||
} | ||
if (this.hasAttribute("value")) { | ||
const value = parseAttribute("value"); | ||
if (!isNaN(value)) { | ||
return value; | ||
} | ||
} | ||
return this._defaultStepBase; | ||
} | ||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-step-default-base | ||
get _defaultStepBase() { | ||
if (this.type === "week") { | ||
// The start of week 1970-W01 | ||
return 259200000; | ||
} | ||
return 0; | ||
} | ||
_attributeApplies(attribute) { | ||
return applicableTypesForAttribute[attribute].has(this.type); | ||
} | ||
_barredFromConstraintValidationSpecialization() { | ||
// https://html.spec.whatwg.org/multipage/input.html#hidden-state-(type=hidden) | ||
// https://html.spec.whatwg.org/multipage/input.html#reset-button-state-(type=reset) | ||
// https://html.spec.whatwg.org/multipage/input.html#button-state-(type=button) | ||
const willNotValidateTypes = new Set(["hidden", "reset", "button"]); | ||
// https://html.spec.whatwg.org/multipage/input.html#attr-input-readonly | ||
const readOnly = this.hasAttribute("readonly"); | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled | ||
return willNotValidateTypes.has(this.type) || readOnly; | ||
} | ||
get validity() { | ||
if (!this._validity) { | ||
this._validity = ValidityState.createImpl(this, { | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-being-missing | ||
valueMissing: () => { | ||
if (!this.hasAttribute("required")) { | ||
return false; | ||
} | ||
if (this.type === "checkbox") { | ||
// https://html.spec.whatwg.org/multipage/input.html#checkbox-state-(type=checkbox) | ||
// Constraint validation: If the element is required and its checkedness is | ||
// false, then the element is suffering from being missing. | ||
return !this.checked; | ||
} else if (this.type === "radio") { | ||
// https://html.spec.whatwg.org/multipage/input.html#radio-button-state-(type=radio) | ||
// Constraint validation: If an element in the radio button group is required, | ||
// and all of the input elements in the radio button group have a checkedness | ||
// that is false, then the element is suffering from being missing. | ||
return !this._isRadioGroupChecked(); | ||
} | ||
return this.value === ""; | ||
}, | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-being-too-long | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-maxlength | ||
// jsdom has no way at the moment to emulate a user interaction, so tooLong/tooShort have | ||
// to be set to false. | ||
tooLong: () => false, | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-being-too-short | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-minlength | ||
tooShort: () => false, | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-an-overflow | ||
// https://html.spec.whatwg.org/multipage/input.html#attr-input-max | ||
rangeOverflow: () => this._attributeApplies("max") && this._maximum !== null && | ||
this._parsedValue > this._maximum, | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-an-underflow | ||
// https://html.spec.whatwg.org/multipage/input.html#attr-input-min | ||
rangeUnderflow: () => this._attributeApplies("min") && this._minimum !== null && | ||
this._parsedValue < this._minimum, | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-a-pattern-mismatch | ||
patternMismatch: () => { | ||
if (!this.hasAttribute("pattern") || !this._attributeApplies("pattern") || this.value === "") { | ||
return false; | ||
} | ||
let regExp; | ||
try { | ||
regExp = new RegExp(this.getAttribute("pattern"), "u"); | ||
} catch (e) { | ||
return false; | ||
} | ||
if (this.type === "email" && this.hasAttribute("multiple")) { | ||
return splitOnCommas(this.value).every(value => regExp.test(value)); | ||
} | ||
return !regExp.test(this.value); | ||
}, | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-a-step-mismatch | ||
// https://html.spec.whatwg.org/multipage/input.html#attr-input-step | ||
stepMismatch: () => { | ||
// Constraint validation: When the element has an allowed value step, and the result of applying | ||
// the algorithm to convert a string to a number to the string given by the element's value is a | ||
// number, and that number subtracted from the step base is not an integral multiple of the | ||
// allowed value step, the element is suffering from a step mismatch. | ||
if (!this._attributeApplies("step")) { | ||
return false; | ||
} | ||
const step = parseFloatingPointNumber(this.getAttribute("step")); | ||
if (isNaN(step) || step <= 0) { | ||
return false; | ||
} | ||
let number = this._parsedValue; | ||
if (isNaN(number) || this.value === "") { | ||
return false; | ||
} | ||
if (this._type === "month") { | ||
number = parseMonthString(this.value).month - 1; | ||
} | ||
return number % (this._stepBase - (this._step * this._stepScaleFactor)) !== 0; | ||
}, | ||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-a-type-mismatch | ||
typeMismatch: () => { | ||
if (this.value === "") { | ||
return false; | ||
} | ||
if (this.type === "email") { | ||
// https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type=email) | ||
// Constraint validation [multiple=false]: While the value of the element is neither the empty | ||
// string nor a single valid e - mail address, the element is suffering from a type mismatch. | ||
// Constraint validation [multiple=true]: While the value of the element is not a valid e-mail address list, | ||
// the element is suffering from a type mismatch. | ||
return !isValidEmailAddress(this.value, this.hasAttribute("multiple")); | ||
} else if (this.type === "url") { | ||
// https://html.spec.whatwg.org/multipage/input.html#url-state-(type=url) | ||
// Constraint validation: While the value of the element is neither the empty string | ||
// nor a valid absolute URL, the element is suffering from a type mismatch. | ||
return !isValidAbsoluteURL(this.value); | ||
} | ||
return false; | ||
} | ||
}); | ||
} | ||
return this._validity; | ||
} | ||
[cloningSteps](copy, node) { | ||
@@ -614,4 +859,6 @@ copy._value = node._value; | ||
mixin(HTMLInputElementImpl.prototype, DefaultConstraintValidationImpl.prototype); | ||
module.exports = { | ||
implementation: HTMLInputElementImpl | ||
}; |
"use strict"; | ||
const HTMLElementImpl = require("./HTMLElement-impl").implementation; | ||
const DefaultConstraintValidationImpl = | ||
require("../constraint-validation/DefaultConstraintValidation-impl").implementation; | ||
const { mixin } = require("../../utils"); | ||
const { reflectURLAttribute } = require("../../utils"); | ||
@@ -30,6 +33,12 @@ const { closest } = require("../helpers/traversal"); | ||
} | ||
_barredFromConstraintValidationSpecialization() { | ||
return true; | ||
} | ||
} | ||
mixin(HTMLObjectElementImpl.prototype, DefaultConstraintValidationImpl.prototype); | ||
module.exports = { | ||
implementation: HTMLObjectElementImpl | ||
}; |
"use strict"; | ||
const HTMLElementImpl = require("./HTMLElement-impl").implementation; | ||
const DefaultConstraintValidationImpl = | ||
require("../constraint-validation/DefaultConstraintValidation-impl").implementation; | ||
const { mixin } = require("../../utils"); | ||
class HTMLOutputElementImpl extends HTMLElementImpl { } | ||
class HTMLOutputElementImpl extends HTMLElementImpl { | ||
_barredFromConstraintValidationSpecialization() { | ||
return true; | ||
} | ||
} | ||
mixin(HTMLOutputElementImpl.prototype, DefaultConstraintValidationImpl.prototype); | ||
module.exports = { | ||
implementation: HTMLOutputElementImpl | ||
}; |
@@ -6,2 +6,6 @@ "use strict"; | ||
const idlUtils = require("../generated/utils.js"); | ||
const ValidityState = require("../generated/ValidityState"); | ||
const DefaultConstraintValidationImpl = | ||
require("../constraint-validation/DefaultConstraintValidation-impl").implementation; | ||
const { mixin } = require("../../utils"); | ||
const HTMLElementImpl = require("./HTMLElement-impl").implementation; | ||
@@ -37,2 +41,4 @@ const NODE_TYPE = require("../node-type"); | ||
this._selectedOptions = null; // lazy | ||
this._customValidityErrorMessage = ""; | ||
} | ||
@@ -217,6 +223,44 @@ | ||
} | ||
_barredFromConstraintValidationSpecialization() { | ||
return this.hasAttribute("readonly"); | ||
} | ||
// Constraint validation: If the element has its required attribute specified, | ||
// and either none of the option elements in the select element's list of options | ||
// have their selectedness set to true, or the only option element in the select | ||
// element's list of options with its selectedness set to true is the placeholder | ||
// label option, then the element is suffering from being missing. | ||
get validity() { | ||
if (!this._validity) { | ||
this._validity = ValidityState.createImpl(this, { | ||
valueMissing: () => { | ||
if (!this.hasAttribute("required")) { | ||
return false; | ||
} | ||
const selectedOptionIndex = this.selectedIndex; | ||
return selectedOptionIndex < 0 || (selectedOptionIndex === 0 && this._hasPlaceholderOption); | ||
} | ||
}); | ||
} | ||
return this._validity; | ||
} | ||
// If a select element has a required attribute specified, does not have a multiple attribute | ||
// specified, and has a display size of 1; and if the value of the first option element in the | ||
// select element's list of options (if any) is the empty string, and that option element's parent | ||
// node is the select element(and not an optgroup element), then that option is the select | ||
// element's placeholder label option. | ||
// https://html.spec.whatwg.org/multipage/form-elements.html#placeholder-label-option | ||
get _hasPlaceholderOption() { | ||
return this.hasAttribute("required") && !this.hasAttribute("multiple") && | ||
this._displaySize === 1 && this.options.length > 0 && this.options.item(0).value === "" && | ||
this.options.item(0).parentNode._localName !== "optgroup"; | ||
} | ||
} | ||
mixin(HTMLSelectElementImpl.prototype, DefaultConstraintValidationImpl.prototype); | ||
module.exports = { | ||
implementation: HTMLSelectElementImpl | ||
}; |
@@ -5,2 +5,7 @@ "use strict"; | ||
const DefaultConstraintValidationImpl = | ||
require("../constraint-validation/DefaultConstraintValidation-impl").implementation; | ||
const ValidityState = require("../generated/ValidityState"); | ||
const { mixin } = require("../../utils"); | ||
const DOMException = require("domexception"); | ||
@@ -17,2 +22,4 @@ const { closest } = require("../helpers/traversal"); | ||
this._dirtyValue = false; | ||
this._customValidityErrorMessage = ""; | ||
} | ||
@@ -181,6 +188,22 @@ | ||
} | ||
_barredFromConstraintValidationSpecialization() { | ||
return this.hasAttribute("readonly"); | ||
} | ||
// https://html.spec.whatwg.org/multipage/form-elements.html#attr-textarea-required | ||
get validity() { | ||
if (!this._validity) { | ||
this._validity = ValidityState.createImpl(this, { | ||
valueMissing: () => this.hasAttribute("required") && this.value === "" | ||
}); | ||
} | ||
return this._validity; | ||
} | ||
} | ||
mixin(HTMLTextAreaElementImpl.prototype, DefaultConstraintValidationImpl.prototype); | ||
module.exports = { | ||
implementation: HTMLTextAreaElementImpl | ||
}; |
"use strict"; | ||
// https://dom.spec.whatwg.org/#interface-nonelementparentnode | ||
// getElementById is implemented separately inside Document and DocumentFragment. | ||
class NonElementParentNodeImpl { | ||
@@ -4,0 +6,0 @@ |
{ | ||
"name": "jsdom", | ||
"version": "11.7.0", | ||
"version": "11.8.0", | ||
"description": "A JavaScript implementation of many web standards", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -359,3 +359,3 @@ # jsdom | ||
All invocations of the `framgment()` factory result in `DocumentFragment`s that share the same owner `Document` and `Window`. This allows many calls to `fragment()` with no extra overhead. But it also means that calls to `fragment()` cannot be customized with any options. | ||
All invocations of the `fragment()` factory result in `DocumentFragment`s that share the same owner `Document` and `Window`. This allows many calls to `fragment()` with no extra overhead. But it also means that calls to `fragment()` cannot be customized with any options. | ||
@@ -362,0 +362,0 @@ Note that serialization is not as easy with `DocumentFragment`s as it is with full `JSDOM` objects. If you need to serialize your DOM, you should probably use the `JSDOM` constructor more directly. But for the special case of a fragment containing a single element, it's pretty easy to do through normal means: |
Sorry, the diff of this file is too big to display
2009189
397
58194