您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Microsoft Edge、Firefox、Opera、Google Chrome向けのpolyfillです。
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @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 http://greasyfork.icu/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); }, }); } })();