// ==UserScript==
// @name polyfill
// @description Firefox、Opera、Google Chrome向けのpolyfillです。
// @version 1.2.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)) {
/**
* @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;
};
/** @see [DOM Standard]{@link https://dom.spec.whatwg.org/#interface-parentnode} */
let ParentNode = {
/**
* 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: function (...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: function (...nodes) {
this.appendChild(convertNodesIntoNode(nodes));
},
};
for (let i_f of [Document, DocumentFragment, Element]) {
Object.assign(i_f.prototype, ParentNode)
}
/** @see [DOM Standard]{@link https://dom.spec.whatwg.org/#interface-childnode} */
let ChildNode = {
/**
* 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: function (...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: function (...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: function (...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);
}
},
};
for (let i_f of [DocumentType, Element, CharacterData]) {
Object.assign(i_f.prototype, ChildNode)
}
}
if (new URLSearchParams('?').has('?')) {
URLSearchParams = new Proxy(URLSearchParams, {
construct: function (URLSearchParams, argumentsList) {
if (argumentsList.length > 0 && !(argumentsList[0] instanceof URLSearchParams)) {
argumentsList[0] = String(argumentsList[0]).replace(/^\?/, '');
}
return new URLSearchParams(...argumentsList);
},
});
}
// 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]{@link https://code.google.com/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];
}
}
},
});
}
})();