Created
October 30, 2015 16:01
-
-
Save ShirtlessKirk/c02a458b0e19a3976b9d to your computer and use it in GitHub Desktop.
DOM4 methods polyfill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Polyfill of DOM4 methods | |
*/ | |
/*global define: false, module: false, console: false */ | |
/*jslint bitwise: true, forin: true */ | |
(function documentModule(global, definition) { // non-exporting module magic dance | |
'use strict'; | |
var | |
amd = 'amd', | |
exports = 'exports'; // keeps the method names for CommonJS / AMD from being compiled to single character variable | |
if (typeof define === 'function' && define[amd]) { | |
define(function definer() { | |
return definition(global); | |
}); | |
} else if (typeof module === 'function' && module[exports]) { | |
module[exports] = definition(global); | |
} else { | |
definition(global); | |
} | |
}(this, function documentPolyfill(global) { | |
'use strict'; | |
if (!global.Element) { | |
return; | |
} | |
var | |
console = global.console || {}, | |
document, | |
documentHead, | |
elementPrototype, | |
elementPrototypeFunctions, | |
forEach = Array.prototype.forEach || function forEach(fn, context) { | |
var | |
counter = 0, | |
object = ({}).valueOf.call(this), | |
length = object.length >>> 0; | |
while (counter < length) { | |
if (counter in object) { | |
fn.call(context, object[counter], counter, object); | |
} | |
counter += 1; | |
} | |
}, | |
hasOwnProperty = Object.prototype.hasOwnProperty, | |
indexOf = Array.prototype.indexOf || function indexOf(search) { | |
var | |
cursor = 0, | |
object = ({}).valueOf.call(this), | |
length = object.length >>> 0; | |
while (cursor < length) { | |
if (cursor in object && object[cursor] === search) { | |
return cursor; | |
} | |
cursor += 1; | |
} | |
return -1; | |
}, | |
keys = Object.keys || function keys(object) { | |
var | |
array = [], | |
key; | |
for (key in object) { | |
if (hasOwnProperty.call(object, key)) { | |
array.push(key); | |
} | |
} | |
return array; | |
}, | |
unique = 0; | |
/** | |
* @private | |
*/ | |
function castToNode(node) { | |
return typeof node === 'string' ? document.createTextNode(node) : node; | |
} | |
/** | |
* @private | |
*/ | |
function createDocumentFragment(nodes) { | |
var | |
fragment; | |
if (nodes.length === 1) { | |
return castToNode(nodes[0]); | |
} | |
fragment = document.createDocumentFragment(); | |
forEach.call(Array.prototype.slice.call(nodes), function appender(value) { | |
fragment.appendChild(castToNode(value)); | |
}); | |
return fragment; | |
} | |
/** | |
* @constructor | |
*/ | |
function Elements() { | |
return this; | |
} | |
/** | |
* @private | |
*/ | |
function attributeName() { | |
return 'dom4-element-id-' + String(Math.random()).replace(/\D/g, ''); | |
} | |
/** | |
* @private | |
*/ | |
function scope(selector, supportsScope, details) { | |
var | |
selectors = selector.split(/\s*,\s*/), | |
scopedSelectors = []; | |
forEach.call(selectors, function scopePusher(value) { | |
scopedSelectors.push(supportsScope ? ':scoped ' + value : '[' + details.attribute + '="' + details.value + '"] ' + value); | |
}); | |
return scopedSelectors.join(); | |
} | |
function after() { | |
var | |
documentFragment, | |
nextSibling, | |
parentNode = this.parentNode; | |
if (parentNode) { | |
documentFragment = createDocumentFragment(arguments); | |
nextSibling = this.nextSibling; | |
if (nextSibling) { | |
parentNode.insertBefore(documentFragment, nextSibling); | |
} else { | |
parentNode.appendChild(documentFragment); | |
} | |
} | |
} | |
function append() { | |
this.appendChild(createDocumentFragment(arguments)); | |
} | |
function before() { | |
if (this.parentNode) { | |
this.parentNode.insertBefore(createDocumentFragment(arguments), this); | |
} | |
} | |
function closest(selector) { | |
var | |
matches = this, | |
parentNode = matches; | |
while ((matches && parentNode.matches) && !parentNode.matches(selector)) { | |
matches = parentNode; | |
parentNode = parentNode.parentNode; | |
} | |
return matches ? parentNode : null; | |
} | |
function getElementsByClassName(className) { | |
return this.querySelectorAll((' ' + className).replace(/ +/g, '.')); // faster than splitting on ' ' and joining with '.' | |
} | |
function matches(selector) { | |
var | |
parentNode = this.parentNode; | |
return !!parentNode && indexOf.call(parentNode.querySelectorAll(selector), this) !== -1; | |
} | |
function prepend() { | |
var | |
documentFragment = createDocumentFragment(arguments), | |
firstChild = this.firstChild; | |
if (firstChild) { | |
this.insertBefore(documentFragment, firstChild); | |
} else { | |
this.appendChild(documentFragment); | |
} | |
} | |
function queryAll(selector) { | |
var | |
attribute, | |
element = this === document ? document.documentElement : this, | |
elements, | |
result, | |
scopeSelector, | |
supportsScope = true; | |
function toArray(list) { | |
var | |
index = 0, | |
length = list.length, | |
array = new Array(length); | |
while (index < length) { | |
array[index] = list[index]; | |
index += 1; | |
} | |
return array; | |
} | |
try { | |
document.createElement('i').querySelector(':scoped *'); | |
} catch (ex) { | |
supportsScope = false; | |
} | |
if (!supportsScope) { | |
attribute = attributeName(); | |
unique += 1; | |
element.setAttribute(attribute, unique); | |
} | |
scopeSelector = scope(selector, supportsScope, { attribute: attribute, value: unique }); | |
elements = element.querySelectorAll(scopeSelector); | |
if (!supportsScope) { | |
element.removeAttribute(attribute); | |
} | |
result = new Elements(); | |
result.push.apply(result, toArray(elements)); | |
return result; | |
} | |
function query(selector) { | |
return queryAll.call(this, selector)[0] || null; | |
} | |
function remove() { | |
var | |
parentNode = this.parentNode; | |
if (parentNode) { | |
parentNode.removeChild(this); | |
} | |
} | |
function replaceWith() { | |
var | |
parentNode = this.parentNode; | |
if (parentNode) { | |
parentNode.replaceChild(createDocumentFragment(arguments), this); | |
} | |
} | |
function replace() { | |
if (console.warn || console.log) { | |
console[console.warn ? 'warn' : 'log']('`replace` is deprecated, use `replaceWith` instead'); | |
} | |
replaceWith.apply(this, arguments); | |
} | |
elementPrototype = global.Element.prototype; | |
elementPrototypeFunctions = { | |
after: after, | |
append: append, | |
before: before, | |
closest: closest, | |
getElementsByClassName: getElementsByClassName, | |
prepend: prepend, | |
query: query, | |
queryAll: queryAll, | |
remove: remove, | |
replace: replace, | |
replaceWith: replaceWith | |
}; | |
forEach.call(keys(elementPrototypeFunctions), function delegate(method) { | |
if (!hasOwnProperty.call(this, method)) { | |
this[method] = elementPrototypeFunctions[method]; | |
} | |
}, elementPrototype); | |
if (!elementPrototype.matches) { | |
elementPrototype.matches = elementPrototype.matchesSelector || | |
elementPrototype.webkitMatchesSelector || | |
elementPrototype.khtmlMatchesSelector || | |
elementPrototype.mozMatchesSelector || | |
elementPrototype.msMatchesSelector || | |
elementPrototype.oMatchesSelector || | |
matches; | |
} | |
Elements.prototype = []; | |
forEach.call(['query', 'queryAll'], function delegate(method) { | |
this[method] = elementPrototypeFunctions[method]; | |
}, Elements.prototype); | |
document = global.document; | |
if (!('head' in document)) { | |
Object.defineProperty(document, 'head', { | |
get: function get() { | |
documentHead = documentHead || document.getElementsByTagName('head')[0]; | |
return documentHead; | |
} | |
}); | |
document.head = document.getElementsByTagName('head')[0]; | |
} | |
if (!document.getElementsByClassName) { | |
document.getElementsByClassName = getElementsByClassName; | |
} | |
forEach.call(['Document', 'DocumentFragment', 'document'], function iterator(object) { | |
if (!(object in global)) { | |
return; | |
} | |
forEach.call(['query', 'queryAll'], function delegate(method) { | |
if (!this[method]) { | |
this[method] = elementPrototypeFunctions[method]; | |
} | |
}, global[object].prototype || global[object]); | |
}); | |
})); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment