SAND-framework/public/assets/hyperform-0.12.0/dist/hyperform.js
2021-06-16 11:35:03 +02:00

3112 lines
92 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// hyperform.js.org
var hyperform = (function () {
'use strict';
var instances = new WeakMap();
/**
* wrap <form>s, window or document, that get treated with the global
* hyperform()
*/
function Wrapper(form, settings) {
/* do not allow more than one instance per form. Otherwise we'd end
* up with double event handlers, polyfills re-applied, ... */
var existing = instances.get(form);
if (existing) {
existing.settings = settings;
return existing;
}
this.form = form;
this.settings = settings;
this.observer = null;
instances.set(form, this);
}
Wrapper.prototype = {
destroy: function destroy() {
instances["delete"](this.form);
if (this._destruct) {
this._destruct();
}
}
};
/**
* try to get the appropriate wrapper for a specific element by looking up
* its parent chain
*
* @return Wrapper | undefined
*/
function get_wrapper(element) {
var wrapped;
if (element.form) {
/* try a shortcut with the element's <form> */
wrapped = instances.get(element.form);
}
/* walk up the parent nodes until document (including) */
while (!wrapped && element) {
wrapped = instances.get(element);
element = element.parentNode;
}
if (!wrapped) {
/* try the global instance, if exists. This may also be undefined. */
wrapped = instances.get(window);
}
return wrapped;
}
/**
* filter a form's elements for the ones needing validation prior to
* a submit
*
* Returns an array of form elements.
*/
function get_validated_elements(form) {
var wrapped_form = get_wrapper(form);
return Array.prototype.filter.call(form.elements, function (element) {
/* it must have a name (or validating nameless inputs is allowed) */
if (element.getAttribute('name') || wrapped_form && wrapped_form.settings.validateNameless) {
return true;
}
return false;
});
}
var registry = Object.create(null);
/**
* run all actions registered for a hook
*
* Every action gets called with a state object as `this` argument and with the
* hook's call arguments as call arguments.
*
* @return mixed the returned value of the action calls or undefined
*/
function call_hook(hook) {
var result;
var call_args = Array.prototype.slice.call(arguments, 1);
if (hook in registry) {
result = registry[hook].reduce(function (args) {
return function (previousResult, currentAction) {
var interimResult = currentAction.apply({
state: previousResult,
hook: hook
}, args);
return interimResult !== undefined ? interimResult : previousResult;
};
}(call_args), result);
}
return result;
}
/**
* Filter a value through hooked functions
*
* Allows for additional parameters:
* js> do_filter('foo', null, current_element)
*/
function do_filter(hook, initial_value) {
var result = initial_value;
var call_args = Array.prototype.slice.call(arguments, 1);
if (hook in registry) {
result = registry[hook].reduce(function (previousResult, currentAction) {
call_args[0] = previousResult;
var interimResult = currentAction.apply({
state: previousResult,
hook: hook
}, call_args);
return interimResult !== undefined ? interimResult : previousResult;
}, result);
}
return result;
}
/**
* remove an action again
*/
function remove_hook(hook, action) {
if (hook in registry) {
for (var i = 0; i < registry[hook].length; i++) {
if (registry[hook][i] === action) {
registry[hook].splice(i, 1);
break;
}
}
}
}
/**
* add an action to a hook
*/
function add_hook(hook, action, position) {
if (!(hook in registry)) {
registry[hook] = [];
}
if (position === undefined) {
position = registry[hook].length;
}
registry[hook].splice(position, 0, action);
}
/**
* return either the data of a hook call or the result of action, if the
* former is undefined
*
* @return function a function wrapper around action
*/
function return_hook_or (hook, action) {
return function () {
var data = call_hook(hook, Array.prototype.slice.call(arguments));
if (data !== undefined) {
return data;
}
return action.apply(this, arguments);
};
}
/* the following code is borrowed from the WebComponents project, licensed
* under the BSD license. Source:
* <https://github.com/webcomponents/webcomponentsjs/blob/5283db1459fa2323e5bfc8b9b5cc1753ed85e3d0/src/WebComponents/dom.js#L53-L78>
*/
// defaultPrevented is broken in IE.
// https://connect.microsoft.com/IE/feedback/details/790389/event-defaultprevented-returns-false-after-preventdefault-was-called
var workingDefaultPrevented = function () {
var e = document.createEvent('Event');
e.initEvent('foo', true, true);
e.preventDefault();
return e.defaultPrevented;
}();
if (!workingDefaultPrevented) {
var origPreventDefault = window.Event.prototype.preventDefault;
window.Event.prototype.preventDefault = function () {
if (!this.cancelable) {
return;
}
origPreventDefault.call(this);
Object.defineProperty(this, 'defaultPrevented', {
get: function get() {
return true;
},
configurable: true
});
};
}
/* end of borrowed code */
function create_event(name) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref$bubbles = _ref.bubbles,
bubbles = _ref$bubbles === void 0 ? true : _ref$bubbles,
_ref$cancelable = _ref.cancelable,
cancelable = _ref$cancelable === void 0 ? false : _ref$cancelable;
var event = document.createEvent('Event');
event.initEvent(name, bubbles, cancelable);
return event;
}
function trigger_event (element, event) {
var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
_ref2$bubbles = _ref2.bubbles,
bubbles = _ref2$bubbles === void 0 ? true : _ref2$bubbles,
_ref2$cancelable = _ref2.cancelable,
cancelable = _ref2$cancelable === void 0 ? false : _ref2$cancelable;
var payload = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
if (!(event instanceof window.Event)) {
event = create_event(event, {
bubbles: bubbles,
cancelable: cancelable
});
}
for (var key in payload) {
if (payload.hasOwnProperty(key)) {
event[key] = payload[key];
}
}
element.dispatchEvent(event);
return event;
}
/* and datetime-local? Spec says “Nah!” */
var dates = ['datetime', 'date', 'month', 'week', 'time'];
var plain_numbers = ['number', 'range'];
/* everything that returns something meaningful for valueAsNumber and
* can have the step attribute */
var numbers = dates.concat(plain_numbers, 'datetime-local');
/* the spec says to only check those for syntax in validity.typeMismatch.
* ¯\_(ツ)_/¯ */
var type_checked = ['email', 'url'];
/* check these for validity.badInput */
var input_checked = ['email', 'date', 'month', 'week', 'time', 'datetime', 'datetime-local', 'number', 'range', 'color'];
var text = ['text', 'search', 'tel', 'password'].concat(type_checked);
/* input element types, that are candidates for the validation API.
* Missing from this set are: button, hidden, menu (from <button>), reset and
* the types for non-<input> elements. */
var validation_candidates = ['checkbox', 'color', 'file', 'image', 'radio', 'submit'].concat(numbers, text);
/* all known types of <input> */
var inputs = ['button', 'hidden', 'reset'].concat(validation_candidates);
/* apparently <select> and <textarea> have types of their own */
var non_inputs = ['select-one', 'select-multiple', 'textarea'];
/**
* get the element's type in a backwards-compatible way
*/
function get_type (element) {
if (element instanceof window.HTMLTextAreaElement) {
return 'textarea';
} else if (element instanceof window.HTMLSelectElement) {
return element.hasAttribute('multiple') ? 'select-multiple' : 'select-one';
} else if (element instanceof window.HTMLButtonElement) {
return (element.getAttribute('type') || 'submit').toLowerCase();
} else if (element instanceof window.HTMLInputElement) {
var attr = (element.getAttribute('type') || '').toLowerCase();
if (attr && inputs.indexOf(attr) > -1) {
return attr;
} else {
/* perhaps the DOM has in-depth knowledge. Take that before returning
* 'text'. */
return element.type || 'text';
}
}
return '';
}
/**
* check if an element should be ignored due to any of its parents
*
* Checks <fieldset disabled> and <datalist>.
*/
function is_in_disallowed_parent(element) {
var p = element.parentNode;
while (p && p.nodeType === 1) {
if (p instanceof window.HTMLFieldSetElement && p.hasAttribute('disabled')) {
/* quick return, if it's a child of a disabled fieldset */
return true;
} else if (p.nodeName.toUpperCase() === 'DATALIST') {
/* quick return, if it's a child of a datalist
* Do not use HTMLDataListElement to support older browsers,
* too.
* @see https://html.spec.whatwg.org/multipage/forms.html#the-datalist-element:barred-from-constraint-validation
*/
return true;
} else if (p === element.form) {
/* the outer boundary. We can stop looking for relevant elements. */
break;
}
p = p.parentNode;
}
return false;
}
/**
* check if an element is a candidate for constraint validation
*
* @see https://html.spec.whatwg.org/multipage/forms.html#barred-from-constraint-validation
*/
function is_validation_candidate (element) {
/* allow a shortcut via filters, e.g. to validate type=hidden fields */
var filtered = do_filter('is_validation_candidate', null, element);
if (filtered !== null) {
return !!filtered;
}
/* it must be any of those elements */
if (element instanceof window.HTMLSelectElement || element instanceof window.HTMLTextAreaElement || element instanceof window.HTMLButtonElement || element instanceof window.HTMLInputElement) {
var type = get_type(element);
/* its type must be in the whitelist */
if (non_inputs.indexOf(type) > -1 || validation_candidates.indexOf(type) > -1) {
/* it mustn't be disabled or readonly */
if (!element.hasAttribute('disabled') && !element.hasAttribute('readonly')) {
var wrapped_form = get_wrapper(element);
if (
/* the parent form doesn't allow non-standard "novalidate" attributes... */
wrapped_form && !wrapped_form.settings.novalidateOnElements ||
/* ...or it doesn't have such an attribute/property */
!element.hasAttribute('novalidate') && !element.noValidate) {
/* it isn't part of a <fieldset disabled> */
if (!is_in_disallowed_parent(element)) {
/* then it's a candidate */
return true;
}
}
}
}
}
/* this is no HTML5 validation candidate... */
return false;
}
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function mark (obj) {
if (['object', 'function'].indexOf(_typeof(obj)) > -1) {
delete obj.__hyperform;
Object.defineProperty(obj, '__hyperform', {
configurable: true,
enumerable: false,
value: true
});
}
return obj;
}
function format_date (date) {
var part = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
switch (part) {
case 'date':
return (date.toLocaleDateString || date.toDateString).call(date);
case 'time':
return (date.toLocaleTimeString || date.toTimeString).call(date);
case 'month':
return 'toLocaleDateString' in date ? date.toLocaleDateString(undefined, {
year: 'numeric',
month: '2-digit'
}) : date.toDateString();
// case 'week':
// TODO
default:
return (date.toLocaleString || date.toString).call(date);
}
}
function sprintf (str) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var args_length = args.length;
var global_index = 0;
return str.replace(/%([0-9]+\$)?([sl])/g, function (match, position, type) {
var local_index = global_index;
if (position) {
local_index = Number(position.replace(/\$$/, '')) - 1;
}
global_index += 1;
var arg = '';
if (args_length > local_index) {
arg = args[local_index];
}
if (arg instanceof Date || typeof arg === 'number' || arg instanceof Number) {
/* try getting a localized representation of dates and numbers, if the
* browser supports this */
if (type === 'l') {
arg = (arg.toLocaleString || arg.toString).call(arg);
} else {
arg = arg.toString();
}
}
return arg;
});
}
/* For a given date, get the ISO week number
*
* Source: http://stackoverflow.com/a/6117889/113195
*
* Based on information at:
*
* http://www.merlyn.demon.co.uk/weekcalc.htm#WNR
*
* Algorithm is to find nearest thursday, it's year
* is the year of the week number. Then get weeks
* between that date and the first day of that year.
*
* Note that dates in one year can be weeks of previous
* or next year, overlap is up to 3 days.
*
* e.g. 2014/12/29 is Monday in week 1 of 2015
* 2012/1/1 is Sunday in week 52 of 2011
*/
function get_week_of_year (d) {
/* Copy date so don't modify original */
d = new Date(+d);
d.setUTCHours(0, 0, 0);
/* Set to nearest Thursday: current date + 4 - current day number
* Make Sunday's day number 7 */
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
/* Get first day of year */
var yearStart = new Date(d.getUTCFullYear(), 0, 1);
/* Calculate full weeks to nearest Thursday */
var weekNo = Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
/* Return array of year and week number */
return [d.getUTCFullYear(), weekNo];
}
function pad(num) {
var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
var s = num + '';
while (s.length < size) {
s = '0' + s;
}
return s;
}
/**
* calculate a string from a date according to HTML5
*/
function date_to_string(date, element_type) {
if (!(date instanceof Date)) {
return null;
}
switch (element_type) {
case 'datetime':
return date_to_string(date, 'date') + 'T' + date_to_string(date, 'time');
case 'datetime-local':
return sprintf('%s-%s-%sT%s:%s:%s.%s', date.getFullYear(), pad(date.getMonth() + 1), pad(date.getDate()), pad(date.getHours()), pad(date.getMinutes()), pad(date.getSeconds()), pad(date.getMilliseconds(), 3)).replace(/(:00)?\.000$/, '');
case 'date':
return sprintf('%s-%s-%s', date.getUTCFullYear(), pad(date.getUTCMonth() + 1), pad(date.getUTCDate()));
case 'month':
return sprintf('%s-%s', date.getUTCFullYear(), pad(date.getUTCMonth() + 1));
case 'week':
{
var params = get_week_of_year(date);
return sprintf.call(null, '%s-W%s', params[0], pad(params[1]));
}
case 'time':
return sprintf('%s:%s:%s.%s', pad(date.getUTCHours()), pad(date.getUTCMinutes()), pad(date.getUTCSeconds()), pad(date.getUTCMilliseconds(), 3)).replace(/(:00)?\.000$/, '');
}
return null;
}
/**
* return a new Date() representing the ISO date for a week number
*
* @see http://stackoverflow.com/a/16591175/113195
*/
function get_date_from_week (week, year) {
var date = new Date(Date.UTC(year, 0, 1 + (week - 1) * 7));
if (date.getUTCDay() <= 4
/* thursday */
) {
date.setUTCDate(date.getUTCDate() - date.getUTCDay() + 1);
} else {
date.setUTCDate(date.getUTCDate() + 8 - date.getUTCDay());
}
return date;
}
/**
* calculate a date from a string according to HTML5
*/
function string_to_date (string, element_type) {
var date;
switch (element_type) {
case 'datetime':
if (!/^([0-9]{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9])(?:\.([0-9]{1,3}))?)?$/.test(string)) {
return null;
}
date = new Date(string + 'z');
return isNaN(date.valueOf()) ? null : date;
case 'date':
if (!/^([0-9]{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/.test(string)) {
return null;
}
date = new Date(string);
return isNaN(date.valueOf()) ? null : date;
case 'month':
if (!/^([0-9]{4})-(0[1-9]|1[012])$/.test(string)) {
return null;
}
date = new Date(string);
return isNaN(date.valueOf()) ? null : date;
case 'week':
if (!/^([0-9]{4})-W(0[1-9]|[1234][0-9]|5[0-3])$/.test(string)) {
return null;
}
return get_date_from_week(Number(RegExp.$2), Number(RegExp.$1));
case 'time':
if (!/^([01][0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9])(?:\.([0-9]{1,3}))?)?$/.test(string)) {
return null;
}
date = new Date('1970-01-01T' + string + 'z');
return date;
}
return null;
}
/**
* calculate a number from a string according to HTML5
*/
function string_to_number (string, element_type) {
var rval = string_to_date(string, element_type);
if (rval !== null) {
return +rval;
}
/* not parseFloat, because we want NaN for invalid values like "1.2xxy" */
return Number(string);
}
/**
* the following validation messages are from Firefox source,
* http://mxr.mozilla.org/mozilla-central/source/dom/locales/en-US/chrome/dom/dom.properties
* released under MPL license, http://mozilla.org/MPL/2.0/.
*/
var catalog = {
en: {
TextTooLong: 'Please shorten this text to %l characters or less (you are currently using %l characters).',
ValueMissing: 'Please fill out this field.',
CheckboxMissing: 'Please check this box if you want to proceed.',
RadioMissing: 'Please select one of these options.',
FileMissing: 'Please select a file.',
SelectMissing: 'Please select an item in the list.',
InvalidEmail: 'Please enter an email address.',
InvalidURL: 'Please enter a URL.',
PatternMismatch: 'Please match the requested format.',
PatternMismatchWithTitle: 'Please match the requested format: %l.',
NumberRangeOverflow: 'Please select a value that is no more than %l.',
DateRangeOverflow: 'Please select a value that is no later than %l.',
TimeRangeOverflow: 'Please select a value that is no later than %l.',
NumberRangeUnderflow: 'Please select a value that is no less than %l.',
DateRangeUnderflow: 'Please select a value that is no earlier than %l.',
TimeRangeUnderflow: 'Please select a value that is no earlier than %l.',
StepMismatch: 'Please select a valid value. The two nearest valid values are %l and %l.',
StepMismatchOneValue: 'Please select a valid value. The nearest valid value is %l.',
BadInputNumber: 'Please enter a number.'
}
};
/**
* the global language Hyperform will use
*/
var language = 'en';
/**
* the base language according to BCP47, i.e., only the piece before the first hyphen
*/
var base_lang = 'en';
/**
* set the language for Hyperforms messages
*/
function set_language(newlang) {
language = newlang;
base_lang = newlang.replace(/[-_].*/, '');
}
/**
* add a lookup catalog "string: translation" for a language
*/
function add_translation(lang, new_catalog) {
if (!(lang in catalog)) {
catalog[lang] = {};
}
for (var key in new_catalog) {
if (new_catalog.hasOwnProperty(key)) {
catalog[lang][key] = new_catalog[key];
}
}
}
/**
* return `s` translated into the current language
*
* Defaults to the base language and then English if the former has no
* translation for `s`.
*/
function _ (s) {
if (language in catalog && s in catalog[language]) {
return catalog[language][s];
} else if (base_lang in catalog && s in catalog[base_lang]) {
return catalog[base_lang][s];
} else if (s in catalog.en) {
return catalog.en[s];
}
return s;
}
var default_step = {
'datetime-local': 60,
datetime: 60,
time: 60
};
var step_scale_factor = {
'datetime-local': 1000,
datetime: 1000,
date: 86400000,
week: 604800000,
time: 1000
};
var default_step_base = {
week: -259200000
};
var default_min = {
range: 0
};
var default_max = {
range: 100
};
/**
* get previous and next valid values for a stepped input element
*/
function get_next_valid (element) {
var n = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
var type = get_type(element);
var aMin = element.getAttribute('min');
var min = default_min[type] || NaN;
if (aMin) {
var pMin = string_to_number(aMin, type);
if (!isNaN(pMin)) {
min = pMin;
}
}
var aMax = element.getAttribute('max');
var max = default_max[type] || NaN;
if (aMax) {
var pMax = string_to_number(aMax, type);
if (!isNaN(pMax)) {
max = pMax;
}
}
var aStep = element.getAttribute('step');
var step = default_step[type] || 1;
if (aStep && aStep.toLowerCase() === 'any') {
/* quick return: we cannot calculate prev and next */
return [_('any value'), _('any value')];
} else if (aStep) {
var pStep = string_to_number(aStep, type);
if (!isNaN(pStep)) {
step = pStep;
}
}
var default_value = string_to_number(element.getAttribute('value'), type);
var value = string_to_number(element.value || element.getAttribute('value'), type);
if (isNaN(value)) {
/* quick return: we cannot calculate without a solid base */
return [_('any valid value'), _('any valid value')];
}
var step_base = !isNaN(min) ? min : !isNaN(default_value) ? default_value : default_step_base[type] || 0;
var scale = step_scale_factor[type] || 1;
var prev = step_base + Math.floor((value - step_base) / (step * scale)) * (step * scale) * n;
var next = step_base + (Math.floor((value - step_base) / (step * scale)) + 1) * (step * scale) * n;
if (prev < min) {
prev = null;
} else if (prev > max) {
prev = max;
}
if (next > max) {
next = null;
} else if (next < min) {
next = min;
}
/* convert to date objects, if appropriate */
if (dates.indexOf(type) > -1) {
prev = date_to_string(new Date(prev), type);
next = date_to_string(new Date(next), type);
}
return [prev, next];
}
/**
* patch String.length to account for non-BMP characters
*
* @see https://mathiasbynens.be/notes/javascript-unicode
* We do not use the simple [...str].length, because it needs a ton of
* polyfills in older browsers.
*/
function unicode_string_length (str) {
return str.match(/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g).length;
}
/**
* internal storage for custom error messages
*/
var store = new WeakMap();
/**
* register custom error messages per element
*/
var custom_messages = {
set: function set(element, validator, message) {
var messages = store.get(element) || {};
messages[validator] = message;
store.set(element, messages);
return custom_messages;
},
get: function get(element, validator) {
var _default = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
var messages = store.get(element);
if (messages === undefined || !(validator in messages)) {
var data_id = 'data-' + validator.replace(/[A-Z]/g, '-$&').toLowerCase();
if (element.hasAttribute(data_id)) {
/* if the element has a data-validator attribute, use this as fallback.
* E.g., if validator == 'valueMissing', the element can specify a
* custom validation message like this:
* <input data-value-missing="Oh noes!">
*/
return element.getAttribute(data_id);
}
return _default;
}
return messages[validator];
},
"delete": function _delete(element) {
var validator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
if (!validator) {
return store["delete"](element);
}
var messages = store.get(element) || {};
if (validator in messages) {
delete messages[validator];
store.set(element, messages);
return true;
}
return false;
}
};
/**
* the internal storage for messages
*/
var store$1 = new WeakMap();
/* jshint -W053 */
/* allow new String() */
/**
* handle validation messages
*
* Falls back to browser-native errors, if any are available. The messages
* are String objects so that we can mark() them.
*/
var message_store = {
set: function set(element, message) {
var is_custom = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
if (element instanceof window.HTMLFieldSetElement) {
var wrapped_form = get_wrapper(element);
if (wrapped_form && !wrapped_form.settings.extendFieldset) {
/* make this a no-op for <fieldset> in strict mode */
return message_store;
}
}
if (typeof message === 'string') {
message = new String(message);
}
if (is_custom) {
message.is_custom = true;
}
mark(message);
store$1.set(element, message);
/* allow the :invalid selector to match */
if ('_original_setCustomValidity' in element) {
element._original_setCustomValidity(message.toString());
}
return message_store;
},
get: function get(element) {
var message = store$1.get(element);
if (message === undefined && '_original_validationMessage' in element) {
/* get the browser's validation message, if we have none. Maybe it
* knows more than we. */
message = new String(element._original_validationMessage);
}
return message ? message : new String('');
},
"delete": function _delete(element) {
var is_custom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if ('_original_setCustomValidity' in element) {
element._original_setCustomValidity('');
}
var message = store$1.get(element);
if (message && is_custom && !message.is_custom) {
/* do not delete "native" messages, if asked */
return false;
}
return store$1["delete"](element);
}
};
var internal_registry = new WeakMap();
/**
* A registry for custom validators
*
* slim wrapper around a WeakMap to ensure the values are arrays
* (hence allowing > 1 validators per element)
*/
var custom_validator_registry = {
set: function set(element, validator) {
var current = internal_registry.get(element) || [];
current.push(validator);
internal_registry.set(element, current);
return custom_validator_registry;
},
get: function get(element) {
return internal_registry.get(element) || [];
},
"delete": function _delete(element) {
return internal_registry["delete"](element);
}
};
/**
* test whether the element suffers from bad input
*/
function test_bad_input (element) {
var type = get_type(element);
if (input_checked.indexOf(type) === -1) {
/* we're not interested, thanks! */
return true;
}
/* the browser hides some bad input from the DOM, e.g. malformed numbers,
* email addresses with invalid punycode representation, ... We try to resort
* to the original method here. The assumption is, that a browser hiding
* bad input will hopefully also always support a proper
* ValidityState.badInput */
if (!element.value) {
if ('_original_validity' in element && !element._original_validity.__hyperform) {
return !element._original_validity.badInput;
}
/* no value and no original badInput: Assume all's right. */
return true;
}
var result = true;
switch (type) {
case 'color':
result = /^#[a-f0-9]{6}$/.test(element.value);
break;
case 'number':
case 'range':
result = !isNaN(Number(element.value));
break;
case 'datetime':
case 'date':
case 'month':
case 'week':
case 'time':
result = string_to_date(element.value, type) !== null;
break;
case 'datetime-local':
result = /^([0-9]{4,})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9])(?:\.([0-9]{1,3}))?)?$/.test(element.value);
break;
}
return result;
}
/**
* test the max attribute
*
* we use Number() instead of parseFloat(), because an invalid attribute
* value like "123abc" should result in an error.
*/
function test_max (element) {
var type = get_type(element);
if (!element.value || !element.hasAttribute('max')) {
/* we're not responsible here */
return true;
}
var value, max;
if (dates.indexOf(type) > -1) {
value = string_to_date(element.value, type);
value = value === null ? NaN : +value;
max = string_to_date(element.getAttribute('max'), type);
max = max === null ? NaN : +max;
} else {
value = Number(element.value);
max = Number(element.getAttribute('max'));
}
/* we cannot validate invalid values and trust on badInput, if isNaN(value) */
return isNaN(max) || isNaN(value) || value <= max;
}
/**
* test the maxlength attribute
*/
function test_maxlength (element) {
if (!element.value || text.indexOf(get_type(element)) === -1 || !element.hasAttribute('maxlength') || !element.getAttribute('maxlength') // catch maxlength=""
) {
return true;
}
var maxlength = parseInt(element.getAttribute('maxlength'), 10);
/* check, if the maxlength value is usable at all.
* We allow maxlength === 0 to basically disable input (Firefox does, too).
*/
if (isNaN(maxlength) || maxlength < 0) {
return true;
}
return unicode_string_length(element.value) <= maxlength;
}
/**
* test the min attribute
*
* we use Number() instead of parseFloat(), because an invalid attribute
* value like "123abc" should result in an error.
*/
function test_min (element) {
var type = get_type(element);
if (!element.value || !element.hasAttribute('min')) {
/* we're not responsible here */
return true;
}
var value, min;
if (dates.indexOf(type) > -1) {
value = string_to_date(element.value, type);
value = value === null ? NaN : +value;
min = string_to_date(element.getAttribute('min'), type);
min = min === null ? NaN : +min;
} else {
value = Number(element.value);
min = Number(element.getAttribute('min'));
}
/* we cannot validate invalid values and trust on badInput, if isNaN(value) */
return isNaN(min) || isNaN(value) || value >= min;
}
/**
* test the minlength attribute
*/
function test_minlength (element) {
if (!element.value || text.indexOf(get_type(element)) === -1 || !element.hasAttribute('minlength') || !element.getAttribute('minlength') // catch minlength=""
) {
return true;
}
var minlength = parseInt(element.getAttribute('minlength'), 10);
/* check, if the minlength value is usable at all. */
if (isNaN(minlength) || minlength < 0) {
return true;
}
return unicode_string_length(element.value) >= minlength;
}
/**
* test the pattern attribute
*/
function test_pattern (element) {
return !element.value || !element.hasAttribute('pattern') || new RegExp('^(?:' + element.getAttribute('pattern') + ')$').test(element.value);
}
/**
* get all radio buttons (including `element`) that belong to element's
* radio group
*/
function get_radiogroup(element) {
if (element.form) {
return Array.prototype.filter.call(element.form.elements, function (radio) {
return radio.type === 'radio' && radio.name === element.name;
});
}
return [element];
}
function has_submittable_option(select) {
/* Definition of the placeholder label option:
* https://www.w3.org/TR/html5/sec-forms.html#element-attrdef-select-required
* Being required (the first constraint in the spec) is trivially true, since
* this function is only called for such selects.
*/
var has_placeholder_option = !select.multiple && select.size <= 1 && select.options.length > 0 && select.options[0].parentNode == select && select.options[0].value === '';
return (
/* anything selected at all? That's redundant with the .some() call below,
* but more performant in the most probable error case. */
select.selectedIndex > -1 && Array.prototype.some.call(select.options, function (option) {
return (
/* it isn't the placeholder option */
(!has_placeholder_option || option.index !== 0) &&
/* it isn't disabled */
!option.disabled &&
/* and it is, in fact, selected */
option.selected
);
})
);
}
/**
* test the required attribute
*/
function test_required (element) {
if (element.type === 'radio') {
/* the happy (and quick) path for radios: */
if (element.hasAttribute('required') && element.checked) {
return true;
}
var radiogroup = get_radiogroup(element);
/* if any radio in the group is required, we need any (not necessarily the
* same) radio to be checked */
if (radiogroup.some(function (radio) {
return radio.hasAttribute('required');
})) {
return radiogroup.some(function (radio) {
return radio.checked;
});
}
/* not required, validation passes */
return true;
}
if (!element.hasAttribute('required')) {
/* nothing to do */
return true;
}
if (element instanceof window.HTMLSelectElement) {
return has_submittable_option(element);
}
return element.type === 'checkbox' ? element.checked : !!element.value;
}
/**
* test the step attribute
*/
function test_step (element) {
var type = get_type(element);
if (!element.value || numbers.indexOf(type) === -1 || (element.getAttribute('step') || '').toLowerCase() === 'any') {
/* we're not responsible here. Note: If no step attribute is given, we
* need to validate against the default step as per spec. */
return true;
}
var step = element.getAttribute('step');
if (step) {
step = string_to_number(step, type);
} else {
step = default_step[type] || 1;
}
if (step <= 0 || isNaN(step)) {
/* error in specified "step". We cannot validate against it, so the value
* is true. */
return true;
}
var scale = step_scale_factor[type] || 1;
var value = string_to_number(element.value, type);
var min = string_to_number(element.getAttribute('min') || element.getAttribute('value') || '', type);
if (isNaN(value)) {
/* we cannot compare an invalid value and trust that the badInput validator
* takes over from here */
return true;
}
if (isNaN(min)) {
min = default_step_base[type] || 0;
}
if (type === 'month') {
/* type=month has month-wide steps. See
* https://html.spec.whatwg.org/multipage/forms.html#month-state-%28type=month%29
*/
min = new Date(min).getUTCFullYear() * 12 + new Date(min).getUTCMonth();
value = new Date(value).getUTCFullYear() * 12 + new Date(value).getUTCMonth();
}
var result = Math.abs(min - value) % (step * scale);
return result < 0.00000001 ||
/* crappy floating-point arithmetics! */
result > step * scale - 0.00000001;
}
var ws_on_start_or_end = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
/**
* trim a string of whitespace
*
* We don't use String.trim() to remove the need to polyfill it.
*/
function trim (str) {
return str.replace(ws_on_start_or_end, '');
}
/**
* split a string on comma and trim the components
*
* As specified at
* https://html.spec.whatwg.org/multipage/infrastructure.html#split-a-string-on-commas
* plus removing empty entries.
*/
function comma_split (str) {
return str.split(',').map(function (item) {
return trim(item);
}).filter(function (b) {
return b;
});
}
/* we use a dummy <a> where we set the href to test URL validity
* The definition is out of the "global" scope so that JSDOM can be instantiated
* after loading Hyperform for tests.
*/
var url_canary;
/* see https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address */
var email_pattern = /^[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])?)*$/;
/**
* test the type-inherent syntax
*/
function test_type (element) {
var type = get_type(element);
if (type !== 'file' && !element.value || type !== 'file' && type_checked.indexOf(type) === -1) {
/* we're not responsible for this element */
return true;
}
var is_valid = true;
switch (type) {
case 'url':
{
if (!url_canary) {
url_canary = document.createElement('a');
}
var value = trim(element.value);
url_canary.href = value;
is_valid = url_canary.href === value || url_canary.href === value + '/';
break;
}
case 'email':
if (element.hasAttribute('multiple')) {
is_valid = comma_split(element.value).every(function (value) {
return email_pattern.test(value);
});
} else {
is_valid = email_pattern.test(trim(element.value));
}
break;
case 'file':
if ('files' in element && element.files.length && element.hasAttribute('accept')) {
var patterns = comma_split(element.getAttribute('accept')).map(function (pattern) {
if (/^(audio|video|image)\/\*$/.test(pattern)) {
pattern = new RegExp('^' + RegExp.$1 + '/.+$');
}
return pattern;
});
if (!patterns.length) {
break;
}
fileloop: for (var i = 0; i < element.files.length; i++) {
/* we need to match a whitelist, so pre-set with false */
var file_valid = false;
patternloop: for (var j = 0; j < patterns.length; j++) {
var file = element.files[i];
var pattern = patterns[j];
var fileprop = file.type;
if (typeof pattern === 'string' && pattern.substr(0, 1) === '.') {
if (file.name.search('.') === -1) {
/* no match with any file ending */
continue patternloop;
}
fileprop = file.name.substr(file.name.lastIndexOf('.'));
}
if (fileprop.search(pattern) === 0) {
/* we found one match and can quit looking */
file_valid = true;
break patternloop;
}
}
if (!file_valid) {
is_valid = false;
break fileloop;
}
}
}
}
return is_valid;
}
/**
* boilerplate function for all tests but customError
*/
function check(test, react) {
return function (element) {
var invalid = !test(element);
if (invalid) {
react(element);
}
return invalid;
};
}
/**
* create a common function to set error messages
*/
function set_msg(element, msgtype, _default) {
message_store.set(element, custom_messages.get(element, msgtype, _default));
}
var badInput = check(test_bad_input, function (element) {
return set_msg(element, 'badInput', _('Please match the requested type.'));
});
function customError(element) {
/* prevent infinite loops when the custom validators call setCustomValidity(),
* which in turn calls this code again. We check, if there is an already set
* custom validity message there. */
if (element.__hf_custom_validation_running) {
var msg = message_store.get(element);
return msg && msg.is_custom;
}
/* check, if there are custom validators in the registry, and call
* them. */
var custom_validators = custom_validator_registry.get(element);
var cvl = custom_validators.length;
var valid = true;
if (cvl) {
element.__hf_custom_validation_running = true;
for (var i = 0; i < cvl; i++) {
var result = custom_validators[i](element);
if (result !== undefined && !result) {
valid = false;
/* break on first invalid response */
break;
}
}
delete element.__hf_custom_validation_running;
}
/* check, if there are other validity messages already */
if (valid) {
var _msg = message_store.get(element);
valid = !(_msg.toString() && 'is_custom' in _msg);
}
return !valid;
}
var patternMismatch = check(test_pattern, function (element) {
set_msg(element, 'patternMismatch', element.title ? sprintf(_('PatternMismatchWithTitle'), element.title) : _('PatternMismatch'));
});
/**
* TODO: when rangeOverflow and rangeUnderflow are both called directly and
* successful, the inRange and outOfRange classes won't get removed, unless
* element.validityState.valid is queried, too.
*/
var rangeOverflow = check(test_max, function (element) {
var type = get_type(element);
var wrapper = get_wrapper(element);
var outOfRangeClass = wrapper && wrapper.settings.classes.outOfRange || 'hf-out-of-range';
var inRangeClass = wrapper && wrapper.settings.classes.inRange || 'hf-in-range';
var msg;
switch (type) {
case 'date':
case 'datetime':
case 'datetime-local':
msg = sprintf(_('DateRangeOverflow'), format_date(string_to_date(element.getAttribute('max'), type), type));
break;
case 'time':
msg = sprintf(_('TimeRangeOverflow'), format_date(string_to_date(element.getAttribute('max'), type), type));
break;
// case 'number':
default:
msg = sprintf(_('NumberRangeOverflow'), string_to_number(element.getAttribute('max'), type));
break;
}
set_msg(element, 'rangeOverflow', msg);
element.classList.add(outOfRangeClass);
element.classList.remove(inRangeClass);
});
var rangeUnderflow = check(test_min, function (element) {
var type = get_type(element);
var wrapper = get_wrapper(element);
var outOfRangeClass = wrapper && wrapper.settings.classes.outOfRange || 'hf-out-of-range';
var inRangeClass = wrapper && wrapper.settings.classes.inRange || 'hf-in-range';
var msg;
switch (type) {
case 'date':
case 'datetime':
case 'datetime-local':
msg = sprintf(_('DateRangeUnderflow'), format_date(string_to_date(element.getAttribute('min'), type), type));
break;
case 'time':
msg = sprintf(_('TimeRangeUnderflow'), format_date(string_to_date(element.getAttribute('min'), type), type));
break;
// case 'number':
default:
msg = sprintf(_('NumberRangeUnderflow'), string_to_number(element.getAttribute('min'), type));
break;
}
set_msg(element, 'rangeUnderflow', msg);
element.classList.add(outOfRangeClass);
element.classList.remove(inRangeClass);
});
var stepMismatch = check(test_step, function (element) {
var list = get_next_valid(element);
var min = list[0];
var max = list[1];
var sole = false;
var msg;
if (min === null) {
sole = max;
} else if (max === null) {
sole = min;
}
if (sole !== false) {
msg = sprintf(_('StepMismatchOneValue'), sole);
} else {
msg = sprintf(_('StepMismatch'), min, max);
}
set_msg(element, 'stepMismatch', msg);
});
var tooLong = check(test_maxlength, function (element) {
set_msg(element, 'tooLong', sprintf(_('TextTooLong'), element.getAttribute('maxlength'), unicode_string_length(element.value)));
});
var tooShort = check(test_minlength, function (element) {
set_msg(element, 'tooShort', sprintf(_('Please lengthen this text to %l characters or more (you are currently using %l characters).'), element.getAttribute('minlength'), unicode_string_length(element.value)));
});
var typeMismatch = check(test_type, function (element) {
var msg = _('Please use the appropriate format.');
var type = get_type(element);
if (type === 'email') {
if (element.hasAttribute('multiple')) {
msg = _('Please enter a comma separated list of email addresses.');
} else {
msg = _('InvalidEmail');
}
} else if (type === 'url') {
msg = _('InvalidURL');
} else if (type === 'file') {
msg = _('Please select a file of the correct type.');
}
set_msg(element, 'typeMismatch', msg);
});
var valueMissing = check(test_required, function (element) {
var msg = _('ValueMissing');
var type = get_type(element);
if (type === 'checkbox') {
msg = _('CheckboxMissing');
} else if (type === 'radio') {
msg = _('RadioMissing');
} else if (type === 'file') {
if (element.hasAttribute('multiple')) {
msg = _('Please select one or more files.');
} else {
msg = _('FileMissing');
}
} else if (element instanceof window.HTMLSelectElement) {
msg = _('SelectMissing');
}
set_msg(element, 'valueMissing', msg);
});
/**
* the "valid" property calls all other validity checkers and returns true,
* if all those return false.
*
* This is the major access point for _all_ other API methods, namely
* (check|report)Validity().
*/
var valid = function valid(element) {
var wrapper = get_wrapper(element);
var validClass = wrapper && wrapper.settings.classes.valid || 'hf-valid';
var invalidClass = wrapper && wrapper.settings.classes.invalid || 'hf-invalid';
var userInvalidClass = wrapper && wrapper.settings.classes.userInvalid || 'hf-user-invalid';
var userValidClass = wrapper && wrapper.settings.classes.userValid || 'hf-user-valid';
var inRangeClass = wrapper && wrapper.settings.classes.inRange || 'hf-in-range';
var outOfRangeClass = wrapper && wrapper.settings.classes.outOfRange || 'hf-out-of-range';
var validatedClass = wrapper && wrapper.settings.classes.validated || 'hf-validated';
element.classList.add(validatedClass);
for (var _i = 0, _arr = [badInput, customError, patternMismatch, rangeOverflow, rangeUnderflow, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing]; _i < _arr.length; _i++) {
var checker = _arr[_i];
if (checker(element)) {
element.classList.add(invalidClass);
element.classList.remove(validClass);
element.classList.remove(userValidClass);
if ((element.type === 'checkbox' || element.type === 'radio') && element.checked !== element.defaultChecked ||
/* the following test is trivially false for checkboxes/radios */
element.value !== element.defaultValue) {
element.classList.add(userInvalidClass);
} else {
element.classList.remove(userInvalidClass);
}
element.setAttribute('aria-invalid', 'true');
return false;
}
}
message_store["delete"](element);
element.classList.remove(invalidClass);
element.classList.remove(userInvalidClass);
element.classList.remove(outOfRangeClass);
element.classList.add(validClass);
element.classList.add(inRangeClass);
if (element.value !== element.defaultValue) {
element.classList.add(userValidClass);
} else {
element.classList.remove(userValidClass);
}
element.setAttribute('aria-invalid', 'false');
return true;
};
var validity_state_checkers = {
badInput: badInput,
customError: customError,
patternMismatch: patternMismatch,
rangeOverflow: rangeOverflow,
rangeUnderflow: rangeUnderflow,
stepMismatch: stepMismatch,
tooLong: tooLong,
tooShort: tooShort,
typeMismatch: typeMismatch,
valueMissing: valueMissing,
valid: valid
};
/**
* the validity state constructor
*/
var ValidityState = function ValidityState(element) {
if (!(element instanceof window.HTMLElement)) {
throw new Error('cannot create a ValidityState for a non-element');
}
var cached = ValidityState.cache.get(element);
if (cached) {
return cached;
}
if (!(this instanceof ValidityState)) {
/* working around a forgotten `new` */
return new ValidityState(element);
}
this.element = element;
ValidityState.cache.set(element, this);
};
/**
* the prototype for new validityState instances
*/
var ValidityStatePrototype = {};
ValidityState.prototype = ValidityStatePrototype;
ValidityState.cache = new WeakMap();
/* small wrapper around the actual validator to check if the validator
* should actually be called. `this` refers to the ValidityState object. */
var checker_getter = function checker_getter(prop, func) {
return function () {
if (!is_validation_candidate(this.element)) {
/* not being validated == valid by default
* return value == false for all props except "valid", because we test
* problems like badInput here */
return prop === 'valid';
}
return func(this.element);
};
};
/**
* copy functionality from the validity checkers to the ValidityState
* prototype
*/
for (var prop in validity_state_checkers) {
Object.defineProperty(ValidityStatePrototype, prop, {
configurable: true,
enumerable: true,
get: checker_getter(prop, validity_state_checkers[prop]),
set: undefined
});
}
/**
* mark the validity prototype, because that is what the client-facing
* code deals with mostly, not the property descriptor thing */
mark(ValidityStatePrototype);
/**
* check an element's validity with respect to it's form
*/
var checkValidity = return_hook_or('checkValidity', function (element) {
/* if this is a <form>, check validity of all child inputs */
if (element instanceof window.HTMLFormElement) {
return get_validated_elements(element).map(checkValidity).every(function (b) {
return b;
});
}
/* default is true, also for elements that are no validation candidates */
var valid = ValidityState(element).valid;
if (valid) {
var wrapped_form = get_wrapper(element);
if (wrapped_form && wrapped_form.settings.validEvent) {
trigger_event(element, 'valid');
}
} else {
trigger_event(element, 'invalid', {
cancelable: true
});
}
return valid;
});
/**
* counter that will be incremented with every call
*
* Will enforce uniqueness, as long as no more than 1 hyperform scripts
* are loaded. (In that case we still have the "random" part below.)
*/
var uid = 0;
/**
* generate a random ID
*
* @see https://gist.github.com/gordonbrander/2230317
*/
function generate_id () {
var prefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'hf_';
return prefix + uid++ + Math.random().toString(36).substr(2);
}
var warningsCache = new WeakMap();
var DefaultRenderer = {
/**
* called when a warning should become visible
*/
attachWarning: function attachWarning(warning, element) {
/* should also work, if element is last,
* http://stackoverflow.com/a/4793630/113195 */
element.parentNode.insertBefore(warning, element.nextSibling);
},
/**
* called when a warning should vanish
*/
detachWarning: function detachWarning(warning, element) {
/* be conservative here, since an overwritten attachWarning() might not
* actually have attached the warning. */
if (warning.parentNode) {
warning.parentNode.removeChild(warning);
}
},
/**
* called when feedback to an element's state should be handled
*
* i.e., showing and hiding warnings
*/
showWarning: function showWarning(element) {
var whole_form_validated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
/* don't render error messages on subsequent radio buttons of the
* same group. This assumes, that element.validity.valueMissing is the only
* possible validation failure for radio buttons. */
if (whole_form_validated && element.type === 'radio' && get_radiogroup(element)[0] !== element) {
return;
}
var msg = message_store.get(element).toString();
var warning = warningsCache.get(element);
if (msg) {
if (!warning) {
var wrapper = get_wrapper(element);
warning = document.createElement('div');
warning.className = wrapper && wrapper.settings.classes.warning || 'hf-warning';
warning.id = generate_id();
warning.setAttribute('aria-live', 'polite');
warningsCache.set(element, warning);
}
element.setAttribute('aria-errormessage', warning.id);
if (!element.hasAttribute('aria-describedby')) {
element.setAttribute('aria-describedby', warning.id);
}
Renderer.setMessage(warning, msg, element);
Renderer.attachWarning(warning, element);
} else if (warning && warning.parentNode) {
if (element.getAttribute('aria-describedby') === warning.id) {
element.removeAttribute('aria-describedby');
}
element.removeAttribute('aria-errormessage');
Renderer.detachWarning(warning, element);
}
},
/**
* set the warning's content
*
* Overwrite this method, if you want, e.g., to allow HTML in warnings
* or preprocess the content.
*/
setMessage: function setMessage(warning, message, element) {
warning.textContent = message;
}
};
var Renderer = {
attachWarning: DefaultRenderer.attachWarning,
detachWarning: DefaultRenderer.detachWarning,
showWarning: DefaultRenderer.showWarning,
setMessage: DefaultRenderer.setMessage,
set: function set(renderer, action) {
if (!action) {
action = DefaultRenderer[renderer];
}
Renderer[renderer] = action;
},
getWarning: function getWarning(element) {
return warningsCache.get(element);
}
};
/**
* check element's validity and report an error back to the user
*/
function reportValidity(element) {
/* if this is a <form>, report validity of all child inputs */
if (element instanceof window.HTMLFormElement) {
element.__hf_form_validation = true;
var form_valid = get_validated_elements(element).map(reportValidity).every(function (b) {
return b;
});
delete element.__hf_form_validation;
return form_valid;
}
/* we copy checkValidity() here, b/c we have to check if the "invalid"
* event was canceled. */
var valid = ValidityState(element).valid;
var event;
if (valid) {
var wrapped_form = get_wrapper(element);
if (wrapped_form && wrapped_form.settings.validEvent) {
event = trigger_event(element, 'valid', {
cancelable: true
});
}
} else {
event = trigger_event(element, 'invalid', {
cancelable: true
});
}
if (!event || !event.defaultPrevented) {
Renderer.showWarning(element, element.form && element.form.__hf_form_validation);
}
return valid;
}
/**
* set a custom validity message or delete it with an empty string
*/
function setCustomValidity(element, msg) {
if (!msg) {
message_store["delete"](element, true);
} else {
message_store.set(element, msg, true);
}
/* live-update the warning */
var warning = Renderer.getWarning(element);
if (warning) {
Renderer.setMessage(warning, msg, element);
}
/* update any classes if the validity state changes */
validity_state_checkers.valid(element);
}
/**
* implement the valueAsDate functionality
*
* @see https://html.spec.whatwg.org/multipage/forms.html#dom-input-valueasdate
*/
function valueAsDate(element) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
var type = get_type(element);
if (dates.indexOf(type) > -1) {
if (value !== undefined) {
/* setter: value must be null or a Date() */
if (value === null) {
element.value = '';
} else if (value instanceof Date) {
if (isNaN(value.getTime())) {
element.value = '';
} else {
element.value = date_to_string(value, type);
}
} else {
throw new window.DOMException('valueAsDate setter encountered invalid value', 'TypeError');
}
return;
}
var value_date = string_to_date(element.value, type);
return value_date instanceof Date ? value_date : null;
} else if (value !== undefined) {
/* trying to set a date on a not-date input fails */
throw new window.DOMException('valueAsDate setter cannot set date on this element', 'InvalidStateError');
}
return null;
}
/**
* implement the valueAsNumber functionality
*
* @see https://html.spec.whatwg.org/multipage/forms.html#dom-input-valueasnumber
*/
function valueAsNumber(element) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
var type = get_type(element);
if (numbers.indexOf(type) > -1) {
if (type === 'range' && element.hasAttribute('multiple')) {
/* @see https://html.spec.whatwg.org/multipage/forms.html#do-not-apply */
return NaN;
}
if (value !== undefined) {
/* setter: value must be NaN or a finite number */
if (isNaN(value)) {
element.value = '';
} else if (typeof value === 'number' && window.isFinite(value)) {
try {
/* try setting as a date, but... */
valueAsDate(element, new Date(value));
} catch (e) {
/* ... when valueAsDate is not responsible, ... */
if (!(e instanceof window.DOMException)) {
throw e;
}
/* ... set it via Number.toString(). */
element.value = value.toString();
}
} else {
throw new window.DOMException('valueAsNumber setter encountered invalid value', 'TypeError');
}
return;
}
return string_to_number(element.value, type);
} else if (value !== undefined) {
/* trying to set a number on a not-number input fails */
throw new window.DOMException('valueAsNumber setter cannot set number on this element', 'InvalidStateError');
}
return NaN;
}
/**
*
*/
function stepDown(element) {
var n = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
if (numbers.indexOf(get_type(element)) === -1) {
throw new window.DOMException('stepDown encountered invalid type', 'InvalidStateError');
}
if ((element.getAttribute('step') || '').toLowerCase() === 'any') {
throw new window.DOMException('stepDown encountered step "any"', 'InvalidStateError');
}
var prev = get_next_valid(element, n)[0];
if (prev !== null) {
valueAsNumber(element, prev);
}
}
/**
*
*/
function stepUp(element) {
var n = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
if (numbers.indexOf(get_type(element)) === -1) {
throw new window.DOMException('stepUp encountered invalid type', 'InvalidStateError');
}
if ((element.getAttribute('step') || '').toLowerCase() === 'any') {
throw new window.DOMException('stepUp encountered step "any"', 'InvalidStateError');
}
var next = get_next_valid(element, n)[1];
if (next !== null) {
valueAsNumber(element, next);
}
}
/**
* get the validation message for an element, empty string, if the element
* satisfies all constraints.
*/
function validationMessage(element) {
var msg = message_store.get(element);
if (!msg) {
return '';
}
/* make it a primitive again, since message_store returns String(). */
return msg.toString();
}
/**
* check, if an element will be subject to HTML5 validation at all
*/
function willValidate(element) {
return is_validation_candidate(element);
}
/* shim layer for the Element.matches method */
var ep = window.Element.prototype;
var native_matches = ep.matches || ep.matchesSelector || ep.msMatchesSelector || ep.webkitMatchesSelector;
function matches (element, selector) {
return native_matches.call(element, selector);
}
/**
* submit a form, because `element` triggered it
*
* This method also dispatches a submit event on the form prior to the
* submission. The event contains the trigger element as `submittedVia`.
*
* If the element is a button with a name, the name=value pair will be added
* to the submitted data.
*/
function submit_form_via(element) {
/* apparently, the submit event is not triggered in most browsers on
* the submit() method, so we do it manually here to model a natural
* submit as closely as possible.
* Now to the fun fact: If you trigger a submit event from a form, what
* do you think should happen?
* 1) the form will be automagically submitted by the browser, or
* 2) nothing.
* And as you already suspected, the correct answer is: both! Firefox
* opts for 1), Chrome for 2). Yay! */
var event_got_cancelled;
var submit_event = create_event('submit', {
cancelable: true
});
/* force Firefox to not submit the form, then fake preventDefault() */
submit_event.preventDefault();
Object.defineProperty(submit_event, 'defaultPrevented', {
value: false,
writable: true
});
Object.defineProperty(submit_event, 'preventDefault', {
value: function value() {
return submit_event.defaultPrevented = event_got_cancelled = true;
},
writable: true
});
trigger_event(element.form, submit_event, {}, {
submittedVia: element
});
if (!event_got_cancelled) {
add_submit_field(element);
window.HTMLFormElement.prototype.submit.call(element.form);
window.setTimeout(function () {
return remove_submit_field(element);
});
}
}
/**
* if a submit button was clicked, add its name=value by means of a type=hidden
* input field
*/
function add_submit_field(button) {
if (['image', 'submit'].indexOf(button.type) > -1 && button.name) {
var wrapper = get_wrapper(button.form) || {};
var submit_helper = wrapper.submit_helper;
if (submit_helper) {
if (submit_helper.parentNode) {
submit_helper.parentNode.removeChild(submit_helper);
}
} else {
submit_helper = document.createElement('input');
submit_helper.type = 'hidden';
wrapper.submit_helper = submit_helper;
}
submit_helper.name = button.name;
submit_helper.value = button.value;
button.form.appendChild(submit_helper);
}
}
/**
* remove a possible helper input, that was added by `add_submit_field`
*/
function remove_submit_field(button) {
if (['image', 'submit'].indexOf(button.type) > -1 && button.name) {
var wrapper = get_wrapper(button.form) || {};
var submit_helper = wrapper.submit_helper;
if (submit_helper && submit_helper.parentNode) {
submit_helper.parentNode.removeChild(submit_helper);
}
}
}
/**
* check a form's validity and submit it
*
* The method triggers a cancellable `validate` event on the form. If the
* event is cancelled, form submission will be aborted, too.
*
* If the form is found to contain invalid fields, focus the first field.
*/
function check$1(button) {
/* trigger a "validate" event on the form to be submitted */
var val_event = trigger_event(button.form, 'validate', {
cancelable: true
});
if (val_event.defaultPrevented) {
/* skip the whole submit thing, if the validation is canceled. A user
* can still call form.submit() afterwards. */
return;
}
var valid = true;
var first_invalid;
button.form.__hf_form_validation = true;
get_validated_elements(button.form).map(function (element) {
if (!reportValidity(element)) {
valid = false;
if (!first_invalid && 'focus' in element) {
first_invalid = element;
}
}
});
delete button.form.__hf_form_validation;
if (valid) {
submit_form_via(button);
} else if (first_invalid) {
/* focus the first invalid element, if validation went south */
first_invalid.focus();
/* tell the tale, if anyone wants to react to it */
trigger_event(button.form, 'forminvalid');
}
}
/**
* test if node is a submit button
*/
function is_submit_button(node) {
return (
/* must be an input or button element... */
(node.nodeName === 'INPUT' || node.nodeName === 'BUTTON') && (
/* ...and have a submitting type */
node.type === 'image' || node.type === 'submit')
);
}
/**
* test, if the click event would trigger a submit
*/
function is_submitting_click(event, button) {
return (
/* prevented default: won't trigger a submit */
!event.defaultPrevented && (
/* left button or middle button (submits in Chrome) */
!('button' in event) || event.button < 2) &&
/* must be a submit button... */
is_submit_button(button) &&
/* the button needs a form, that's going to be submitted */
button.form &&
/* again, if the form should not be validated, we're out of the game */
!button.form.hasAttribute('novalidate')
);
}
/**
* test, if the keypress event would trigger a submit
*/
function is_submitting_keypress(event) {
return (
/* prevented default: won't trigger a submit */
!event.defaultPrevented && (
/* ...and <Enter> was pressed... */
event.keyCode === 13 &&
/* ...on an <input> that is... */
event.target.nodeName === 'INPUT' &&
/* ...a standard text input field (not checkbox, ...) */
text.indexOf(event.target.type) > -1 ||
/* or <Enter> or <Space> was pressed... */
(event.keyCode === 13 || event.keyCode === 32) &&
/* ...on a submit button */
is_submit_button(event.target)) &&
/* there's a form... */
event.target.form &&
/* ...and the form allows validation */
!event.target.form.hasAttribute('novalidate')
);
}
/**
* catch clicks to children of <button>s
*/
function get_clicked_button(element) {
if (is_submit_button(element)) {
return element;
} else if (matches(element, 'button:not([type]) *, button[type="submit"] *')) {
return get_clicked_button(element.parentNode);
} else {
return null;
}
}
/**
* return event handler to catch explicit submission by click on a button
*/
function get_click_handler() {
var ignore = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
return function (event) {
var button = get_clicked_button(event.target);
if (button && is_submitting_click(event, button)) {
event.preventDefault();
if (ignore || button.hasAttribute('formnovalidate')) {
/* if validation should be ignored, we're not interested in any checks */
submit_form_via(button);
} else {
check$1(button);
}
}
};
}
var click_handler = get_click_handler();
var ignored_click_handler = get_click_handler(true);
/**
* catch implicit submission by pressing <Enter> in some situations
*/
function get_keypress_handler(ignore) {
return function keypress_handler(event) {
if (is_submitting_keypress(event)) {
event.preventDefault();
var wrapper = get_wrapper(event.target.form) || {
settings: {}
};
if (wrapper.settings.preventImplicitSubmit) {
/* user doesn't want an implicit submit. Cancel here. */
return;
}
/* check, that there is no submit button in the form. Otherwise
* that should be clicked. */
var el = event.target.form.elements.length;
var submit;
for (var i = 0; i < el; i++) {
if (['image', 'submit'].indexOf(event.target.form.elements[i].type) > -1) {
submit = event.target.form.elements[i];
break;
}
}
/* trigger an "implicit_submit" event on the form to be submitted */
var implicit_event = trigger_event(event.target.form, 'implicit_submit', {
cancelable: true
}, {
trigger: event.target,
submittedVia: submit || event.target
});
if (implicit_event.defaultPrevented) {
/* skip the submit, if implicit submit is canceled */
return;
}
if (submit) {
submit.click();
} else if (ignore) {
submit_form_via(event.target);
} else {
check$1(event.target);
}
}
};
}
var keypress_handler = get_keypress_handler();
var ignored_keypress_handler = get_keypress_handler(true);
/**
* catch all relevant events _prior_ to a form being submitted
*
* @param bool ignore bypass validation, when an attempt to submit the
* form is detected. True, when the wrapper's revalidate
* setting is 'never'.
*/
function catch_submit(listening_node) {
var ignore = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (ignore) {
listening_node.addEventListener('click', ignored_click_handler);
listening_node.addEventListener('keypress', ignored_keypress_handler);
} else {
listening_node.addEventListener('click', click_handler);
listening_node.addEventListener('keypress', keypress_handler);
}
}
/**
* decommission the event listeners from catch_submit() again
*/
function uncatch_submit(listening_node) {
listening_node.removeEventListener('click', ignored_click_handler);
listening_node.removeEventListener('keypress', ignored_keypress_handler);
listening_node.removeEventListener('click', click_handler);
listening_node.removeEventListener('keypress', keypress_handler);
}
/**
* add `property` to an element
*
* ATTENTION! This function will search for an equally named property on the
* *prototype* of an element, if element is a concrete DOM node. Do not use
* it as general-purpose property installer.
*
* js> installer(element, 'foo', { value: 'bar' });
* js> assert(element.foo === 'bar');
*/
function install_property (element, property, descriptor) {
descriptor.configurable = true;
descriptor.enumerable = true;
if ('value' in descriptor) {
descriptor.writable = true;
}
/* on concrete instances, i.e., <input> elements, the naive lookup
* yields undefined. We have to look on its prototype then. On elements
* like the actual HTMLInputElement object the first line works. */
var original_descriptor = Object.getOwnPropertyDescriptor(element, property);
if (original_descriptor === undefined) {
original_descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), property);
}
if (original_descriptor) {
if (original_descriptor.configurable === false) {
/* Safari <= 9 and PhantomJS will end up here :-( Nothing to do except
* warning */
var wrapper = get_wrapper(element);
if (wrapper && wrapper.settings.debug) {
/* global console */
console.log('[hyperform] cannot install custom property ' + property);
}
return false;
}
/* we already installed that property... */
if (original_descriptor.get && original_descriptor.get.__hyperform || original_descriptor.value && original_descriptor.value.__hyperform) {
return;
}
/* publish existing property under new name, if it's not from us */
Object.defineProperty(element, '_original_' + property, original_descriptor);
}
delete element[property];
Object.defineProperty(element, property, descriptor);
return true;
}
function is_field (element) {
return element instanceof window.HTMLButtonElement || element instanceof window.HTMLInputElement || element instanceof window.HTMLSelectElement || element instanceof window.HTMLTextAreaElement || element instanceof window.HTMLFieldSetElement || element === window.HTMLButtonElement.prototype || element === window.HTMLInputElement.prototype || element === window.HTMLSelectElement.prototype || element === window.HTMLTextAreaElement.prototype || element === window.HTMLFieldSetElement.prototype;
}
/**
* remove `property` from element and restore _original_property, if present
*/
function uninstall_property (element, property) {
try {
delete element[property];
} catch (e) {
/* Safari <= 9 and PhantomJS will end up here :-( Nothing to do except
* warning */
var wrapper = get_wrapper(element);
if (wrapper && wrapper.settings.debug) {
/* global console */
console.log('[hyperform] cannot uninstall custom property ' + property);
}
return false;
}
var original_descriptor = Object.getOwnPropertyDescriptor(element, '_original_' + property);
if (original_descriptor) {
Object.defineProperty(element, property, original_descriptor);
}
}
var gA = function gA(prop) {
return function () {
return do_filter('attr_get_' + prop, this.getAttribute(prop), this);
};
};
var sA = function sA(prop) {
return function (value) {
this.setAttribute(prop, do_filter('attr_set_' + prop, value, this));
};
};
var gAb = function gAb(prop) {
return function () {
return do_filter('attr_get_' + prop, this.hasAttribute(prop), this);
};
};
var sAb = function sAb(prop) {
return function (value) {
if (do_filter('attr_set_' + prop, value, this)) {
this.setAttribute(prop, prop);
} else {
this.removeAttribute(prop);
}
};
};
var gAn = function gAn(prop) {
return function () {
return do_filter('attr_get_' + prop, Math.max(0, Number(this.getAttribute(prop))), this);
};
};
var sAn = function sAn(prop) {
return function (value) {
value = do_filter('attr_set_' + prop, value, this);
if (/^[0-9]+$/.test(value)) {
this.setAttribute(prop, value);
}
};
};
function install_properties(element) {
for (var _i = 0, _arr = ['accept', 'max', 'min', 'pattern', 'placeholder', 'step']; _i < _arr.length; _i++) {
var prop = _arr[_i];
install_property(element, prop, {
get: gA(prop),
set: sA(prop)
});
}
for (var _i2 = 0, _arr2 = ['multiple', 'required', 'readOnly']; _i2 < _arr2.length; _i2++) {
var _prop = _arr2[_i2];
install_property(element, _prop, {
get: gAb(_prop.toLowerCase()),
set: sAb(_prop.toLowerCase())
});
}
for (var _i3 = 0, _arr3 = ['minLength', 'maxLength']; _i3 < _arr3.length; _i3++) {
var _prop2 = _arr3[_i3];
install_property(element, _prop2, {
get: gAn(_prop2.toLowerCase()),
set: sAn(_prop2.toLowerCase())
});
}
}
function uninstall_properties(element) {
for (var _i4 = 0, _arr4 = ['accept', 'max', 'min', 'pattern', 'placeholder', 'step', 'multiple', 'required', 'readOnly', 'minLength', 'maxLength']; _i4 < _arr4.length; _i4++) {
var prop = _arr4[_i4];
uninstall_property(element, prop);
}
}
var polyfills = {
checkValidity: {
value: mark(function () {
return checkValidity(this);
})
},
reportValidity: {
value: mark(function () {
return reportValidity(this);
})
},
setCustomValidity: {
value: mark(function (msg) {
return setCustomValidity(this, msg);
})
},
stepDown: {
value: mark(function () {
var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
return stepDown(this, n);
})
},
stepUp: {
value: mark(function () {
var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
return stepUp(this, n);
})
},
validationMessage: {
get: mark(function () {
return validationMessage(this);
})
},
validity: {
get: mark(function () {
return ValidityState(this);
})
},
valueAsDate: {
get: mark(function () {
return valueAsDate(this);
}),
set: mark(function (value) {
valueAsDate(this, value);
})
},
valueAsNumber: {
get: mark(function () {
return valueAsNumber(this);
}),
set: mark(function (value) {
valueAsNumber(this, value);
})
},
willValidate: {
get: mark(function () {
return willValidate(this);
})
}
};
function polyfill (element) {
if (is_field(element)) {
for (var prop in polyfills) {
install_property(element, prop, polyfills[prop]);
}
install_properties(element);
} else if (element instanceof window.HTMLFormElement || element === window.HTMLFormElement.prototype) {
install_property(element, 'checkValidity', polyfills.checkValidity);
install_property(element, 'reportValidity', polyfills.reportValidity);
}
}
function polyunfill (element) {
if (is_field(element)) {
uninstall_property(element, 'checkValidity');
uninstall_property(element, 'reportValidity');
uninstall_property(element, 'setCustomValidity');
uninstall_property(element, 'stepDown');
uninstall_property(element, 'stepUp');
uninstall_property(element, 'validationMessage');
uninstall_property(element, 'validity');
uninstall_property(element, 'valueAsDate');
uninstall_property(element, 'valueAsNumber');
uninstall_property(element, 'willValidate');
uninstall_properties(element);
} else if (element instanceof window.HTMLFormElement) {
uninstall_property(element, 'checkValidity');
uninstall_property(element, 'reportValidity');
}
}
var element_prototypes = [window.HTMLButtonElement.prototype, window.HTMLInputElement.prototype, window.HTMLSelectElement.prototype, window.HTMLTextAreaElement.prototype, window.HTMLFieldSetElement.prototype];
/**
* get the appropriate function to revalidate form elements
*/
function get_revalidator() {
var method = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'hybrid';
return function (event) {
if (event.target instanceof window.HTMLButtonElement || event.target instanceof window.HTMLTextAreaElement || event.target instanceof window.HTMLSelectElement || event.target instanceof window.HTMLInputElement) {
if (event.target.form && event.target.form.hasAttribute('novalidate')) {
/* do nothing, if the form forbids it. This still allows manual
* validation via, e.g., input.reportValidity(), but mirrors browser
* behavior, that are also completely silent in this case. */
return;
}
if (method === 'hybrid') {
/* "hybrid" somewhat simulates what browsers do. See for example
* Firefox's :-moz-ui-invalid pseudo-class:
* https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-ui-invalid */
if (event.type === 'blur' && event.target.value !== event.target.defaultValue || ValidityState(event.target).valid) {
/* on blur, update the report when the value has changed from the
* default or when the element is valid (possibly removing a still
* standing invalidity report). */
reportValidity(event.target);
} else if (event.type === 'keyup' && event.keyCode !== 9 || event.type === 'change') {
if (ValidityState(event.target).valid) {
// report instantly, when an element becomes valid,
// postpone report to blur event, when an element is invalid
reportValidity(event.target);
}
}
} else if (event.type !== 'keyup' || event.keyCode !== 9) {
/* do _not_ validate, when the user "tabbed" into the field initially,
* i.e., a keyup event with keyCode 9 */
reportValidity(event.target);
}
}
};
}
/**
* run a function on all found elements
*/
function execute_on_elements(fn, elements) {
if (elements instanceof window.Element) {
elements = [elements];
}
var elements_length = elements.length;
for (var i = 0; i < elements_length; i++) {
fn(elements[i]);
}
}
/**
* get a function, that removes hyperform behavior again
*/
function get_destructor(hform) {
var form = hform.form;
return function () {
uncatch_submit(form);
form.removeEventListener('keyup', hform.revalidator);
form.removeEventListener('change', hform.revalidator);
form.removeEventListener('blur', hform.revalidator, true);
if (form === window || form.nodeType === 9) {
hform.uninstall(element_prototypes);
polyunfill(window.HTMLFormElement);
} else if (form instanceof window.HTMLFormElement || form instanceof window.HTMLFieldSetElement) {
hform.uninstall(form.elements);
if (form instanceof window.HTMLFormElement) {
polyunfill(form);
}
} else if (form instanceof window.HTMLElement) {
hform.observer.disconnect();
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = Array.prototype.slice.call(form.getElementsByTagName('form'))[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var subform = _step.value;
hform.uninstall(subform.elements);
polyunfill(subform);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
};
}
/**
* add hyperform behavior to a freshly initialized wrapper
*/
function add_behavior(hform) {
var form = hform.form;
var settings = hform.settings;
hform.revalidator = get_revalidator(settings.revalidate);
hform.observer = {
disconnect: function disconnect() {}
};
hform.install = function (elements) {
return execute_on_elements(polyfill, elements);
};
hform.uninstall = function (elements) {
return execute_on_elements(polyunfill, elements);
};
hform._destruct = get_destructor(hform);
catch_submit(form, settings.revalidate === 'never');
if (form === window || form.nodeType === 9) {
/* install on the prototypes, when called for the whole document */
hform.install(element_prototypes);
polyfill(window.HTMLFormElement);
} else if (form instanceof window.HTMLFormElement || form instanceof window.HTMLFieldSetElement) {
hform.install(form.elements);
if (form instanceof window.HTMLFormElement) {
polyfill(form);
}
} else if (form instanceof window.HTMLElement) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = Array.prototype.slice.call(hform.form.getElementsByTagName('form'))[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var subform = _step2.value;
hform.install(subform.elements);
polyfill(subform);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
_iterator2["return"]();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
hform.observer = new window.MutationObserver(function (mutationsList) {
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = mutationsList[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var mutation = _step3.value;
if (mutation.type === 'childList') {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = Array.prototype.slice.call(mutation.addedNodes)[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var subform = _step4.value;
if (subform instanceof window.HTMLFormElement) {
hform.install(subform.elements);
polyfill(subform);
}
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4["return"] != null) {
_iterator4["return"]();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = Array.prototype.slice.call(mutation.removedNodes)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var _subform = _step5.value;
if (_subform instanceof window.HTMLFormElement) {
hform.uninstall(_subform.elements);
polyunfill(_subform);
}
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5["return"] != null) {
_iterator5["return"]();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3["return"] != null) {
_iterator3["return"]();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
});
hform.observer.observe(form, {
subtree: true,
childList: true
});
} else {
throw new Error('Hyperform must be used with a node or window.');
}
if (settings.revalidate === 'oninput' || settings.revalidate === 'hybrid') {
/* in a perfect world we'd just bind to "input", but support here is
* abysmal: http://caniuse.com/#feat=input-event */
form.addEventListener('keyup', hform.revalidator);
form.addEventListener('change', hform.revalidator);
}
if (settings.revalidate === 'onblur' || settings.revalidate === 'hybrid') {
/* useCapture=true, because `blur` doesn't bubble. See
* https://developer.mozilla.org/en-US/docs/Web/Events/blur#Event_delegation
* for a discussion */
form.addEventListener('blur', hform.revalidator, true);
}
}
var version = '0.12.0';
/**
* public hyperform interface:
*/
function hyperform(form) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
classes = _ref.classes,
_ref$debug = _ref.debug,
debug = _ref$debug === void 0 ? false : _ref$debug,
extendFieldset = _ref.extendFieldset,
novalidateOnElements = _ref.novalidateOnElements,
_ref$preventImplicitS = _ref.preventImplicitSubmit,
preventImplicitSubmit = _ref$preventImplicitS === void 0 ? false : _ref$preventImplicitS,
revalidate = _ref.revalidate,
_ref$strict = _ref.strict,
strict = _ref$strict === void 0 ? false : _ref$strict,
validEvent = _ref.validEvent,
_ref$validateNameless = _ref.validateNameless,
validateNameless = _ref$validateNameless === void 0 ? false : _ref$validateNameless;
if (!classes) {
classes = {};
}
if (extendFieldset === undefined) {
extendFieldset = !strict;
}
if (novalidateOnElements === undefined) {
novalidateOnElements = !strict;
}
if (preventImplicitSubmit === undefined) {
preventImplicitSubmit = false;
}
if (revalidate === undefined) {
/* other recognized values: 'oninput', 'onblur', 'onsubmit' and 'never' */
revalidate = strict ? 'onsubmit' : 'hybrid';
}
if (validEvent === undefined) {
validEvent = !strict;
}
var settings = {
debug: debug,
strict: strict,
preventImplicitSubmit: preventImplicitSubmit,
revalidate: revalidate,
validEvent: validEvent,
extendFieldset: extendFieldset,
classes: classes,
novalidateOnElements: novalidateOnElements,
validateNameless: validateNameless
};
if (form instanceof window.NodeList || form instanceof window.HTMLCollection || form instanceof Array) {
return Array.prototype.map.call(form, function (element) {
return hyperform(element, settings);
});
}
var wrapper = new Wrapper(form, settings);
add_behavior(wrapper);
return wrapper;
}
hyperform.version = version;
hyperform.checkValidity = checkValidity;
hyperform.reportValidity = reportValidity;
hyperform.setCustomValidity = setCustomValidity;
hyperform.stepDown = stepDown;
hyperform.stepUp = stepUp;
hyperform.validationMessage = validationMessage;
hyperform.ValidityState = ValidityState;
hyperform.valueAsDate = valueAsDate;
hyperform.valueAsNumber = valueAsNumber;
hyperform.willValidate = willValidate;
hyperform.setLanguage = function (lang) {
set_language(lang);
return hyperform;
};
hyperform.addTranslation = function (lang, catalog) {
add_translation(lang, catalog);
return hyperform;
};
hyperform.setRenderer = function (renderer, action) {
Renderer.set(renderer, action);
return hyperform;
};
hyperform.addValidator = function (element, validator) {
custom_validator_registry.set(element, validator);
return hyperform;
};
hyperform.setMessage = function (element, validator, message) {
custom_messages.set(element, validator, message);
return hyperform;
};
hyperform.addHook = function (hook, action, position) {
add_hook(hook, action, position);
return hyperform;
};
hyperform.removeHook = function (hook, action) {
remove_hook(hook, action);
return hyperform;
};
if (document.currentScript && document.currentScript.hasAttribute('data-hf-autoload')) {
hyperform(window);
}
return hyperform;
}());