// ==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];
}
}
},
});
}
})();