browsers/polyfill.js
/* eslint-disable */
/*!
Copyright (C) 2013-2015 by WebReflection
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
(function(window){
/*! (C) WebReflection Mit Style License */
if (document.createEvent) return;
var
DUNNOABOUTDOMLOADED = true,
READYEVENTDISPATCHED = false,
ONREADYSTATECHANGE = 'onreadystatechange',
DOMCONTENTLOADED = 'DOMContentLoaded',
SECRET = '__IE8__' + Math.random(),
// Object = window.Object,
defineProperty = Object.defineProperty ||
// just in case ...
function (object, property, descriptor) {
object[property] = descriptor.value;
},
defineProperties = Object.defineProperties ||
// IE8 implemented defineProperty but not the plural...
function (object, descriptors) {
for(var key in descriptors) {
if (hasOwnProperty.call(descriptors, key)) {
try {
defineProperty(object, key, descriptors[key]);
} catch(o_O) {
if (window.console) {
console.log(key + ' failed on object:', object, o_O.message);
}
}
}
}
},
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor,
hasOwnProperty = Object.prototype.hasOwnProperty,
// here IE7 will break like a charm
ElementPrototype = window.Element.prototype,
TextPrototype = window.Text.prototype,
// none of above native constructors exist/are exposed
possiblyNativeEvent = /^[a-z]+$/,
// ^ actually could probably be just /^[a-z]+$/
readyStateOK = /loaded|complete/,
types = {},
div = document.createElement('div'),
html = document.documentElement,
removeAttribute = html.removeAttribute,
setAttribute = html.setAttribute,
valueDesc = function (value) {
return {
enumerable: true,
writable: true,
configurable: true,
value: value
};
}
;
function commonEventLoop(currentTarget, e, $handlers, synthetic) {
for(var
handler,
continuePropagation,
handlers = $handlers.slice(),
evt = enrich(e, currentTarget),
i = 0, length = handlers.length; i < length; i++
) {
handler = handlers[i];
if (typeof handler === 'object') {
if (typeof handler.handleEvent === 'function') {
handler.handleEvent(evt);
}
} else {
handler.call(currentTarget, evt);
}
if (evt.stoppedImmediatePropagation) break;
}
continuePropagation = !evt.stoppedPropagation;
/*
if (continuePropagation && !synthetic && !live(currentTarget)) {
evt.cancelBubble = true;
}
*/
return (
synthetic &&
continuePropagation &&
currentTarget.parentNode
) ?
currentTarget.parentNode.dispatchEvent(evt) :
!evt.defaultPrevented
;
}
function commonDescriptor(get, set) {
return {
// if you try with enumerable: true
// IE8 will miserably fail
configurable: true,
get: get,
set: set
};
}
function commonTextContent(protoDest, protoSource, property) {
var descriptor = getOwnPropertyDescriptor(
protoSource || protoDest, property
);
defineProperty(
protoDest,
'textContent',
commonDescriptor(
function () {
return descriptor.get.call(this);
},
function (textContent) {
descriptor.set.call(this, textContent);
}
)
);
}
function enrich(e, currentTarget) {
e.currentTarget = currentTarget;
e.eventPhase = (
// AT_TARGET : BUBBLING_PHASE
e.target === e.currentTarget ? 2 : 3
);
return e;
}
function find(array, value) {
var i = array.length;
while(i-- && array[i] !== value);
return i;
}
function getTextContent() {
if (this.tagName === 'BR') return '\n';
var
textNode = this.firstChild,
arrayContent = []
;
while(textNode) {
if (textNode.nodeType !== 8 && textNode.nodeType !== 7) {
arrayContent.push(textNode.textContent);
}
textNode = textNode.nextSibling;
}
return arrayContent.join('');
}
function live(self) {
return self.nodeType !== 9 && html.contains(self);
}
function onkeyup(e) {
var evt = document.createEvent('Event');
evt.initEvent('input', true, true);
(e.srcElement || e.fromElement || document).dispatchEvent(evt);
}
function onReadyState(e) {
if (!READYEVENTDISPATCHED && readyStateOK.test(
document.readyState
)) {
READYEVENTDISPATCHED = !READYEVENTDISPATCHED;
document.detachEvent(ONREADYSTATECHANGE, onReadyState);
e = document.createEvent('Event');
e.initEvent(DOMCONTENTLOADED, true, true);
document.dispatchEvent(e);
}
}
function getter(attr) {
return function() {
return html[attr] || (document.body && document.body[attr]) || 0;
};
}
function setTextContent(textContent) {
var node;
while ((node = this.lastChild)) {
this.removeChild(node);
}
/*jshint eqnull:true */
if (textContent != null) {
this.appendChild(document.createTextNode(textContent));
}
}
function verify(self, e) {
if (!e) {
e = window.event;
}
if (!e.target) {
e.target = e.srcElement || e.fromElement || document;
}
if (!e.timeStamp) {
e.timeStamp = (new Date()).getTime();
}
return e;
}
// normalized textContent for:
// comment, script, style, text, title
commonTextContent(
window.HTMLCommentElement.prototype,
ElementPrototype,
'nodeValue'
);
commonTextContent(
window.HTMLScriptElement.prototype,
null,
'text'
);
commonTextContent(
TextPrototype,
null,
'nodeValue'
);
commonTextContent(
window.HTMLTitleElement.prototype,
null,
'text'
);
defineProperty(
window.HTMLStyleElement.prototype,
'textContent',
(function(descriptor){
return commonDescriptor(
function () {
return descriptor.get.call(this.styleSheet);
},
function (textContent) {
descriptor.set.call(this.styleSheet, textContent);
}
);
}(getOwnPropertyDescriptor(window.CSSStyleSheet.prototype, 'cssText')))
);
var opacityre = /\b\s*alpha\s*\(\s*opacity\s*=\s*(\d+)\s*\)/;
defineProperty(
window.CSSStyleDeclaration.prototype,
'opacity', {
get: function() {
var m = this.filter.match(opacityre);
return m ? (m[1] / 100).toString() : '';
},
set: function(value) {
this.zoom = 1;
var found = false;
if (value < 1) {
value = ' alpha(opacity=' + Math.round(value * 100) + ')';
}
else {
value = '';
}
this.filter = this.filter.replace(opacityre,
function() { found = true; return value; });
if (!found && value) {
this.filter += value;
}
}
}
);
defineProperties(
ElementPrototype,
{
// bonus
textContent: {
get: getTextContent,
set: setTextContent
},
// http://www.w3.org/TR/ElementTraversal/#interface-elementTraversal
firstElementChild: {
get: function () {
for(var
childNodes = this.childNodes || [],
i = 0, length = childNodes.length;
i < length; i++
) {
if (childNodes[i].nodeType == 1) return childNodes[i];
}
}
},
lastElementChild: {
get: function () {
for(var
childNodes = this.childNodes || [],
i = childNodes.length;
i--;
) {
if (childNodes[i].nodeType == 1) return childNodes[i];
}
}
},
oninput: {
get: function () {
return this._oninput || null;
},
set: function (oninput) {
if (this._oninput) {
this.removeEventListener('input', this._oninput);
this._oninput = oninput;
if (oninput) {
this.addEventListener('input', oninput);
}
}
}
},
previousElementSibling: {
get: function () {
var previousElementSibling = this.previousSibling;
while (previousElementSibling && previousElementSibling.nodeType != 1) {
previousElementSibling = previousElementSibling.previousSibling;
}
return previousElementSibling;
}
},
nextElementSibling: {
get: function () {
var nextElementSibling = this.nextSibling;
while (nextElementSibling && nextElementSibling.nodeType != 1) {
nextElementSibling = nextElementSibling.nextSibling;
}
return nextElementSibling;
}
},
childElementCount: {
get: function () {
for(var
count = 0,
childNodes = this.childNodes || [],
i = childNodes.length; i--; count += childNodes[i].nodeType == 1
);
return count;
}
},
/*
// children would be an override
// IE8 already supports them but with comments too
// not just nodeType 1
children: {
get: function () {
for(var
children = [],
childNodes = this.childNodes || [],
i = 0, length = childNodes.length;
i < length; i++
) {
if (childNodes[i].nodeType == 1) {
children.push(childNodes[i]);
}
}
return children;
}
},
*/
// DOM Level 2 EventTarget methods and events
addEventListener: valueDesc(function (type, handler, capture) {
if (typeof handler !== 'function' && typeof handler !== 'object') return;
var
self = this,
ontype = 'on' + type,
temple = self[SECRET] ||
defineProperty(
self, SECRET, {value: {}}
)[SECRET],
currentType = temple[ontype] || (temple[ontype] = {}),
handlers = currentType.h || (currentType.h = []),
e, attr
;
if (!hasOwnProperty.call(currentType, 'w')) {
currentType.w = function (e) {
// e[SECRET] is a silent notification needed to avoid
// fired events during live test
return e[SECRET] || commonEventLoop(self, verify(self, e), handlers, false);
};
// if not detected yet
if (!hasOwnProperty.call(types, ontype)) {
// and potentially a native event
if(possiblyNativeEvent.test(type)) {
// do this heavy thing
try {
// TODO: should I consider tagName too so that
// INPUT[ontype] could be different ?
e = document.createEventObject();
// do not clone ever a node
// specially a document one ...
// use the secret to ignore them all
e[SECRET] = true;
// document a part if a node has never been
// added to any other node, fireEvent might
// behave very weirdly (read: trigger unspecified errors)
if (self.nodeType != 9) {
/*jshint eqnull:true */
if (self.parentNode == null) {
div.appendChild(self);
}
if ((attr = self.getAttribute(ontype))) {
removeAttribute.call(self, ontype);
}
}
self.fireEvent(ontype, e);
types[ontype] = true;
} catch(meh) {
types[ontype] = false;
while (div.hasChildNodes()) {
div.removeChild(div.firstChild);
}
}
if (attr != null) {
setAttribute.call(self, ontype, attr);
}
} else {
// no need to bother since
// 'x-event' ain't native for sure
types[ontype] = false;
}
}
if ((currentType.n = types[ontype])) {
self.attachEvent(ontype, currentType.w);
}
}
if (find(handlers, handler) < 0) {
handlers[capture ? 'unshift' : 'push'](handler);
}
if (type === 'input') {
self.attachEvent('onkeyup', onkeyup);
}
}),
dispatchEvent: valueDesc(function (e) {
var
self = this,
ontype = 'on' + e.type,
temple = self[SECRET],
currentType = temple && temple[ontype],
valid = !!currentType,
parentNode
;
if (!e.target) e.target = self;
return (valid ? (
currentType.n /* && live(self) */ ?
self.fireEvent(ontype, e) :
commonEventLoop(
self,
e,
currentType.h,
true
)
) : (
(parentNode = self.parentNode) /* && live(self) */ ?
parentNode.dispatchEvent(e) :
true
)), !e.defaultPrevented;
}),
removeEventListener: valueDesc(function (type, handler, capture) {
if (typeof handler !== 'function' && typeof handler !== 'object') return;
var
self = this,
ontype = 'on' + type,
temple = self[SECRET],
currentType = temple && temple[ontype],
handlers = currentType && currentType.h,
i = handlers ? find(handlers, handler) : -1
;
if (-1 < i) handlers.splice(i, 1);
})
}
);
/* this is not needed in IE8
defineProperties(window.HTMLSelectElement.prototype, {
value: {
get: function () {
return this.options[this.selectedIndex].value;
}
}
});
//*/
// EventTarget methods for Text nodes too
defineProperties(TextPrototype, {
addEventListener: valueDesc(ElementPrototype.addEventListener),
dispatchEvent: valueDesc(ElementPrototype.dispatchEvent),
removeEventListener: valueDesc(ElementPrototype.removeEventListener)
});
defineProperties(
window.XMLHttpRequest.prototype,
{
addEventListener: valueDesc(function (type, handler, capture) {
var
self = this,
ontype = 'on' + type,
temple = self[SECRET] ||
defineProperty(
self, SECRET, {value: {}}
)[SECRET],
currentType = temple[ontype] || (temple[ontype] = {}),
handlers = currentType.h || (currentType.h = [])
;
if (find(handlers, handler) < 0) {
if (!self[ontype]) {
self[ontype] = function () {
var e = document.createEvent('Event');
e.initEvent(type, true, true);
self.dispatchEvent(e);
};
}
handlers[capture ? 'unshift' : 'push'](handler);
}
}),
dispatchEvent: valueDesc(function (e) {
var
self = this,
ontype = 'on' + e.type,
temple = self[SECRET],
currentType = temple && temple[ontype],
valid = !!currentType
;
return valid && (
currentType.n /* && live(self) */ ?
self.fireEvent(ontype, e) :
commonEventLoop(
self,
e,
currentType.h,
true
)
);
}),
removeEventListener: valueDesc(ElementPrototype.removeEventListener)
}
);
var buttonGetter = getOwnPropertyDescriptor(Event.prototype, 'button').get;
defineProperties(
window.Event.prototype,
{
bubbles: valueDesc(true),
cancelable: valueDesc(true),
preventDefault: valueDesc(function () {
if (this.cancelable) {
this.returnValue = false;
}
}),
stopPropagation: valueDesc(function () {
this.stoppedPropagation = true;
this.cancelBubble = true;
}),
stopImmediatePropagation: valueDesc(function () {
this.stoppedImmediatePropagation = true;
this.stopPropagation();
}),
initEvent: valueDesc(function(type, bubbles, cancelable){
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
if (!this.bubbles) {
this.stopPropagation();
}
}),
pageX: {get: function(){ return this._pageX || (this._pageX = this.clientX + window.scrollX - (html.clientLeft || 0)); }},
pageY: {get: function(){ return this._pageY || (this._pageY = this.clientY + window.scrollY - (html.clientTop || 0)); }},
which: {get: function(){ return this.keyCode ? this.keyCode : (isNaN(this.button) ? undefined : this.button + 1); }},
charCode: {get: function(){ return (this.keyCode && this.type == 'keypress') ? this.keyCode : 0; }},
buttons: {get: function(){ return buttonGetter.call(this); }},
button: {get: function() {
var buttons = this.buttons;
return (buttons & 1 ? 0 : (buttons & 2 ? 2 : (buttons & 4 ? 1 : undefined)));
}},
defaultPrevented: {get: function() {
// if preventDefault() was never called, or returnValue not given a value
// then returnValue is undefined
var returnValue = this.returnValue, undef;
return !(returnValue === undef || returnValue);
}},
relatedTarget: {get: function() {
var type = this.type;
if (type === 'mouseover') {
return this.fromElement;
} else if (type === 'mouseout') {
return this.toElement;
} else {
return null;
}
}}
}
);
defineProperties(
window.HTMLDocument.prototype,
{
defaultView: {
get: function () {
return this.parentWindow;
}
},
textContent: {
get: function () {
return this.nodeType === 11 ? getTextContent.call(this) : null;
},
set: function (textContent) {
if (this.nodeType === 11) {
setTextContent.call(this, textContent);
}
}
},
addEventListener: valueDesc(function(type, handler, capture) {
var self = this;
ElementPrototype.addEventListener.call(self, type, handler, capture);
// NOTE: it won't fire if already loaded, this is NOT a $.ready() shim!
// this behaves just like standard browsers
if (
DUNNOABOUTDOMLOADED &&
type === DOMCONTENTLOADED &&
!readyStateOK.test(
self.readyState
)
) {
DUNNOABOUTDOMLOADED = false;
self.attachEvent(ONREADYSTATECHANGE, onReadyState);
/* global top */
if (window == top) {
(function gonna(e){try{
self.documentElement.doScroll('left');
onReadyState();
}catch(o_O){
setTimeout(gonna, 50);
}}());
}
}
}),
dispatchEvent: valueDesc(ElementPrototype.dispatchEvent),
removeEventListener: valueDesc(ElementPrototype.removeEventListener),
createEvent: valueDesc(function(Class){
var e;
if (Class !== 'Event') throw new Error('unsupported ' + Class);
e = document.createEventObject();
e.timeStamp = (new Date()).getTime();
return e;
})
}
);
defineProperties(
window.Window.prototype,
{
getComputedStyle: valueDesc(function(){
var // partially grabbed from jQuery and Dean's hack
notpixel = /^(?:[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|))(?!px)[a-z%]+$/,
position = /^(top|right|bottom|left)$/,
re = /\-([a-z])/g,
place = function (match, $1) {
return $1.toUpperCase();
}
;
function ComputedStyle(_) {
this._ = _;
}
ComputedStyle.prototype.getPropertyValue = function (name) {
var
el = this._,
style = el.style,
currentStyle = el.currentStyle,
runtimeStyle = el.runtimeStyle,
result,
left,
rtLeft
;
if (name == 'opacity') {
return style.opacity || '1';
}
name = (name === 'float' ? 'style-float' : name).replace(re, place);
result = currentStyle ? currentStyle[name] : style[name];
if (notpixel.test(result) && !position.test(name)) {
left = style.left;
rtLeft = runtimeStyle && runtimeStyle.left;
if (rtLeft) {
runtimeStyle.left = currentStyle.left;
}
style.left = name === 'fontSize' ? '1em' : result;
result = style.pixelLeft + 'px';
style.left = left;
if (rtLeft) {
runtimeStyle.left = rtLeft;
}
}
/*jshint eqnull:true */
return result == null ?
result : ((result + '') || 'auto');
};
// unsupported
function PseudoComputedStyle() {}
PseudoComputedStyle.prototype.getPropertyValue = function () {
return null;
};
return function (el, pseudo) {
return pseudo ?
new PseudoComputedStyle(el) :
new ComputedStyle(el);
};
}()),
addEventListener: valueDesc(function (type, handler, capture) {
var
self = window,
ontype = 'on' + type,
handlers
;
if (!self[ontype]) {
self[ontype] = function(e) {
return commonEventLoop(self, verify(self, e), handlers, false) && undefined;
};
}
handlers = self[ontype][SECRET] || (
self[ontype][SECRET] = []
);
if (find(handlers, handler) < 0) {
handlers[capture ? 'unshift' : 'push'](handler);
}
}),
dispatchEvent: valueDesc(function (e) {
var method = window['on' + e.type];
return method ? method.call(window, e) !== false && !e.defaultPrevented : true;
}),
removeEventListener: valueDesc(function (type, handler, capture) {
var
ontype = 'on' + type,
handlers = (window[ontype] || Object)[SECRET],
i = handlers ? find(handlers, handler) : -1
;
if (-1 < i) handlers.splice(i, 1);
}),
pageXOffset: {get: getter('scrollLeft')},
pageYOffset: {get: getter('scrollTop')},
scrollX: {get: getter('scrollLeft')},
scrollY: {get: getter('scrollTop')},
innerWidth: {get: getter('clientWidth')},
innerHeight: {get: getter('clientHeight')}
}
);
window.HTMLElement = window.Element;
(function (styleSheets, HTML5Element, i) {
for (i = 0; i < HTML5Element.length; i++) document.createElement(HTML5Element[i]);
if (!styleSheets.length) document.createStyleSheet('');
styleSheets[0].addRule(HTML5Element.join(','), 'display:block;');
}(document.styleSheets, ['header', 'nav', 'section', 'article', 'aside', 'footer']));
}(window || global));