Greasy Fork

polyfill

Microsoft Edge、Firefox、Opera、Google Chrome向けのpolyfillです。

目前为 2018-09-01 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.icu/scripts/17895/625392/polyfill.js

// ==UserScript==
// @name        polyfill
// @description Microsoft Edge、Firefox、Opera、Google Chrome向けのpolyfillです。
// @version     1.10.0
// @license     Mozilla Public License Version 2.0 (MPL 2.0); https://www.mozilla.org/MPL/2.0/
// @compatible  Edge
// @compatible  Firefox
// @compatible  Opera
// @compatible  Chrome
// @author      100の人
// @homepage    https://greasyfork.org/scripts/17895
// ==/UserScript==

(function () {
'use strict';

const privateFields = new WeakMap();
const pte = function (object) {
	let fields = {};
	if (privateFields.has(object)) {
		// クラス、またはインスタンス
		fields = privateFields.get(object);
	} else {
		if (privateFields.has(object.constructor.prototype)) {
			// インスタンス
			Object.assign(fields, privateFields.get(object.constructor.prototype));
			for (const key in fields) {
				if (typeof fields[key] === 'function') {
					// インスタンスメソッド
					fields[key] = fields[key].bind(object);
				}
			}
		}
		privateFields.set(object, fields);
	}
	return fields;
};

///////////////////////////////////////////////////////////////////////////////
//////// For Microsoft Edge, Firefox, Opera, and Google Chrome         ////////
///////////////////////////////////////////////////////////////////////////////

if (typeof URLSearchParams === 'undefined') {
	// Microsoft Edge
	/**
	 * @param {string} input
	 * @returns {(string[])[]} List of name-value pairs where both name and value hold a string.
	 * @see [application/x-www-form-urlencoded – URL Standard]{@link https://url.spec.whatwg.org/#concept-urlencoded-string-parser}
	 * @memberof URL
	 * @function
	 * @access private
	 */
	const parseXWWWFormUrlencoded = input => input.split('&').filter(bytes => bytes !== '').map(
		bytes => bytes.replace(/\+/g, ' ').split(/([^=]*)=?(.*)/).slice(1, 3).map(decodeURIComponent)
	);

	/**
	 * @param {string} input - A byte sequence.
	 * @returns {string}
	 * @see [application/x-www-form-urlencoded – URL Standard]{@link https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer}
	 * @memberof URL
	 * @function
	 * @access private
	 */
	const serializeXWWWFormUrlencodedByte
		= input => encodeURIComponent(input).replace(/%20/g, '+').replace(/[!~'()]+/g, escape);

	/**
	 * @param {(string[])[]} pairs - List of name-value pairs pairs.
	 * @returns {string}
	 * @see [application/x-www-form-urlencoded – URL Standard]{@link https://url.spec.whatwg.org/#concept-urlencoded-serializer}
	 * @memberof URL
	 * @function
	 * @access private
	 */
	const serializeXWWWFormUrlencodedString = function (pairs) {
		return pairs
			.map(pair => serializeXWWWFormUrlencodedByte(pair[0]) + '=' + serializeXWWWFormUrlencodedByte(pair[1]))
			.join('&');
	};

	Object.defineProperty(window, 'URLSearchParams', {
		writable: true,
		enumerable: false,
		configurable: true,

		/**
		 * A URLSearchParams object has an associated list of name-value pairs, which is initially empty.
		 * @see [URLSearchParams Interface – URL Standard]{@link https://url.spec.whatwg.org/#interface-urlsearchparams}
		 */
		value: class {
			/**
			 * @param {((string[])[]|Object.<string>|string)} [init]
			 */
			constructor(init = '')
			{
				/**
				 * A URLSearchParams object has an associated list of name-value pairs, which is initially empty.
				 * @member {(string[])[]}
				 * @access private
				 */
				pte(this).list = typeof init === 'object' && init !== null
					? (Symbol.iterator in init
						? Array.from(init).map(function (item) {
							const pair = Array.from(item);
							if (pair.length !== 2) {
								throw new TypeError(
									'URLSearchParams require name/value tuples when being initialized by a sequence.'
								);
							}
							return [String(pair[0]), String(pair[1])];
						})
						: Object.getOwnPropertyNames(init).map(key => [String(key), String(init[key])]))
					: parseXWWWFormUrlencoded(String(init).replace(/^\?/, ''));

				/**
				 * A URLSearchParams object has an associated url object, which is initially null.
				 * @member {?(URL|HTMLAnchorElement|HTMLAreaElement)}
				 * @access private
				 */
				pte(this).urlObject = null;
			}

			/**
			 * Append a new name-value pair whose name is name and value is value, to the list of name-value pairs.
			 * @param {string} name
			 * @param {string} value
			 */
			append(name, value)
			{
				if (arguments.length < 2) {
					throw new TypeError(`Failed to execute 'append' on 'URLSearchParams': 2 argument required, but only ${arguments.length} present.`);
				}
				pte(this).list.push([String(name), String(value)]);
				pte(this).update();
			}

			/**
			 * Remove all name-value pairs whose name is name.
			 * @param {string} name
			 */
			delete(name)
			{
				if (arguments.length < 1) {
					throw new TypeError(`Failed to execute 'delete' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`);
				}
				for (let i = 0, l = pte(this).list.length; i < l; i++) {
					if (pte(this).list[i][0] === name) {
						pte(this).list.splice(i, 1);
						i--;
						l--;
					}
				}
				pte(this).update();
			}

			/**
			 * Return the value of the first name-value pair whose name is name, and null if there is no such pair.
			 * @param {string} name
			 * @returns {?string}
			 */
			get(name)
			{
				if (arguments.length < 1) {
					throw new TypeError(`Failed to execute 'get' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`);
				}
				for (const pair of pte(this).list) {
					if (pair[0] === name) {
						return pair[1];
					}
				}
				return null;
			}

			/**
			 * Return the values of all name-value pairs whose name is name, in list order,
			 * and the empty sequence otherwise.
			 * @param {string} name
			 * @returns {string[]}
			 */
			getAll(name)
			{
				if (arguments.length < 1) {
					throw new TypeError(`Failed to execute 'getAll' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`);
				}
				const values = [];
				for (const pair of pte(this).list) {
					if (pair[0] === name) {
						values.push(pair[1]);
					}
				}
				return values;
			}

			/**
			 * If there are any name-value pairs whose name is name,
			 * set the value of the first such name-value pair to value and remove the others.
			 * Otherwise,
			 * append a new name-value pair whose name is name and value is value, to the list of name-value pairs.
			 * @param {string} name
			 * @param {string} value
			 */
			set(name, value)
			{
				if (arguments.length < 2) {
					throw new TypeError(`Failed to execute 'set' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`);
				}
				let flag;
				for (let i = 0, l = pte(this).list.length; i < l; i++) {
					if (pte(this).list[i][0] === name) {
						if (flag) {
							pte(this).list.splice(i, 1);
							i--;
							l--;
						} else {
							pte(this).list[i][1] = String(value);
							flag = true;
						}
					}
				}
				if (!flag) {
					pte(this).list.push([String(name), String(value)]);
				}
				pte(this).update();
			}

			/**
			 * Return true if there is a name-value pair whose name is name, and false otherwise.
			 * @param {string} name
			 * @returns {boolean}
			 */
			has(name)
			{
				if (arguments.length < 1) {
					throw new TypeError(`Failed to execute 'name' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`);
				}
				return pte(this).list.some(pair => pair[0] === name);
			}

			/**
			 * Return the serialization of the URLSearchParams object's associated list of name-value pairs.
			 * @returns {string}
			 */
			toString()
			{
				return serializeXWWWFormUrlencodedString(pte(this).list);
			}

			/**
			 * The value pairs to iterate over
			 *     are the list name-value pairs with the key being the name and the value the value.
			 * @returns {Iterator.<string[]>}
			 */
			* [Symbol.iterator]()
			{
				for (const [key, value] of pte(this).list) {
					yield [key, value];
				}
			}

			/**
			 * @param {Function} callback
			 * @param {*} thisArg
			 */
			forEach(callback, thisArg = null)
			{
				if (typeof callback !== 'function') {
					throw new TypeError(`${callback} is not a function`);
				}
				for (const [key, value] of pte(this).list) {
					callback.call(thisArg, value, key);
				}
			}

			/**
			 * @returns {Iterator.<string[]>}
			 */
			* entries()
			{
				for (const [key, value] of pte(this).list) {
					yield [key, value];
				}
			}

			/**
			 * @returns {Iterator.<string>}
			 */
			* keys()
			{
				for (const [key] of pte(this).list) {
					yield key;
				}
			}

			/**
			 * @returns {Iterator.<string>}
			 */
			* values()
			{
				for (const [, value] of pte(this).list) {
					yield value;
				}
			}
		},
	});

	// Symbol.iterator 以外の各メソッドを列挙可能に
	// 4.6.7. Operations <https://www.w3.org/TR/WebIDL-1/#es-operations>
	// 4.6.8. Common iterator behavior <https://www.w3.org/TR/WebIDL-1/#es-iterators>
	const props = {};
	for (const methodName of Object.getOwnPropertyNames(URLSearchParams.prototype)) {
		props[methodName] = { enumerable: true };
	}
	Object.defineProperties(URLSearchParams.prototype, props);

	/**
	 * @see [URLSearchParams Interface – URL Standard]{@link https://url.spec.whatwg.org/#concept-urlsearchparams-update}
	 * @access private
	 * @memberof URLSearchParams#
	 */
	pte(URLSearchParams.prototype).update = function () {
		const urlObject = pte(this).urlObject;
		if (urlObject) {
			urlObject.search = serializeXWWWFormUrlencodedString(pte(this).list);
		}
	};

	/*globals URL :true*/
	URL = new Proxy(URL, {
		construct(URL, argumentsList) {
			const url = Reflect.construct(URL, argumentsList);
			const queryObject = new URLSearchParams(url.search);
			pte(queryObject).urlObject = url;
			/**
			 * A URL object has a query object (a URLSearchParams object).
			 * @member {URLSearchParams}
			 * @access private
			 */
			pte(url).queryObject = queryObject;
			return url;
		},
	});

	/**
	 * @see [The search attribute – URL members – URL Standard]{@link https://url.spec.whatwg.org/#dom-url-search}
	 * @access private
	 * @memberof URL#
	 */
	pte(URL.prototype).updateQueryObject = function () {
		const list = pte(pte(this).queryObject).list;
		list.splice(0, list.length, ...parseXWWWFormUrlencoded(this.search.replace('?', '')));
	};

	Object.defineProperties(URL.prototype, {
		href: {
			set: new Proxy(Object.getOwnPropertyDescriptor(URL.prototype, 'href').set, {
				apply(setter, url, argumentsList) {
					Reflect.apply(setter, url, argumentsList);
					pte(url).updateQueryObject();
				},
			}),
		},
		search: {
			set: new Proxy(Object.getOwnPropertyDescriptor(URL.prototype, 'search').set, {
				apply(setter, url, argumentsList) {
					Reflect.apply(setter, url, argumentsList);
					pte(url).updateQueryObject();
				},
			}),
		},
		/**
		 * @member {URLSearchParams} URL#searchParams
		 * @readonly
		 */
		searchParams: {
			enumerable: true,
			configurable: true,
			get() {
				return pte(this).queryObject;
			},
		},
	});
} else if (!new URLSearchParams({key: ''}).has('key')) {
	// Firefox, Opera, and Google Chrome
	/*globals URLSearchParams: true */
	const notStrippedLeadingQuestionMark = new URLSearchParams('?').has('?');
	const notSupportedSequence = !new URLSearchParams([['key', '']]).has('key');
	URLSearchParams = new Proxy(URLSearchParams, {
		construct(URLSearchParams, argumentsList) {
			if (argumentsList.length > 0) {
				if (typeof argumentsList[0] === 'object' && argumentsList[0] !== null) {
					let params;
					if (Symbol.iterator in argumentsList[0]) {
						if (notSupportedSequence) {
							// Firefox 45 ESR, Firefox 52 ESR, Opera, and Google Chrome
							params = new URLSearchParams();
							for (const item of argumentsList[0]) {
								const pair = Array.from(item);
								if (pair.length !== 2) {
									throw new TypeError('URLSearchParams require name/value tuples when being initialized by a sequence.');
								}
								params.append(pair[0], pair[1]);
							}
						}
					} else {
						params = new URLSearchParams();
						for (const key of Object.getOwnPropertyNames(argumentsList[0])) {
							params.append(key, argumentsList[0][key]);
						}
					}
					if (params) {
						return params;
					}
				}

				if (notStrippedLeadingQuestionMark) {
					// Firefox 45 ESR
					argumentsList[0] = String(argumentsList[0]).replace(/^\?/, '');
				}
			}
			return Reflect.construct(URLSearchParams, argumentsList);
		},
	});
}

if (!('createFor' in URL)) {
	/**
	 * 分をミリ秒に変換するときの乗数。
	 * @constant {number}
	 */
	const MINUTES_TO_MILISECONDS = 60 * 1000;

	/**
	 * Blob URL を自動破棄するまでのミリ秒数。
	 * @constant {number}
	 */
	const MAX_LIFETIME = 10 * MINUTES_TO_MILISECONDS;

	/**
	 * Blob URLを生成し、{@link MAX_LIFETIME}ミリ秒後に破棄します。
	 * @see [File API]{@link https://www.w3.org/TR/FileAPI/#dfn-createFor}
	 * @see [Bug 1062917 - Implement URL.createFor]{@link https://bugzilla.mozilla.org/show_bug.cgi?id=1062917}
	 * @see [Issue 608460 - chromium - Consider implementing URL.createFor() - Monorail]{@link https://bugs.chromium.org/p/chromium/issues/detail?id=608460}
	 * @param {Blob} blob
	 * @returns {string} Blob URL。
	 */
	URL.createFor = function (blob) {
		const url = this.createObjectURL(blob);
		window.setTimeout(() => this.revokeObjectURL(url), MAX_LIFETIME);
		return url;
	};
}

///////////////////////////////////////////////////////////////////////////////
//////// For Microsoft Edge                                            ////////
///////////////////////////////////////////////////////////////////////////////

if (!('prepend' in document)) {
	/**
	 * 複数のインターフェースに[Unscopable]拡張属性を伴うメンバーを実装します。
	 * @param {Function[]} interfaces
	 * @param {Object.<Function>} members
	 */
	const implementUnscopableMembers = function (interfaces, members) {
		for (const intrfc of interfaces) {
			Object.assign(intrfc.prototype, members);
			if (Symbol.unscopables) {
				const object = {};
				for (const memberName of Object.keys(members)) {
					object[memberName] = true;
				}
				if (intrfc.prototype[Symbol.unscopables]) {
					Object.assign(intrfc.prototype[Symbol.unscopables], object);
				} else {
					intrfc.prototype[Symbol.unscopables] = object;
				}
			}
		}
	};

	/**
	 * @see [DOM Standard]{@link https://dom.spec.whatwg.org/#converting-nodes-into-a-node}
	 * @param {(Node|string)[]} nodes
	 * @returns {(Node|DocumentFragment)}
	 */
	const convertNodesIntoNode = function (nodes) {
		for (let i = 0, l = nodes.length; i < l; i++) {
			if (!(nodes[i] instanceof Node)) {
				nodes[i] = new Text(nodes[i]);
			}
		}
		if (nodes.length === 1) {
			return nodes[0];
		}
		const fragment = new DocumentFragment();
		for (const node of nodes) {
			fragment.appendChild(node);
		}
		return fragment;
	};

	// https://dom.spec.whatwg.org/#interface-parentnode
	implementUnscopableMembers([Document, DocumentFragment, Element], {
		/**
		 * Inserts nodes before the first child of node, while replacing strings in nodes with equivalent Text nodes.
		 * @see [DOM Standard]{@link https://dom.spec.whatwg.org/#dom-parentnode-prepend}
		 * @param {...(Node|string)} nodes
		 * @throws {DOMException} Throws a HierarchyRequestError if the constraints of the node tree are violated.
		 */
		prepend(...nodes) {
			this.insertBefore(convertNodesIntoNode(nodes), this.firstChild);
		},

		/**
		 * Inserts nodes after the last child of node, while replacing strings in nodes with equivalent Text nodes.
		 * @see [DOM Standard]{@link https://dom.spec.whatwg.org/#dom-parentnode-append}
		 * @param {...(Node|string)} nodes
		 * @throws {DOMException} Throws a HierarchyRequestError if the constraints of the node tree are violated.
		 */
		append(...nodes) {
			this.appendChild(convertNodesIntoNode(nodes));
		},
	});

	// https://dom.spec.whatwg.org/#interface-childnode
	implementUnscopableMembers([DocumentType, Element, CharacterData], {
		/**
		 * Inserts nodes just before node, while replacing strings in nodes with equivalent Text nodes.
		 * @see [DOM Standard]{@link https://dom.spec.whatwg.org/#dom-childnode-before}
		 * @param {...(Node|string)} nodes
		 * @throws {DOMException} Throws a HierarchyRequestError if the constraints of the node tree are violated.
		 */
		before(...nodes) {
			const parent = this.parentNode;
			if (!parent) {
				return;
			}
			let viablePreviousSibling;
			while ((viablePreviousSibling = this.previousSibling) && nodes.includes(viablePreviousSibling)) {
			}
			parent.insertBefore(
				convertNodesIntoNode(nodes),
				viablePreviousSibling ? viablePreviousSibling.nextSibling : parent.firstChild
			);
		},

		/**
		 * Inserts nodes just after node, while replacing strings in nodes with equivalent Text nodes.
		 * @see [DOM Standard]{@link https://dom.spec.whatwg.org/#dom-childnode-after}
		 * @param {...(Node|string)} nodes
		 * @throws {DOMException} Throws a HierarchyRequestError if the constraints of the node tree are violated.
		 */
		after(...nodes) {
			const parent = this.parentNode;
			if (!parent) {
				return;
			}
			let viableNextSibling;
			while ((viableNextSibling = this.nextSibling) && nodes.includes(viableNextSibling)) {
			}
			parent.insertBefore(convertNodesIntoNode(nodes), viableNextSibling);
		},

		/**
		 * Replaces node with nodes, while replacing strings in nodes with equivalent Text nodes.
		 * @see [DOM Standard]{@link https://dom.spec.whatwg.org/#dom-childnode-replacewith}
		 * @param {...(Node|string)} nodes
		 * @throws {DOMException} Throws a HierarchyRequestError if the constraints of the node tree are violated.
		 */
		replaceWith(...nodes) {
			const parent = this.parentNode;
			if (!parent) {
				return;
			}
			let viableNextSibling;
			while ((viableNextSibling = this.nextSibling) && nodes.includes(viableNextSibling)) {
			}
			const node = convertNodesIntoNode(nodes);
			if (this.parentNode === parent) {
				parent.replaceChild(node, this);
			} else {
				parent.insertBefore(node, viableNextSibling);
			}
		},
	});
}

// 開発者ツールのコンソールから、スタックトレースなどを確認できるようにします
if (typeof chrome !== 'undefined' && !('runtime' in chrome)) {
	window.addEventListener('error', function (event) {
		if (event.error) {
			console.debug(event.error);
		}
	});
}

if (!(Symbol.iterator in NodeList.prototype)) {
	Object.assign(NodeList.prototype, {
		/**
		 * @see [Issue #5998615 NodeList should be iterable — Microsoft Edge Development]{@link https://developer.microsoft.com/microsoft-edge/platform/issues/5998615/}
		 * @returns {Iterator.<(number|Node)[]>}
		 */
		*[Symbol.iterator]()
		{
			for (let i = 0, l = this.length; i < l; i++) {
				yield this[i];
			}
		},
		/**
		 * @param {Function} callback
		 * @param {*} thisArg
		 * @function
		 */
		forEach: Array.prototype.forEach,
		/**
		 * @returns {Iterator.<(number|Node)[]>}
		 * @function
		 */
		* entries()
		{
			for (let i = 0, l = this.length; i < l; i++) {
				yield [i, this[i]];
			}
		},
		/**
		 * @returns {Iterator.<number>}
		 * @function
		 */
		* keys()
		{
			for (let i = 0, l = this.length; i < l; i++) {
				yield i;
			}
		},
		/**
		 * @returns {Iterator.<Node>}
		 * @function
		 */
		* values()
		{
			for (let i = 0, l = this.length; i < l; i++) {
				yield this[i];
			}
		},
	});
	Object.defineProperty(NodeList.prototype, Symbol.iterator, {enumerable: false});
}

for (const intrfc of [HTMLCollection, HTMLFormElement]) {
	if (!(Symbol.iterator in intrfc.prototype)) {
		/**
		 * @see [Issue #5998615 NodeList should be iterable — Microsoft Edge Development]{@link https://developer.microsoft.com/microsoft-edge/platform/issues/5998615/}
		 */
		Object.defineProperty(intrfc.prototype, Symbol.iterator, {
			writable: true,
			enumerable: false,
			configurable: true,
			value: function * () {
				for (let i = 0, l = this.length; i < l; i++) {
					yield this[i];
				}
			},
		});
	}
}

try {
	new Text();
} catch (exception) {
	/*globals Text: true */
	Text = new Proxy(Text, {
		construct(Text, argumentsList)
		{
			return document.createTextNode(0 in argumentsList ? argumentsList[0] : '');
		},
	});
}

try {
	new Range();
} catch (exception) {
	/*globals Range: true */
	Range = new Proxy(Range, {
		construct(Range, argumentsList)
		{
			return document.createRange();
		},
	});
}

try {
	new DocumentFragment();
} catch (exception) {
	/*globals DocumentFragment: true */
	/**
	 * @see [Issue #9628204 Unable to call DocumentFragment as a constructor — Microsoft Edge Development]{@link https://developer.microsoft.com/microsoft-edge/platform/issues/9628204/}
	 */
	DocumentFragment = new Proxy(DocumentFragment, {
		construct(DocumentFragment, argumentsList)
		{
			return document.createDocumentFragment();
		},
	});
}

if (!('firstElementChild' in new DocumentFragment())) {
	/**
	 * @see [Issue #10060579 Document Fragment does not support children property — Microsoft Edge Development]{@link https://developer.microsoft.com/microsoft-edge/platform/issues/10060579/}
	 */
	Object.defineProperties(DocumentFragment.prototype, {
		firstElementChild: {
			get()
			{
				return Array.from(this.childNodes).find(node => node.nodeType === Node.ELEMENT_NODE);
			},
			enumerable: true,
			configurable: true,
		},
		lastElementChild: {
			get()
			{
				return Array.from(this.childNodes).reverse().find(node => node.nodeType === Node.ELEMENT_NODE);
			},
			enumerable: true,
			configurable: true,
		},
	});
}

/* eslint-disable */
/**
 * @see [Issue #10320716 Edge Browser missing function Element.closest(selector) — Microsoft Edge Development]{https://developer.microsoft.com/microsoft-edge/platform/issues/10320716/}
 * @see [Polyfill — Element.closest() — Web API インターフェイス | MDN]{@link https://developer.mozilla.org/docs/Web/API/Element/closest#Specification}
 * @license CC0-1.0
 */
if (window.Element && !Element.prototype.closest) {
    Element.prototype.closest =
    function(s) {
        var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i,
            el = this;
        do {
            i = matches.length;
            while (--i >= 0 && matches.item(i) !== el) {};
        } while ((i < 0) && (el = el.parentElement));
        return el;
    };
}
/* eslint-enable */

if (!('matches' in document.documentElement)) {
	Element.prototype.matches = Element.prototype.webkitMatchesSelector;
}

try {
	new File([''], '');
} catch (exception) {
	/*globals File: true */
	File = new Proxy(File, {
		construct: function (File, argumentsList) {
			if (argumentsList.length < 2) {
				throw new TypeError('Argument not optional');
			}
			const file = new Blob(argumentsList[0], argumentsList[2] || {});
			Object.defineProperty(file, 'name', {get: function () {
				return String(argumentsList[1]);
			}});
			if (argumentsList[2] && 'lastModified' in argumentsList[2]) {
				Object.defineProperty(file, 'lastModified', {get: function () {
					return Number.parseInt(argumentsList[2]);
				}});
			}
			Object.setPrototypeOf(file, File.prototype);
			return file;
		},
	});
}

if (!(Symbol.iterator in new FormData())) {
	Object.assign(FormData.prototype, {
		append: new Proxy(FormData.prototype.append, {
			apply: function (append, formData, argumentsList) {
				Reflect.apply(append, formData, argumentsList);
				if (!pte(formData).entries) {
					pte(formData).entries = [];
				}
				pte(formData).entries.push([
					String(argumentsList[0]),
					argumentsList[1] instanceof Blob
						? (!(argumentsList[1] instanceof File) || 2 in argumentsList
							? new File([argumentsList[1]], 2 in argumentsList ? argumentsList[2] : 'blob')
							: argumentsList[1])
						: String(argumentsList[1]),
				]);
			},
		}),

		has(name)
		{
			return (pte(this).entries || []).some(entry => entry[0] === name);
		},

		get(name)
		{
			const entry = (pte(this).entries || []).find(function (entry) {
				return entry[0] === name;
			});
			return entry ? entry[1] : null;
		},

		* [Symbol.iterator]()
		{
			for (const [name, value] of pte(this).entries || []) {
				yield [name, value];
			}
		},

		forEach(callback, thisArg = null)
		{
			if (typeof callback !== 'function') {
				throw new TypeError(`${callback} is not a function`);
			}
			for (const [name, value] of pte(this).entries || []) {
				callback.call(thisArg, value, name);
			}
		},

		* entries()
		{
			for (const [name, value] of pte(this).entries || []) {
				yield [name, value];
			}
		},

		* keys()
		{
			for (const [name] of pte(this).entries || []) {
				yield name;
			}
		},

		* values()
		{
			for (const [, value] of pte(this).entries || []) {
				yield value;
			}
		},
	});
	Object.defineProperty(FormData.prototype, Symbol.iterator, {enumerable: false});
}

try {
	new File([''], '');
} catch (exception) {
	/*globals File: true */
	File = new Proxy(File, {
		construct: function (File, argumentsList) {
			if (argumentsList.length < 2) {
				throw new TypeError('Argument not optional');
			}
			const file = new Blob(argumentsList[0], argumentsList[2] || {});
			Object.defineProperty(file, 'name', {get: function () {
				return String(argumentsList[1]);
			}});
			if (argumentsList[2] && 'lastModified' in argumentsList[2]) {
				Object.defineProperty(file, 'lastModified', {get: function () {
					return Number.parseInt(argumentsList[2]);
				}});
			}
			Object.setPrototypeOf(file, File.prototype);
			return file;
		},
	});
}

if (DOMImplementation.prototype.createDocument.length !== 2) {
	DOMImplementation.prototype.createDocument = new Proxy(DOMImplementation.prototype.createDocument, {
		apply: function (createDocument, thisArg, argumentsList) {
			if (argumentsList.length === 2) {
				argumentsList[2] = null;
			}
			return Reflect.apply(createDocument, thisArg, argumentsList);
		},
	});
}

})();