polyfill

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

Stan na 24-01-2017. Zobacz najnowsza wersja.

Ten skrypt nie powinien być instalowany bezpośrednio. Jest to biblioteka dla innych skyptów do włączenia dyrektywą meta // @require https://updategf.qytechs.cn/scripts/17895/171353/polyfill.js

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name        polyfill
// @description Microsoft Edge、Firefox、Opera、Google Chrome向けのpolyfillです。
// @version     1.7.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';

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

if (typeof URLSearchParams === 'undefined') {
	// Microsoft Edge
	let privateFields = new WeakMap();
	let 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 (let key in fields) {
					if (typeof fields[key] === 'function') {
						// インスタンスメソッド
						fields[key] = fields[key].bind(object);
					}
				}
			}
			privateFields.set(object, fields);
		}
		return fields;
	};

	/**
	 * @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
	 */
	let 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
	 */
	let 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
	 */
	let 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) {
							let 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 (let 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.`);
				}
				let values = [];
				for (let 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 (let [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 (let [key, value] of pte(this).list) {
					callback.call(thisArg, value, key);
				}
			}

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

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

			/**
			 * @returns {Iterator.<string>}
			 */
			* values()
			{
				for (let [, 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>
	let props = {};
	for (let 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 () {
		let urlObject = pte(this).urlObject;
		if (urlObject) {
			urlObject.search = serializeXWWWFormUrlencodedString(pte(this).list);
		}
	};

	/*globals URL :true*/
	URL = new Proxy(URL, {
		construct(URL, argumentsList) {
			let url = Reflect.construct(URL, argumentsList);
			let 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 () {
		let 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 */
	URLSearchParams = new Proxy(URLSearchParams, {
		construct(URLSearchParams, argumentsList) {
			if (argumentsList.length > 0) {
				if (typeof argumentsList[0] === 'object' && argumentsList[0] !== null) {
					let params = new URLSearchParams();
					if (Symbol.iterator in argumentsList[0]) {
						for (let item of argumentsList[0]) {
							let 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 {
						for (let key of Object.getOwnPropertyNames(argumentsList[0])) {
							params.append(key, argumentsList[0][key]);
						}
					}
					return params;
				}
				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) {
		let url = this.createObjectURL(blob);
		window.setTimeout(() => this.revokeObjectURL(url), MAX_LIFETIME);
		return url;
	};
}

///////////////////////////////////////////////////////////////////////////////
//////// For Microsoft Edge and Firefox 45 ESR                         ////////
///////////////////////////////////////////////////////////////////////////////

if (!('prepend' in document)) {
	/**
	 * 複数のインターフェースに[Unscopable]拡張属性を伴うメンバーを実装します。
	 * @param {Function[]} interfaces
	 * @param {Object.<Function>} members
	 */
	let implementUnscopableMembers = function (interfaces, members) {
		for (let intrfc of interfaces) {
			Object.assign(intrfc.prototype, members);
			if (Symbol.unscopables) {
				let object = {};
				for (let 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)}
	 */
	let 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];
		}
		let fragment = new DocumentFragment();
		for (let 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) {
			let 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) {
			let 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) {
			let parent = this.parentNode;
			if (!parent) {
				return;
			}
			let viableNextSibling;
			while ((viableNextSibling = this.nextSibling) && nodes.includes(viableNextSibling)) {
			}
			let node = convertNodesIntoNode(nodes);
			if (this.parentNode === parent) {
				parent.replaceChild(node, this);
			} else {
				parent.insertBefore(node, viableNextSibling);
			}
		},
	});
}

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

// 開発者ツールのコンソールから、スタックトレースなどを確認できるようにします
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});
}

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 (!(document.getElementsByName('') instanceof NodeList)) {
	Document.prototype.getElementsByName = new Proxy(Document.prototype.getElementsByName, {
		apply(getElementsByName, doc, argumentsList)
		{
			let htmlCollection = Reflect.apply(getElementsByName, doc, argumentsList);
			Object.defineProperty(htmlCollection, Symbol.iterator, {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function * () {
					for (let i = 0, l = this.length; i < l; i++) {
						yield this[i];
					}
				},
			});
			return htmlCollection;
		},
	});
}

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

///////////////////////////////////////////////////////////////////////////////
//////// For Firefox                                                   ////////
///////////////////////////////////////////////////////////////////////////////

// 6.4 Time Zone Names | ECMAScript® 2016 Internationalization API Specification
// http://www.ecma-international.org/ecma-402/3.0/index.html#sec-time-zone-names
// Bug 837961 – Add support for IANA time zone names to internationalization API
// <https://bugzilla.mozilla.org/show_bug.cgi?id=837961>
// メモ:JavaScript で システム時刻から別のタイムゾーンの時刻へ変換 — ねじろぐ @drillbits
// <http://d.hatena.ne.jp/drillbits/20100127/javascript_timezone_system>
try {
	new Intl.DateTimeFormat('ja', {timeZone: 'Asia/Tokyo'});
} catch (exception) {
	/**
	 * 大文字化したタイムゾーン名とタイムゾーンオフセットの組。
	 * @constant {Object.<number>}
	 */
	const TIMEZONE_OFFSETS = {
		'ASIA/TOKYO': -540,
	};

	/**
	 * 分をミリ秒に変換するときの乗数。
	 * @constant {number}
	 */
	const MINUTES_TO_MILLISECONDS = 60 * 1000;

	Date.prototype.toLocaleString = new Proxy(Date.prototype.toLocaleString, {
		apply: function (target, thisArg, argumentsList) {
			if (typeof argumentsList[1] === 'object' && argumentsList[1] !== null) {
				let timeZone = String(argumentsList[1].timeZone).toUpperCase();
				if (TIMEZONE_OFFSETS.hasOwnProperty(timeZone)) {
					let timeZoneOffset = TIMEZONE_OFFSETS[timeZone];
					if (thisArg.getTimezoneOffset() === timeZoneOffset) {
						delete argumentsList[1].timeZone;
					} else {
						thisArg = new Date(thisArg.getTime() - timeZoneOffset * MINUTES_TO_MILLISECONDS);
						argumentsList[1].timeZone = 'UTC';
					}
				}
			}
			return target.apply(thisArg, argumentsList);
		},
	});
}

///////////////////////////////////////////////////////////////////////////////
//////// For Firefox 45 ESR                                            ////////
///////////////////////////////////////////////////////////////////////////////

try {
	new CustomEvent('', {detail: {}});
} catch (exception) {
	/*globals CustomEvent: true, cloneInto: true */
	CustomEvent = new Proxy(CustomEvent, { construct: function (Target, args) {
		if (args.length >= 2
			&& typeof args[1] === 'object' && typeof args[1].detail === 'object' && args[1].detail !== null) {
			args[1].detail = cloneInto(args[1].detail, window, {
				cloneFunctions: true,
				wrapReflectors: true,
			});
		}
		return new Target(...args);
	} });
}

})();