Greasy Fork

polyfill

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

目前为 2016-05-12 提交的版本。查看 最新版本

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

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

(function () {
'use strict';

// Polyfill for Firefox, Opera, and Google Chrome
if (!('prepend' in document)) {
	/**
	 * 複数のインターフェースに[Unscopable]拡張属性を伴うメンバーを実装します。
	 * @param {Function[]} interfaces。
	 * @param {Object.<Function>} members
	 */
	let implementUnscopableMembers = function (interfaces, members) {
		for (let i_f of interfaces) {
			Object.assign(i_f.prototype, members);
			if (Symbol.unscopables) {
				let object = {};
				for (let memberName of Object.keys(members)){
					object[memberName] = true;
				}
				if (i_f.prototype[Symbol.unscopables]) {
					Object.assign(i_f.prototype[Symbol.unscopables], object);
				} else {
					i_f.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.parent === parent) {
				parent.replaceChild(node, this);
			} else {
				parent.insertBefore(node, viableNextSibling);
			}
		},
	});
}

if (new URLSearchParams('?').has('?')) {
	URLSearchParams = new Proxy(URLSearchParams, {
		construct(URLSearchParams, argumentsList) {
			if (argumentsList.length > 0 && !(argumentsList[0] instanceof URLSearchParams)) {
				argumentsList[0] = String(argumentsList[0]).replace(/^\?/, '');
			}
			return new URLSearchParams(...argumentsList);
		},
	});
}

if (!('createFor' in URL)) {
	/**
	 * 分をミリ秒に変換するときの乗数。
	 * @var {number}
	 */
	const MINUTES_TO_MILISECONDS = 60 * 1000;
	
	/**
	 * Blob URL を自動破棄するまでのミリ秒数。
	 * @var {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;
	};
}

// Polyfill for Firefox 38 ESR
if (!''.includes) {
	/** @see [Bug 1102219 – Rename String.prototype.contains to String.prototype.includes]{@link https://bugzilla.mozilla.org/show_bug.cgi?id=1102219} */
	Object.defineProperty(String.prototype, 'includes', {
		writable: true,
		enumerable: false,
		configurable: true,
		value: String.prototype.contains,
	});
}

if (!Array.prototype.includes) {
	/**
	 * The includes() method determines whether an array includes a certain element, returning true or false as appropriate.
	 * @see [Array.prototype.includes() - JavaScript | MDN]{@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/includes}
	 * @see [Bug 1070767 – Enable {Array, %TypedArray%}.prototype.includes in all builds]{@link https://bugzilla.mozilla.org/show_bug.cgi?id=1070767}
	 * @function Array#includes
	 * @license CC0-1.0
	 */
	Object.defineProperty(Array.prototype, 'includes', {
		writable: true,
		enumerable: false,
		configurable: true,
		value: function (searchElement /*, fromIndex*/ ) {
			'use strict';
			var O = Object(this);
			var len = parseInt(O.length) || 0;
			if (len === 0) {
				return false;
			}
			var n = parseInt(arguments[1]) || 0;
			var k;
			if (n >= 0) {
				k = n;
			} else {
				k = len + n;
				if (k < 0) {k = 0;}
			}
			var currentElement;
			while (k < len) {
				currentElement = O[k];
				if (searchElement === currentElement ||
					 (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN
					return true;
				}
				k++;
			}
			return false;
		}
	});
}

// Polyfill for Opera and Google Chrome
if (!(Symbol.iterator in NodeList.prototype)) {
	Object.defineProperties(NodeList.prototype, /** @lends NodeList# */ {
		/**
		 * @see [Issue 401699 - chromium - Add iterator support to NodeList and friends - Monorail]{@link https://bugs.chromium.org/p/chromium/issues/detail?id=401699}
		 * @returns {Iterator.<Array.<number, Node>>}
		 * @name NodeList#@@iterator
		 */
		[Symbol.iterator]: {
			writable: true,
			enumerable: false,
			configurable: true,
			value: function* () {
				for (var i = 0, l = this.length; i < l; i++) {
					yield this[i];
				}
			}
		},
		/**
		 * @param {Function} callback
		 * @param {*} thisArg
		 * @function
		 */
		forEach: {
			writable: true,
			enumerable: true,
			configurable: true,
			value: Array.prototype.forEach
		},
		/**
		 * @returns {Iterator.<Array.<number, Node>>}
		 * @function
		 */
		entries: {
			writable: true,
			enumerable: true,
			configurable: true,
			value: function* () {
				for (var i = 0, l = this.length; i < l; i++) {
					yield [i, this[i]];
				}
			}
		},
		/**
		 * @returns {Iterator.<number>}
		 * @function
		 */
		keys: {
			writable: true,
			enumerable: true,
			configurable: true,
			value: function* () {
				for (var i = 0, l = this.length; i < l; i++) {
					yield i;
				}
			}
		},
		/**
		 * @returns {Iterator.<Node>}
		 * @function
		 */
		values: {
			writable: true,
			enumerable: true,
			configurable: true,
			value: function* () {
				for (var i = 0, l = this.length; i < l; i++) {
					yield this[i];
				}
			}
		},
	});
}

})();