diff options
Diffstat (limited to 'school/node_modules/jsdom/lib/jsdom/living/nodes/HTMLInputElement-impl.js')
-rw-r--r-- | school/node_modules/jsdom/lib/jsdom/living/nodes/HTMLInputElement-impl.js | 1128 |
1 files changed, 0 insertions, 1128 deletions
diff --git a/school/node_modules/jsdom/lib/jsdom/living/nodes/HTMLInputElement-impl.js b/school/node_modules/jsdom/lib/jsdom/living/nodes/HTMLInputElement-impl.js deleted file mode 100644 index edd299d..0000000 --- a/school/node_modules/jsdom/lib/jsdom/living/nodes/HTMLInputElement-impl.js +++ /dev/null @@ -1,1128 +0,0 @@ -"use strict"; -const DOMException = require("domexception/webidl2js-wrapper"); -const FileList = require("../generated/FileList"); -const Decimal = require("decimal.js"); -const HTMLElementImpl = require("./HTMLElement-impl").implementation; -const idlUtils = require("../generated/utils"); -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 { getLabelsForLabelable, formOwner } = require("../helpers/form-controls"); -const { fireAnEvent } = require("../helpers/events"); -const { - isDisabled, - isValidEmailAddress, - isValidAbsoluteURL, - sanitizeValueByType -} = require("../helpers/form-controls"); -const { - asciiCaseInsensitiveMatch, - asciiLowercase, - parseFloatingPointNumber, - splitOnCommas -} = require("../helpers/strings"); -const { isDate } = require("../helpers/dates-and-times"); -const { - convertStringToNumberByType, - convertStringToDateByType, - serializeDateByType, - convertNumberToStringByType -} = require("../helpers/number-and-date-inputs"); - -const filesSymbol = Symbol("files"); - -// https://html.spec.whatwg.org/multipage/input.html#attr-input-type -const inputAllowedTypes = new Set([ - "hidden", "text", "search", "tel", "url", "email", "password", "date", - "month", "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio", - "file", "submit", "image", "reset", "button" -]); - -// https://html.spec.whatwg.org/multipage/input.html#concept-input-apply - -const variableLengthSelectionAllowedTypes = new Set(["text", "search", "url", "tel", "password"]); -const numericTypes = new Set(["date", "month", "week", "time", "datetime-local", "number", "range"]); - -const applicableTypesForIDLMember = { - valueAsDate: new Set(["date", "month", "week", "time"]), - valueAsNumber: numericTypes, - - select: new Set([ - "text", "search", "url", "tel", "email", "password", "date", "month", "week", - "time", "datetime-local", "number", "color", "file" - ]), - selectionStart: variableLengthSelectionAllowedTypes, - selectionEnd: variableLengthSelectionAllowedTypes, - selectionDirection: variableLengthSelectionAllowedTypes, - setRangeText: variableLengthSelectionAllowedTypes, - setSelectionRange: variableLengthSelectionAllowedTypes, - stepDown: numericTypes, - stepUp: numericTypes -}; - -const lengthPatternSizeTypes = new Set(["text", "search", "url", "tel", "email", "password"]); -const readonlyTypes = - new Set([...lengthPatternSizeTypes, "date", "month", "week", "time", "datetime-local", "number"]); - -const applicableTypesForContentAttribute = { - list: new Set(["text", "search", "url", "tel", "email", ...numericTypes, "color"]), - max: numericTypes, - maxlength: lengthPatternSizeTypes, - min: numericTypes, - minlength: lengthPatternSizeTypes, - multiple: new Set(["email", "file"]), - pattern: lengthPatternSizeTypes, - readonly: readonlyTypes, - required: new Set([...readonlyTypes, "checkbox", "radio", "file"]), - step: numericTypes -}; - -const valueAttributeDefaultMode = new Set(["hidden", "submit", "image", "reset", "button"]); -const valueAttributeDefaultOnMode = new Set(["checkbox", "radio"]); - -function valueAttributeMode(type) { - if (valueAttributeDefaultMode.has(type)) { - return "default"; - } - if (valueAttributeDefaultOnMode.has(type)) { - return "default/on"; - } - if (type === "file") { - return "filename"; - } - return "value"; -} - -function getTypeFromAttribute(typeAttribute) { - if (typeof typeAttribute !== "string") { - return "text"; - } - const type = asciiLowercase(typeAttribute); - return inputAllowedTypes.has(type) ? type : "text"; -} - -class HTMLInputElementImpl extends HTMLElementImpl { - constructor(globalObject, args, privateData) { - super(globalObject, args, privateData); - - this._selectionStart = this._selectionEnd = 0; - this._selectionDirection = "none"; - this._value = ""; - this._dirtyValue = false; - this._checkedness = false; - this._dirtyCheckedness = false; - - this._preCheckedRadioState = null; - this._legacyActivationBehaviorPreviousIndeterminateState = false; - - this.indeterminate = false; - - this._customValidityErrorMessage = ""; - - this._labels = null; - - this._hasActivationBehavior = true; - } - - // https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-number - get _convertStringToNumber() { - return convertStringToNumberByType[this.type]; - } - - get _convertNumberToString() { - return convertNumberToStringByType[this.type]; - } - - get _convertDateToString() { - return serializeDateByType[this.type]; - } - - get _convertStringToDate() { - return convertStringToDateByType[this.type]; - } - - _isStepAligned(v) { - return new Decimal(v).minus(this._stepBase) - .modulo(this._allowedValueStep) - .isZero(); - } - - // Returns a Decimal. - _stepAlign(v, roundUp) { - const allowedValueStep = this._allowedValueStep; - const stepBase = this._stepBase; - - return new Decimal(v).minus(stepBase) - .toNearest(allowedValueStep, roundUp ? Decimal.ROUND_UP : Decimal.ROUND_DOWN) - .add(stepBase); - } - - // For <input>, https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-value - // is a simple value that is gotten and set, not computed. - _getValue() { - return this._value; - } - - _legacyPreActivationBehavior() { - if (this.type === "checkbox") { - this.checked = !this.checked; - this._legacyActivationBehaviorPreviousIndeterminateState = this.indeterminate; - this.indeterminate = false; - } else if (this.type === "radio") { - this._preCheckedRadioState = this.checked; - this.checked = true; - } - } - - _legacyCanceledActivationBehavior() { - if (this.type === "checkbox") { - this.checked = !this.checked; - this.indeterminate = this._legacyActivationBehaviorPreviousIndeterminateState; - } else if (this.type === "radio") { - if (this._preCheckedRadioState !== null) { - this.checked = this._preCheckedRadioState; - this._preCheckedRadioState = null; - } - } - } - - _activationBehavior() { - if (!this._mutable && this.type !== "checkbox" && this.type !== "radio") { - return; - } - - const { form } = this; - - if (this.type === "checkbox" || (this.type === "radio" && !this._preCheckedRadioState)) { - if (this.isConnected) { - fireAnEvent("input", this, undefined, { bubbles: true }); - fireAnEvent("change", this, undefined, { bubbles: true }); - } - } else if (form && this.type === "submit") { - form._doSubmit(); - } else if (form && this.type === "reset") { - form._doReset(); - } - } - - _attrModified(name, value, oldVal) { - const wrapper = idlUtils.wrapperForImpl(this); - if (!this._dirtyValue && name === "value") { - this._value = sanitizeValueByType(this, wrapper.defaultValue); - } - if (!this._dirtyCheckedness && name === "checked") { - this._checkedness = wrapper.defaultChecked; - if (this._checkedness) { - this._removeOtherRadioCheckedness(); - } - } - - if (name === "name" || name === "type") { - if (this._checkedness) { - this._removeOtherRadioCheckedness(); - } - } - - if (name === "type") { - const prevType = getTypeFromAttribute(oldVal); - const curType = getTypeFromAttribute(value); - // When an input element's type attribute changes stateā¦ - if (prevType !== curType) { - const prevValueMode = valueAttributeMode(prevType); - const curValueMode = valueAttributeMode(curType); - if (prevValueMode === "value" && this._value !== "" && - (curValueMode === "default" || curValueMode === "default/on")) { - this.setAttributeNS(null, "value", this._value); - } else if (prevValueMode !== "value" && curValueMode === "value") { - this._value = this.getAttributeNS(null, "value") || ""; - this._dirtyValue = false; - } else if (prevValueMode !== "filename" && curValueMode === "filename") { - this._value = ""; - } - - this._signalATypeChange(); - - this._value = sanitizeValueByType(this, this._value); - - const previouslySelectable = this._idlMemberApplies("setRangeText", prevType); - const nowSelectable = this._idlMemberApplies("setRangeText", curType); - if (!previouslySelectable && nowSelectable) { - this._selectionStart = 0; - this._selectionEnd = 0; - this._selectionDirection = "none"; - } - } - } - - super._attrModified(name, value, oldVal); - } - - // https://html.spec.whatwg.org/multipage/input.html#signal-a-type-change - _signalATypeChange() { - if (this._checkedness) { - this._removeOtherRadioCheckedness(); - } - } - - _formReset() { - const wrapper = idlUtils.wrapperForImpl(this); - this._value = sanitizeValueByType(this, wrapper.defaultValue); - this._dirtyValue = false; - this._checkedness = wrapper.defaultChecked; - this._dirtyCheckedness = false; - if (this._checkedness) { - this._removeOtherRadioCheckedness(); - } - } - - _changedFormOwner() { - if (this._checkedness) { - this._removeOtherRadioCheckedness(); - } - } - - get _otherRadioGroupElements() { - const wrapper = idlUtils.wrapperForImpl(this); - const root = this._radioButtonGroupRoot; - if (!root) { - return []; - } - - const result = []; - - const descendants = domSymbolTree.treeIterator(root); - for (const candidate of descendants) { - if (candidate._radioButtonGroupRoot !== root) { - continue; - } - - const candidateWrapper = idlUtils.wrapperForImpl(candidate); - if (!candidateWrapper.name || candidateWrapper.name !== wrapper.name) { - continue; - } - - if (candidate !== this) { - result.push(candidate); - } - } - return result; - } - - _removeOtherRadioCheckedness() { - for (const radioGroupElement of this._otherRadioGroupElements) { - radioGroupElement._checkedness = false; - } - } - - get _radioButtonGroupRoot() { - const wrapper = idlUtils.wrapperForImpl(this); - if (this.type !== "radio" || !wrapper.name) { - return null; - } - - let e = domSymbolTree.parent(this); - while (e) { - // root node of this home sub tree - // or the form element we belong to - if (!domSymbolTree.parent(e) || e.nodeName.toUpperCase() === "FORM") { - return e; - } - e = domSymbolTree.parent(e); - } - return null; - } - - _someInRadioGroup(name) { - if (this[name]) { - return true; - } - return this._otherRadioGroupElements.some(radioGroupElement => radioGroupElement[name]); - } - - get _mutable() { - return !isDisabled(this) && !this._hasAttributeAndApplies("readonly"); - } - - get labels() { - return getLabelsForLabelable(this); - } - - get form() { - return formOwner(this); - } - - get checked() { - return this._checkedness; - } - - set checked(checked) { - this._checkedness = Boolean(checked); - this._dirtyCheckedness = true; - if (this._checkedness) { - this._removeOtherRadioCheckedness(); - } - } - - get value() { - switch (valueAttributeMode(this.type)) { - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-value - case "value": - return this._getValue(); - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default - case "default": { - const attr = this.getAttributeNS(null, "value"); - return attr !== null ? attr : ""; - } - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default-on - case "default/on": { - const attr = this.getAttributeNS(null, "value"); - return attr !== null ? attr : "on"; - } - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-filename - case "filename": - return this.files.length ? "C:\\fakepath\\" + this.files[0].name : ""; - default: - throw new Error("jsdom internal error: unknown value attribute mode"); - } - } - - set value(val) { - switch (valueAttributeMode(this.type)) { - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-value - case "value": { - const oldValue = this._value; - this._value = sanitizeValueByType(this, val); - this._dirtyValue = true; - - if (oldValue !== this._value) { - this._selectionStart = this._selectionEnd = this._getValueLength(); - this._selectionDirection = "none"; - } - break; - } - - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default-on - case "default": - case "default/on": - this.setAttributeNS(null, "value", val); - break; - - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-filename - case "filename": - if (val === "") { - this.files.length = 0; - } else { - throw DOMException.create(this._globalObject, [ - "This input element accepts a filename, which may only be programmatically set to the empty string.", - "InvalidStateError" - ]); - } - break; - - default: - throw new Error("jsdom internal error: unknown value attribute mode"); - } - } - - // https://html.spec.whatwg.org/multipage/input.html#dom-input-valueasdate - get valueAsDate() { - if (!this._idlMemberApplies("valueAsDate")) { - return null; - } - - const window = this._ownerDocument._defaultView; - const convertedValue = this._convertStringToDate(this._value); - - if (convertedValue instanceof Date) { - return new window.Date(convertedValue.getTime()); - } - - return null; - } - - set valueAsDate(v) { - if (!this._idlMemberApplies("valueAsDate")) { - throw DOMException.create(this._globalObject, [ - "Failed to set the 'valueAsDate' property on 'HTMLInputElement': This input element does not support Date " + - "values.", - "InvalidStateError" - ]); - } - - if (v !== null && !isDate(v)) { - throw new TypeError("Failed to set the 'valueAsDate' property on 'HTMLInputElement': The provided value is " + - "not a Date."); - } - - if (v === null || isNaN(v)) { - this._value = ""; - } - - this._value = this._convertDateToString(v); - } - - // https://html.spec.whatwg.org/multipage/input.html#dom-input-valueasnumber - get valueAsNumber() { - if (!this._idlMemberApplies("valueAsNumber")) { - return NaN; - } - - const parsedValue = this._convertStringToNumber(this._value); - return parsedValue !== null ? parsedValue : NaN; - } - - set valueAsNumber(v) { - if (!isFinite(v)) { - throw new TypeError("Failed to set infinite value as Number"); - } - - if (!this._idlMemberApplies("valueAsNumber")) { - throw DOMException.create(this._globalObject, [ - "Failed to set the 'valueAsNumber' property on 'HTMLInputElement': This input element does not support " + - "Number values.", - "InvalidStateError" - ]); - } - - this._value = this._convertNumberToString(v); - } - - // https://html.spec.whatwg.org/multipage/input.html#dom-input-stepup - _stepUpdate(n, isUp) { - const methodName = isUp ? "stepUp" : "stepDown"; - if (!this._idlMemberApplies(methodName)) { - throw DOMException.create(this._globalObject, [ - `Failed to invoke '${methodName}' method on 'HTMLInputElement': ` + - "This input element does not support Number values.", - "InvalidStateError" - ]); - } - - const allowedValueStep = this._allowedValueStep; - if (allowedValueStep === null) { - throw DOMException.create(this._globalObject, [ - `Failed to invoke '${methodName}' method on 'HTMLInputElement': ` + - "This input element does not support value step.", - "InvalidStateError" - ]); - } - - const min = this._minimum; - const max = this._maximum; - - if (min !== null && max !== null) { - if (min > max) { - return; - } - - const candidateStepValue = this._stepAlign(Decimal.add(min, allowedValueStep), /* roundUp = */ false); - if (candidateStepValue.lt(min) || candidateStepValue.gt(max)) { - return; - } - } - - let value = 0; - try { - value = this.valueAsNumber; - if (isNaN(value)) { // Empty value is parsed as NaN. - value = 0; - } - } catch (error) { - // Step 5. Default value is 0. - } - value = new Decimal(value); - - const valueBeforeStepping = value; - - if (!this._isStepAligned(value)) { - value = this._stepAlign(value, /* roundUp = */ isUp); - } else { - let delta = Decimal.mul(n, allowedValueStep); - if (!isUp) { - delta = delta.neg(); - } - value = value.add(delta); - } - - if (min !== null && value.lt(min)) { - value = this._stepAlign(min, /* roundUp = */ true); - } - - if (max !== null && value.gt(max)) { - value = this._stepAlign(max, /* roundUp = */ false); - } - - if (isUp ? value.lt(valueBeforeStepping) : value.gt(valueBeforeStepping)) { - return; - } - - this._value = this._convertNumberToString(value.toNumber()); - } - - stepDown(n = 1) { - return this._stepUpdate(n, false); - } - - stepUp(n = 1) { - return this._stepUpdate(n, true); - } - - get files() { - if (this.type === "file") { - this[filesSymbol] = this[filesSymbol] || FileList.createImpl(this._globalObject); - } else { - this[filesSymbol] = null; - } - return this[filesSymbol]; - } - - set files(value) { - if (this.type === "file" && value !== null) { - this[filesSymbol] = value; - } - } - - get type() { - const typeAttribute = this.getAttributeNS(null, "type"); - return getTypeFromAttribute(typeAttribute); - } - - set type(type) { - this.setAttributeNS(null, "type", type); - } - - _dispatchSelectEvent() { - fireAnEvent("select", this, undefined, { bubbles: true, cancelable: true }); - } - - _getValueLength() { - return typeof this.value === "string" ? this.value.length : 0; - } - - select() { - if (!this._idlMemberApplies("select")) { - return; - } - - this._selectionStart = 0; - this._selectionEnd = this._getValueLength(); - this._selectionDirection = "none"; - this._dispatchSelectEvent(); - } - - get selectionStart() { - if (!this._idlMemberApplies("selectionStart")) { - return null; - } - - return this._selectionStart; - } - - set selectionStart(start) { - if (!this._idlMemberApplies("selectionStart")) { - throw DOMException.create(this._globalObject, ["The object is in an invalid state.", "InvalidStateError"]); - } - - this.setSelectionRange(start, Math.max(start, this._selectionEnd), this._selectionDirection); - } - - get selectionEnd() { - if (!this._idlMemberApplies("selectionEnd")) { - return null; - } - - return this._selectionEnd; - } - - set selectionEnd(end) { - if (!this._idlMemberApplies("selectionEnd")) { - throw DOMException.create(this._globalObject, ["The object is in an invalid state.", "InvalidStateError"]); - } - - this.setSelectionRange(this._selectionStart, end, this._selectionDirection); - } - - get selectionDirection() { - if (!this._idlMemberApplies("selectionDirection")) { - return null; - } - - return this._selectionDirection; - } - - set selectionDirection(dir) { - if (!this._idlMemberApplies("selectionDirection")) { - throw DOMException.create(this._globalObject, ["The object is in an invalid state.", "InvalidStateError"]); - } - - this.setSelectionRange(this._selectionStart, this._selectionEnd, dir); - } - - setSelectionRange(start, end, dir) { - if (!this._idlMemberApplies("setSelectionRange")) { - throw DOMException.create(this._globalObject, ["The object is in an invalid state.", "InvalidStateError"]); - } - - this._selectionEnd = Math.min(end, this._getValueLength()); - this._selectionStart = Math.min(start, this._selectionEnd); - this._selectionDirection = dir === "forward" || dir === "backward" ? dir : "none"; - this._dispatchSelectEvent(); - } - - setRangeText(repl, start, end, selectionMode = "preserve") { - if (!this._idlMemberApplies("setRangeText")) { - throw DOMException.create(this._globalObject, ["The object is in an invalid state.", "InvalidStateError"]); - } - - if (arguments.length < 2) { - start = this._selectionStart; - end = this._selectionEnd; - } else if (start > end) { - throw DOMException.create(this._globalObject, ["The index is not in the allowed range.", "IndexSizeError"]); - } - - start = Math.min(start, this._getValueLength()); - end = Math.min(end, this._getValueLength()); - - const val = this.value; - let selStart = this._selectionStart; - let selEnd = this._selectionEnd; - - this.value = val.slice(0, start) + repl + val.slice(end); - - const newEnd = start + this.value.length; - - if (selectionMode === "select") { - this.setSelectionRange(start, newEnd); - } else if (selectionMode === "start") { - this.setSelectionRange(start, start); - } else if (selectionMode === "end") { - this.setSelectionRange(newEnd, newEnd); - } else { // preserve - const delta = repl.length - (end - start); - - if (selStart > end) { - selStart += delta; - } else if (selStart > start) { - selStart = start; - } - - if (selEnd > end) { - selEnd += delta; - } else if (selEnd > start) { - selEnd = newEnd; - } - - this.setSelectionRange(selStart, selEnd); - } - } - - // https://html.spec.whatwg.org/multipage/input.html#the-list-attribute - get list() { - const id = this._getAttributeIfApplies("list"); - if (!id) { - return null; - } - - const el = this.getRootNode({}).getElementById(id); - - if (el && el.localName === "datalist") { - return el; - } - - return null; - } - - // Reflected IDL attribute does not care about whether the content attribute applies. - get maxLength() { - if (!this.hasAttributeNS(null, "maxlength")) { - return 524288; // stole this from chrome - } - return parseInt(this.getAttributeNS(null, "maxlength")); - } - - set maxLength(value) { - if (value < 0) { - throw DOMException.create(this._globalObject, ["The index is not in the allowed range.", "IndexSizeError"]); - } - this.setAttributeNS(null, "maxlength", String(value)); - } - - get minLength() { - if (!this.hasAttributeNS(null, "minlength")) { - return 0; - } - return parseInt(this.getAttributeNS(null, "minlength")); - } - - set minLength(value) { - if (value < 0) { - throw DOMException.create(this._globalObject, ["The index is not in the allowed range.", "IndexSizeError"]); - } - this.setAttributeNS(null, "minlength", String(value)); - } - - get size() { - if (!this.hasAttributeNS(null, "size")) { - return 20; - } - return parseInt(this.getAttributeNS(null, "size")); - } - - set size(value) { - if (value <= 0) { - throw DOMException.create(this._globalObject, ["The index is not in the allowed range.", "IndexSizeError"]); - } - this.setAttributeNS(null, "size", String(value)); - } - - // https://html.spec.whatwg.org/multipage/input.html#the-min-and-max-attributes - get _minimum() { - let min = this._defaultMinimum; - const attr = this._getAttributeIfApplies("min"); - if (attr !== null && this._convertStringToNumber !== undefined) { - const parsed = this._convertStringToNumber(attr); - if (parsed !== null) { - min = parsed; - } - } - return min; - } - - get _maximum() { - let max = this._defaultMaximum; - const attr = this._getAttributeIfApplies("max"); - if (attr !== null && this._convertStringToNumber !== undefined) { - const parsed = this._convertStringToNumber(attr); - if (parsed !== null) { - max = parsed; - } - } - return max; - } - - get _defaultMinimum() { - if (this.type === "range") { - return 0; - } - return null; - } - - get _defaultMaximum() { - if (this.type === "range") { - return 100; - } - return null; - } - - // https://html.spec.whatwg.org/multipage/input.html#concept-input-step - get _allowedValueStep() { - if (!this._contentAttributeApplies("step")) { - return null; - } - const attr = this.getAttributeNS(null, "step"); - if (attr === null) { - return this._defaultStep * this._stepScaleFactor; - } - if (asciiCaseInsensitiveMatch(attr, "any")) { - return null; - } - const parsedStep = parseFloatingPointNumber(attr); - if (parsedStep === null || parsedStep <= 0) { - return this._defaultStep * this._stepScaleFactor; - } - return parsedStep * this._stepScaleFactor; - } - - // 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() { - if (this._hasAttributeAndApplies("min")) { - const min = this._convertStringToNumber(this.getAttributeNS(null, "min")); - if (min !== null) { - return min; - } - } - if (this.hasAttributeNS(null, "value")) { - const value = this._convertStringToNumber(this.getAttributeNS(null, "value")); - if (value !== null) { - return value; - } - } - if (this._defaultStepBase !== null) { - return this._defaultStepBase; - } - return 0; - } - - // 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 null; - } - - // https://html.spec.whatwg.org/multipage/input.html#common-input-element-attributes - // When an attribute doesn't apply to an input element, user agents must ignore the attribute. - _contentAttributeApplies(attribute) { - return applicableTypesForContentAttribute[attribute].has(this.type); - } - - _hasAttributeAndApplies(attribute) { - return this._contentAttributeApplies(attribute) && this.hasAttributeNS(null, attribute); - } - - _getAttributeIfApplies(attribute) { - if (this._contentAttributeApplies(attribute)) { - return this.getAttributeNS(null, attribute); - } - return null; - } - - _idlMemberApplies(member, type = this.type) { - return applicableTypesForIDLMember[member].has(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._hasAttributeAndApplies("readonly"); - - // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled - return willNotValidateTypes.has(this.type) || readOnly; - } - - // https://html.spec.whatwg.org/multipage/input.html#concept-input-required - get _required() { - return this._hasAttributeAndApplies("required"); - } - - // https://html.spec.whatwg.org/multipage/input.html#has-a-periodic-domain - get _hasAPeriodicDomain() { - return this.type === "time"; - } - - // https://html.spec.whatwg.org/multipage/input.html#has-a-reversed-range - get _hasAReversedRange() { - return this._hasAPeriodicDomain && this._maximum < this._minimum; - } - - get validity() { - if (!this._validity) { - // Constraint validation: When an element has a reversed range, 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 the number obtained from that algorithm is more than the maximum and less - // than the minimum, the element is simultaneously suffering from an underflow and suffering - // from an overflow. - const reversedRangeSufferingOverUnderflow = () => { - const parsedValue = this._convertStringToNumber(this._value); - return parsedValue !== null && parsedValue > this._maximum && parsedValue < this._minimum; - }; - - const state = { - // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-being-missing - valueMissing: () => { - // https://html.spec.whatwg.org/multipage/input.html#the-required-attribute - // Constraint validation: If the element is required, and its value IDL attribute applies - // and is in the mode value, and the element is mutable, and the element's value is the - // empty string, then the element is suffering from being missing. - // - // Note: As of today, the value IDL attribute always applies. - if (this._required && valueAttributeMode(this.type) === "value" && this._mutable && this._value === "") { - return true; - } - - switch (this.type) { - // 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. - case "checkbox": - if (this._required && !this._checkedness) { - return true; - } - break; - - // 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. - case "radio": - if (this._someInRadioGroup("_required") && !this._someInRadioGroup("checked")) { - return true; - } - break; - - // https://html.spec.whatwg.org/multipage/input.html#file-upload-state-(type=file) - // Constraint validation: If the element is required and the list of selected files is - // empty, then the element is suffering from being missing. - case "file": - if (this._required && this.files.length === 0) { - return true; - } - break; - } - - return false; - }, - - // 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 - rangeOverflow: () => { - // https://html.spec.whatwg.org/multipage/input.html#the-min-and-max-attributes - if (this._hasAReversedRange) { - return reversedRangeSufferingOverUnderflow(); - } - // Constraint validation: When the element has a maximum and does not have a reversed - // range, 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 the number obtained from that - // algorithm is more than the maximum, the element is suffering from an overflow. - if (this._maximum !== null) { - const parsedValue = this._convertStringToNumber(this._value); - if (parsedValue !== null && parsedValue > this._maximum) { - return true; - } - } - return false; - }, - - // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-an-underflow - rangeUnderflow: () => { - // https://html.spec.whatwg.org/multipage/input.html#the-min-and-max-attributes - if (this._hasAReversedRange) { - return reversedRangeSufferingOverUnderflow(); - } - // Constraint validation: When the element has a minimum and does not have a reversed - // range, 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 the number obtained from that - // algorithm is less than the minimum, the element is suffering from an underflow. - if (this._minimum !== null) { - const parsedValue = this._convertStringToNumber(this._value); - if (parsedValue !== null && parsedValue < this._minimum) { - return true; - } - } - return false; - }, - - // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-a-pattern-mismatch - patternMismatch: () => { - // https://html.spec.whatwg.org/multipage/input.html#the-pattern-attribute - if (this._value === "" || !this._hasAttributeAndApplies("pattern")) { - return false; - } - let regExp; - try { - const pattern = this.getAttributeNS(null, "pattern"); - // The pattern attribute should be matched against the entire value, not just any - // subset, so add ^ and $ anchors. But also check the validity of the regex itself - // first. - new RegExp(pattern, "u"); // eslint-disable-line no-new - regExp = new RegExp("^(?:" + pattern + ")$", "u"); - } catch (e) { - return false; - } - if (this._hasAttributeAndApplies("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: () => { - const allowedValueStep = this._allowedValueStep; - if (allowedValueStep === null) { - return false; - } - const number = this._convertStringToNumber(this._value); - return number !== null && !this._isStepAligned(number); - }, - - // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-a-type-mismatch - typeMismatch: () => { - switch (this.type) { - // 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. - case "url": - if (this._value !== "" && !isValidAbsoluteURL(this._value)) { - return true; - } - break; - - // 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. - case "email": - if (this._value !== "" && !isValidEmailAddress(this._getValue(), this.hasAttributeNS(null, "multiple"))) { - return true; - } - break; - } - return false; - } - }; - - this._validity = ValidityState.createImpl(this._globalObject, [], { - element: this, - state - }); - } - return this._validity; - } - - [cloningSteps](copy, node) { - copy._value = node._value; - copy._checkedness = node._checkedness; - copy._dirtyValue = node._dirtyValue; - copy._dirtyCheckedness = node._dirtyCheckedness; - } -} - -mixin(HTMLInputElementImpl.prototype, DefaultConstraintValidationImpl.prototype); - -module.exports = { - implementation: HTMLInputElementImpl -}; |