trisquel-icecat/icecat/debian/tests/data/HTML5test/scripts/7/engine.js

4092 lines
96 KiB
JavaScript

Test7 = (function() {
var NO = 0,
YES = 1,
OLD = 2,
BUGGY = 4,
PREFIX = 8,
BLOCKED = 16,
DISABLED = 32,
UNCONFIRMED = 64;
var release = 7;
var blacklists = [];
function results (parent) { this.initialize(parent); }
results.prototype = {
initialize: function(parent) {
this.parent = parent;
this.items = [];
this.points = 0;
this.max = 0;
this.backgroundTasks = 0;
this.update();
},
startBackground: function(id) {
this.parent.startBackground(id);
},
stopBackground: function(id) {
this.parent.stopBackground(id);
},
getSection: function(data) {
var i = new section(this, data);
this.items.push(i);
return i;
},
update: function() {
var points = 0;
var max = 0;
for (var i = 0; i < this.items.length; i++) {
points += this.items[i].getPoints();
max += this.items[i].getMaximum();
}
this.points = points;
this.max = max;
},
retrieve: function() {
var data = {
points: this.points,
items: {}
};
for (var i = 0; i < this.items.length; i++) {
data.items[this.items[i].data.id] = this.items[i].retrieve();
}
return data;
}
};
function section (parent, data) { this.initialize(parent, data); }
section.prototype = {
initialize: function(parent, data) {
this.items = [];
this.points = 0;
this.max = 0;
this.parent = parent;
this.data = data;
this.path = data.id;
},
startBackground: function(id) {
this.parent.startBackground(id);
},
stopBackground: function(id) {
this.parent.stopBackground(id);
},
setItem: function(result) {
var i = new item(this, result);
this.items.push(i);
this.update();
return i;
},
getGroup: function(data) {
var i = new group(this, data);
this.items.push(i);
return i;
},
update: function() {
var points = 0;
var max = 0;
for (var i = 0; i < this.items.length; i++) {
points += this.items[i].getPoints();
max += this.items[i].getMaximum();
}
this.points = points;
this.max = max;
this.parent.update();
},
getPoints: function() {
return this.points;
},
getMaximum: function() {
return this.max;
},
retrieve: function() {
var data = {
points: this.points,
items: {}
};
for (var i = 0; i < this.items.length; i++) {
data.items[this.items[i].data.id] = this.items[i].retrieve();
}
return data;
}
};
function group (parent, data) { this.initialize(parent, data); }
group.prototype = {
initialize: function(parent, data) {
this.items = [];
this.points = 0;
this.max = 0;
this.data = data;
this.parent = parent;
this.path = parent.path + '.' + data.id;
},
update: function() {
var points = 0;
var max = 0;
var count = 0;
var hasRequired = false;
var passedAll = true;
var passedAllRequiredFields = true;
var passedPartially = false;
for (var i = 0; i < this.items.length; i++) {
points += this.items[i].getPoints();
max += this.items[i].getMaximum();
count += this.items[i].getPassed() > 0 ? 1 : 0;
if (this.items[i].getRequired()) {
hasRequired = true;
passedAllRequiredFields &= this.items[i].getPassed() > 0;
}
passedAll &= this.items[i].getPassed() > 0;
passedPartially |= this.items[i].getPartiallyPassed();
}
if (!passedAllRequiredFields) {
points = 0;
}
if (this.data.value && this.items.length) {
if (hasRequired ? passedAllRequiredFields : passedAll) {
points += this.data.value;
}
max += this.data.value;
}
this.points = points;
this.max = max;
this.parent.update();
},
startBackground: function(id) {
this.parent.startBackground(id);
},
stopBackground: function(id) {
this.parent.stopBackground(id);
},
setItem: function(result) {
var i = new item(this, result);
this.items.push(i);
this.update();
return i;
},
getPoints: function() {
return this.points;
},
getMaximum: function() {
return this.max;
},
retrieve: function() {
var data = {
points: this.points,
items: {}
};
for (var i = 0; i < this.items.length; i++) {
data.items[this.items[i].data.id] = this.items[i].retrieve();
}
return data;
}
};
function item (parent, data) { this.initialize(parent, data); }
item.prototype = {
initialize: function(parent, data) {
this.parent = parent;
this.path = parent.path + '.' + data.id;
this.data = data;
if (typeof this.data.value == 'undefined') this.data.value = 0;
if (typeof this.data.award == 'undefined') this.data.award = this.data.value;
if (typeof this.data.passed == 'undefined') this.data.padded = false;
if (this.data.passed) {
var blacklist = this.isOnBlacklist();
if (blacklist) {
this.data.passed = blacklist;
}
}
},
update: function(data) {
for (var key in data) {
this.data[key] = data[key];
}
if (typeof this.data.value == 'undefined') this.data.value = 0;
if (typeof this.data.award == 'undefined') this.data.award = this.data.value;
if (typeof this.data.passed == 'undefined') this.data.passed = false;
if (this.data.passed) {
var blacklist = this.isOnBlacklist();
if (blacklist) {
this.data.passed = blacklist;
}
}
this.parent.update();
},
isOnBlacklist: function() {
var part = '';
var parts = this.path.split('.');
for (var i = 0; i < parts.length; i++) {
part += (i == 0 ? '' : '.') + parts[i];
for (var k = 0; k < blacklists.length; k++) {
if (typeof blacklists[k][1][part] != 'undefined') {
if (blacklists[k][1][part]) {
if (console && console.log) console.log('BLOCKED TEST: ' + part + '!');
return blacklists[k][0];
}
}
}
}
return false;
},
startBackground: function() {
this.parent.startBackground(this.data.id);
},
stopBackground: function() {
this.parent.stopBackground(this.data.id);
},
getPoints: function() {
return this.data.passed & YES ? this.data.award : 0;
},
getMaximum: function() {
return this.data.value;
},
getPassed: function() {
return this.data.passed;
},
getPartiallyPassed: function() {
if (this.data.custom && this.data.custom == 'partial') {
return true;
}
return this.getPassed();
},
getRequired: function() {
return !!this.data.required;
},
retrieve: function() {
var data = {
points: this.getPoints()
};
return data;
}
};
var isEventSupported = (function(){
var TAGNAMES = {
'select':'input','change':'input','input':'input',
'submit':'form','reset':'form','forminput':'form','formchange':'form',
'error':'img','load':'img','abort':'img'
}
function isEventSupported(eventName, element) {
element = element || document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName;
var isSupported = (eventName in element);
if (!isSupported) {
if (!element.setAttribute) {
element = document.createElement('div');
}
if (element.setAttribute && element.removeAttribute) {
element.setAttribute(eventName, '');
isSupported = typeof element[eventName] == 'function';
if (typeof element[eventName] != 'undefined') {
element[eventName] = void 0;
}
element.removeAttribute(eventName);
}
}
element = null;
return isSupported;
}
return isEventSupported;
})();
var getRenderedStyle = (function(){
function getRenderedStyle(elem, name) {
if (document.defaultView && document.defaultView.getComputedStyle) {
s = document.defaultView.getComputedStyle(elem, "");
r = [];
if (s.length) {
for (var i = 0; i < s.length; i++) {
try {
v = s.getPropertyValue(s[i]);
if (v != '') {
r.push(s[i] + ': ' + v);
}
} catch(e) {
};
}
} else {
for (var i in s) {
try {
v = s.getPropertyValue(i);
if (v != '') {
r.push(i + ': ' + v);
}
} catch(e) {
};
}
}
return r.join('; ') + ';';
} else {
return null;
}
}
return getRenderedStyle;
})();
function testParsing (results) { this.initialize(results); }
testParsing.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'parsing'
});
this.section.setItem({
id: 'doctype',
passed: document.compatMode == 'CSS1Compat',
value: 0
});
var result = true;
var e = document.createElement('div');
try {
e.innerHTML = "<div<div>";
result &= e.firstChild && e.firstChild.nodeName == "DIV<DIV";
e.innerHTML = "<div foo<bar=''>";
result &= e.firstChild.attributes[0].nodeName == "foo<bar" || e.firstChild.attributes[0].name == "foo<bar";
e.innerHTML = "<div foo=`bar`>";
result &= e.firstChild.getAttribute("foo") == "`bar`";
e.innerHTML = "<div \"foo=''>";
result &= e.firstChild && (e.firstChild.attributes[0].nodeName == "\"foo" || e.firstChild.attributes[0].name == "\"foo");
e.innerHTML = "<a href='\nbar'></a>";
result &= e.firstChild && e.firstChild.getAttribute("href") == "\nbar";
e.innerHTML = "<!DOCTYPE html>";
result &= e.firstChild == null;
e.innerHTML = "\u000D";
result &= e.firstChild && e.firstChild.nodeValue == "\u000A";
e.innerHTML = "&lang;&rang;";
result &= e.firstChild.nodeValue == "\u27E8\u27E9";
e.innerHTML = "&apos;";
result &= e.firstChild.nodeValue == "'";
e.innerHTML = "&ImaginaryI;";
result &= e.firstChild.nodeValue == "\u2148";
e.innerHTML = "&Kopf;";
result &= e.firstChild.nodeValue == "\uD835\uDD42";
e.innerHTML = "&notinva;";
result &= e.firstChild.nodeValue == "\u2209";
e.innerHTML = '<?import namespace="foo" implementation="#bar">';
result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == '?import namespace="foo" implementation="#bar"';
e.innerHTML = '<!--foo--bar-->';
result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == 'foo--bar';
e.innerHTML = '<![CDATA[x]]>';
result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == '[CDATA[x]]';
e.innerHTML = "<textarea><!--</textarea>--></textarea>";
result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == "<!--";
e.innerHTML = "<textarea><!--</textarea>-->";
result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == "<!--";
e.innerHTML = "<style><!--</style>--></style>";
result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == "<!--";
e.innerHTML = "<style><!--</style>-->";
result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == "<!--";
} catch(e) {
result = false;
}
this.section.setItem({
id: 'tokenizer',
passed: result,
value: 3
});
var result = true;
var e = document.createElement('div');
try {
var h = document.createElement("html");
h.innerHTML = "";
result &= h.firstChild && h.firstChild.nodeName == "HEAD" && h.lastChild.nodeName == "BODY" && h.firstChild.nextSibling == h.lastChild;
} catch (e) {
result = false;
}
try {
var t = document.createElement("table");
t.innerHTML = "<col>";
result &= t.firstChild && t.firstChild.nodeName == "COLGROUP";
} catch (e) {
result = false;
}
e.innerHTML = "<ul><li>A </li> <li>B</li></ul>";
result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.firstChild && e.firstChild.firstChild.firstChild.nodeValue == "A ";
e.innerHTML = "<table><form><input type=hidden><input></form><div></div></table>";
result &= e.firstChild &&
e.firstChild.nodeName == "INPUT" &&
e.firstChild.nextSibling &&
e.firstChild.nextSibling.nodeName == "DIV" &&
e.lastChild.nodeName == "TABLE" &&
e.firstChild.nextSibling.nextSibling == e.lastChild &&
e.lastChild.firstChild &&
e.lastChild.firstChild.nodeName == "FORM" &&
e.lastChild.firstChild.firstChild == null &&
e.lastChild.lastChild.nodeName == "INPUT" &&
e.lastChild.firstChild.nextSibling == e.lastChild.lastChild;
e.innerHTML = "<i>A<b>B<p></i>C</b>D";
result &= e.firstChild &&
e.childNodes.length == 3 &&
e.childNodes[0].nodeName == "I" &&
e.childNodes[0].childNodes.length == 2 &&
e.childNodes[0].childNodes[0].nodeValue == "A" &&
e.childNodes[0].childNodes[1].nodeName == "B" &&
e.childNodes[0].childNodes[1].childNodes.length == 1 &&
e.childNodes[0].childNodes[1].childNodes[0].nodeValue == "B" &&
e.childNodes[1].nodeName == "B" &&
e.childNodes[1].firstChild == null &&
e.childNodes[2].nodeName == "P" &&
e.childNodes[2].childNodes.length == 2 &&
e.childNodes[2].childNodes[0].nodeName == "B" &&
e.childNodes[2].childNodes[0].childNodes.length == 2 &&
e.childNodes[2].childNodes[0].childNodes[0].nodeName == "I" &&
e.childNodes[2].childNodes[0].childNodes[0].firstChild == null &&
e.childNodes[2].childNodes[0].childNodes[1].nodeValue == "C" &&
e.childNodes[2].childNodes[1].nodeValue == "D";
e.innerHTML = "<div></div>";
result &= e.firstChild && "namespaceURI" in e.firstChild && e.firstChild.namespaceURI == "http://www.w3.org/1999/xhtml";
this.section.setItem({
id: 'tree',
passed: result,
value: 2
});
var e = document.createElement('div');
e.innerHTML = '<svg></svg>';
var passed = e.firstChild && "namespaceURI" in e.firstChild && e.firstChild.namespaceURI == 'http://www.w3.org/2000/svg';
this.section.setItem({
id: 'svg',
passed: passed
});
var e = document.createElement('div');
e.innerHTML = '<math></math>';
var passed = e.firstChild && "namespaceURI" in e.firstChild && e.firstChild.namespaceURI == 'http://www.w3.org/1998/Math/MathML';
this.section.setItem({
id: 'mathml',
passed: passed
});
}
};
function testResponsive (results) { this.initialize(results); }
testResponsive.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'responsive'
});
this.section.setItem({
id: 'picture',
passed: 'HTMLPictureElement' in window,
value: 5
});
this.section.setItem({
id: 'srcset',
passed: 'srcset' in document.createElement('img'),
value: 5
});
this.section.setItem({
id: 'sizes',
passed: 'sizes' in document.createElement('img'),
value: 5
});
}
};
function testCanvas (results) { this.initialize(results); }
testCanvas.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'canvas'
});
this.canvas = document.createElement('canvas');
this.section.setItem({
id: 'context',
passed: !! (this.canvas.getContext && typeof CanvasRenderingContext2D != 'undefined' && this.canvas.getContext('2d') instanceof CanvasRenderingContext2D),
value: 10
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = typeof this.canvas.getContext('2d').fillText == 'function';
}
catch(e) {
}
}
this.section.setItem({
id: 'text',
passed: passed,
value: 2
});
this.section.setItem({
id: 'path',
passed: typeof Path2D != "undefined" ? YES : typeof Path != "undefined" ? YES | OLD : NO,
value: 2
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = typeof this.canvas.getContext('2d').ellipse != 'undefined';
}
catch(e) {
}
}
this.section.setItem({
id: 'ellipse',
passed: passed,
value: 2
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = typeof this.canvas.getContext('2d').setLineDash != 'undefined';
}
catch(e) {
}
}
this.section.setItem({
id: 'dashed',
passed: passed,
value: 2
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = typeof this.canvas.getContext('2d').drawSystemFocusRing != 'undefined';
}
catch(e) {
}
}
this.section.setItem({
id: 'focusring',
passed: passed,
value: 1
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = typeof this.canvas.getContext('2d').addHitRegion != 'undefined';
}
catch(e) {
}
}
this.section.setItem({
id: 'hittest',
passed: passed,
value: 1
});
var passed = false;
if (this.canvas.getContext) {
this.canvas.width = 1;
this.canvas.height = 1;
try {
var ctx = this.canvas.getContext('2d');
ctx.fillStyle = '#fff';
ctx.fillRect(0,0,1,1);
ctx.globalCompositeOperation = 'screen';
ctx.fillStyle = '#000';
ctx.fillRect(0,0,1,1);
var data = ctx.getImageData(0,0,1,1);
passed = ctx.globalCompositeOperation == 'screen' && data.data[0] == 255;
}
catch(e) {
}
}
this.section.setItem({
id: 'blending',
passed: passed,
value: 5
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = this.canvas.toDataURL('image/png').substring(5,14) == 'image/png';
}
catch(e) {
}
}
this.section.setItem({
id: 'png',
passed: passed,
value: 0
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = this.canvas.toDataURL('image/jpeg').substring(5,15) == 'image/jpeg';
}
catch(e) {
}
}
this.section.setItem({
id: 'jpeg',
passed: passed,
value: 0
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = this.canvas.toDataURL('image/vnd.ms-photo').substring(5,23) == 'image/vnd.ms-photo';
}
catch(e) {
}
}
this.section.setItem({
id: 'jpegxr',
passed: passed,
value: 0
});
var passed = false;
if (this.canvas.getContext) {
try {
passed = this.canvas.toDataURL('image/webp').substring(5,15) == 'image/webp';
}
catch(e) {
}
}
this.section.setItem({
id: 'webp',
passed: passed,
value: 0
});
}
};
function testVideo (results) { this.initialize(results); }
testVideo.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'video'
});
this.element = document.createElement('video');
this.section.setItem({
id: 'element',
passed: !!this.element.canPlayType,
value: 16
});
this.section.setItem({
id: 'audiotracks',
passed: 'audioTracks' in this.element,
value: 2
});
this.section.setItem({
id: 'videotracks',
passed: 'videoTracks' in this.element,
value: 2
});
this.section.setItem({
id: 'subtitle',
passed: 'track' in document.createElement('track'),
value: 8
});
this.section.setItem({
id: 'poster',
passed: 'poster' in this.element,
value: 1
});
this.section.setItem({
id: 'drm',
passed: 'setMediaKeys' in this.element ? YES : 'webkitAddKey' in this.element || 'webkitSetMediaKeys' in this.element || 'mozSetMediaKeys' in this.element || 'msSetMediaKeys' in this.element ? YES | PREFIX : NO
});
this.section.setItem({
id: 'mediasource',
passed: 'MediaSource' in window ? YES : 'WebKitMediaSource' in window || 'mozMediaSource' in window || 'msMediaSource' in window ? YES | PREFIX : NO,
value: 2
});
this.section.setItem({
id: 'recorder',
passed: 'MediaRecorder' in window,
value: 2
});
/* I added a workaround for IE9, which only detects H.264 if you also provide an audio codec. Bug filed @ connect.microsoft.com */
var item = {
id: 'mpeg4',
passed: !!this.element.canPlayType && this.canPlayType('video/mp4; codecs="mp4v.20.8"')
};
this.section.setItem(item);
var item = {
id: 'h264',
passed: !!this.element.canPlayType && (this.canPlayType('video/mp4; codecs="avc1.42E01E"') || this.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'))
};
this.section.setItem(item);
var item = {
id: 'h265',
passed: !!this.element.canPlayType && (this.canPlayType('video/mp4; codecs="hvc1.1.L0.0"') || this.canPlayType('video/mp4; codecs="hev1.1.L0.0"'))
};
this.section.setItem(item);
var item = {
id: 'theora',
passed: !!this.element.canPlayType && this.canPlayType('video/ogg; codecs="theora"')
};
this.section.setItem(item);
var item = {
id: 'webmvp8',
passed: !!this.element.canPlayType && this.canPlayType('video/webm; codecs="vp8"')
};
this.section.setItem(item);
var item = {
id: 'webmvp9',
passed: !!this.element.canPlayType && this.canPlayType('video/webm; codecs="vp9"')
};
this.section.setItem(item);
var passed = true;
if (!!this.element.canPlayType) {
if (this.element.canPlayType('video/nonsense') == 'no') {
passed = false;
if (console && console.log) console.log('Codec detection is buggy: known bug in Firefox 3.5.0 - 3.5.1 and Safari 4.0.0 - 4.0.4 that answer "no" to unknown codecs instead of an empty string')
}
if (this.element.canPlayType('video/webm') == 'probably') {
passed = false;
if (console && console.log) console.log('Codec detection is buggy: known bug that Firefox 27 and earlier always says "probably" when asked about WebM, even when the codecs string is not present')
}
if (this.element.canPlayType('video/mp4; codecs="avc1.42E01E"') == 'maybe' && this.element.canPlayType('video/mp4') == 'probably') {
passed = false;
if (console && console.log) console.log('Codec detection is buggy: known bug in iOS 4.1 and earlier that switches "maybe" and "probably" around')
}
if (this.element.canPlayType('video/mp4; codecs="avc1.42E01E"') == 'maybe' && this.element.canPlayType('video/mp4') == 'maybe') {
passed = false;
if (console && console.log) console.log('Codec detection is buggy: known bug in Android where no better answer than "maybe" is given')
}
if (this.element.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') == 'probably' && this.element.canPlayType('video/mp4; codecs="avc1.42E01E"') != 'probably') {
passed = false;
if (console && console.log) console.log('Codec detection is buggy: known bug in Internet Explorer 9 that requires both audio and video codec on test')
}
/* This test should always return an empty string, if not the codecs string is ignored */
// if (this.element.canPlayType('video/mp4; codecs="whatthewhat"') != '') passed = false;
/* This test should always return an empty string, if not there is no check at all */
// if (this.element.canPlayType('video/huh') != '') passed = false;
}
var item = {
id: 'canplaytype',
passed: this.element.canPlayType ? (passed ? YES : YES | BUGGY) : NO,
value: 4
};
this.section.setItem(item);
},
canPlayType: function(t) {
/*
There is a bug in iOS 4.1 or earlier where probably and maybe are switched around.
This bug was reported and fixed in iOS 4.2
*/
if (Browsers.isOs('iOS', '<', '4.2'))
return this.element.canPlayType(t) == 'probably' || this.element.canPlayType(t) == 'maybe';
else
return this.element.canPlayType(t) == 'probably';
}
};
function testAudio (results) { this.initialize(results); }
testAudio.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'audio'
});
this.element = document.createElement('audio');
this.section.setItem({
id: 'element',
passed: !!this.element.canPlayType,
value: 18
});
this.section.setItem({
id: 'loop',
passed: 'loop' in this.element,
value: 1
});
this.section.setItem({
id: 'preload',
passed: 'preload' in this.element,
value: 1
});
var item = {
id: 'pcm',
passed: !!this.element.canPlayType && this.canPlayType('audio/wav; codecs="1"')
};
this.section.setItem(item);
var r = false;
if (this.element.canPlayType) {
var t = this.element.canPlayType('audio/mpeg');
if (t == 'maybe') {
// We need to check if the browser really supports playing MP3s by loading one and seeing if the
// loadedmetadata event is triggered... but for now assume it does support it...
r = true;
} else if (t == 'probably') {
r = true;
}
}
var item = {
id: 'mp3',
passed: r
};
this.section.setItem(item);
var item = {
id: 'aac',
passed: !!this.element.canPlayType && this.canPlayType('audio/mp4; codecs="mp4a.40.2"')
};
this.section.setItem(item);
var item = {
id: 'ac3',
passed: !!this.element.canPlayType && this.canPlayType('audio/mp4; codecs="ac-3"')
};
this.section.setItem(item);
var item = {
id: 'ec3',
passed: !!this.element.canPlayType && this.canPlayType('audio/mp4; codecs="ec-3"')
};
this.section.setItem(item);
var item = {
id: 'vorbis',
passed: !!this.element.canPlayType && this.canPlayType('audio/ogg; codecs="vorbis"')
};
this.section.setItem(item);
var item = {
id: 'opus',
passed: !!this.element.canPlayType && this.canPlayType('audio/ogg; codecs="opus"')
};
this.section.setItem(item);
var item = {
id: 'webm',
passed: !!this.element.canPlayType && this.canPlayType('audio/webm; codecs="vorbis"')
};
this.section.setItem(item);
var item = {
id: 'webmopus',
passed: !!this.element.canPlayType && this.canPlayType('audio/webm; codecs="opus"')
};
this.section.setItem(item);
this.section.setItem({
id: 'webaudio',
passed: 'AudioContext' in window ? YES : 'webkitAudioContext' in window || 'mozAudioContext' in window || 'oAudioContext' in window || 'msAudioContext' in window ? YES | PREFIX : NO,
value: 5
});
this.section.setItem({
id: 'speechrecognition',
passed: 'SpeechRecognition' in window ? YES : 'webkitSpeechRecognition' in window || 'mozSpeechRecognition' in window || 'oSpeechRecognition' in window || 'msSpeechRecognition' in window ? YES | PREFIX : NO,
value: 3
});
var speechSynthesis = window.speechSynthesis || window.webkitSpeechSynthesis || window.mozSpeechSynthesis || window.oSpeechSynthesis || window.msSpeechSynthesis;
var available = 'speechSynthesis' in window ? YES : 'webkitSpeechSynthesis' in window || 'mozSpeechSynthesis' in window || 'oSpeechSynthesis' in window || 'msSpeechSynthesis' in window ? YES | PREFIX : NO;
var voices = speechSynthesis ? speechSynthesis.getVoices().length : 0;
var speechItem = this.section.setItem({
id: 'speechsynthesis',
passed: speechSynthesis && voices ? available : NO,
value: 2
});
if (speechSynthesis && !voices) {
if (speechSynthesis.addEventListener) {
speechItem.startBackground();
speechSynthesis.addEventListener("voiceschanged", function() {
voices = speechSynthesis.getVoices().length;
speechItem.update({
passed: voices ? available : NO,
});
speechItem.stopBackground();
});
window.setTimeout(function() {
speechItem.stopBackground();
}, 1000);
}
}
},
canPlayType: function(t) {
/*
There is a bug in iOS 4.1 or earlier where probably and maybe are switched around.
This bug was reported and fixed in iOS 4.2
*/
if (Browsers.isOs('iOS', '<', '4.2'))
return this.element.canPlayType(t) == 'probably' || this.element.canPlayType(t) == 'maybe';
else
return this.element.canPlayType(t) == 'probably';
}
};
function testWebRTC (results) { this.initialize(results); }
testWebRTC.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'webrtc'
});
var webrtc = !!window.RTCPeerConnection ? YES : !!window.webkitRTCPeerConnection || !!window.mozRTCPeerConnection || !!window.msRTCPeerConnection || !!window.oRTCPeerConnection ? YES | PREFIX : NO;
var objectrtc = !!window.RTCIceTransport ? YES : !!window.webkitRTCIceTransport || !!window.mozRTCIceTransport || !!window.msRTCIceTransport || !!window.oRTCIceTransport ? YES | PREFIX : NO;
this.section.setItem({
id: 'webrtc',
passed: webrtc,
value: 15
});
this.section.setItem({
id: 'objectrtc',
passed: objectrtc,
value: 0,
award: webrtc == NO ? 15 : 0 /* Only award 15 points when WebRTC is not supported */
});
var passed = false;
try {
o = new (window.RTCPeerConnection || window.msRTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection)(null);
passed = 'createDataChannel' in o;
}
catch(e) {
}
this.section.setItem({
id: 'datachannel',
passed: passed ? (window.RTCPeerConnection ? YES : YES | PREFIX) : NO,
value: 5
});
}
};
function testInput (results) { this.initialize(results); }
testInput.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'input'
});
this.section.setItem({
id: 'getUserMedia',
passed: !!navigator.getUserMedia ? YES : !!navigator.webkitGetUserMedia || !!navigator.mozGetUserMedia || !!navigator.msGetUserMedia || !!navigator.oGetUserMedia ? YES | PREFIX : NO,
value: 15
});
this.section.setItem({
id: 'getGamepads',
passed: !!navigator.getGamepads ? YES : !!navigator.webkitGetGamepads || !!navigator.mozGetGamepads || !!navigator.msGetGamepads || !!navigator.oGetGamepads ? YES | PREFIX : NO,
value: 2
});
this.section.setItem({
id: 'pointerLock',
passed: 'pointerLockElement' in document ? YES : 'oPointerLockElement' in document || 'msPointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document ? YES | PREFIX : NO,
value: 3
});
this.section.setItem({
id: 'pointerevents',
passed: !!window.PointerEvent ? YES : !!window.webkitPointerEvent || !!window.mozPointerEvent || !!window.msPointerEvent || !!window.oPointerEvent ? YES | PREFIX : NO,
value: 5
});
}
};
function testElements (results) { this.initialize(results); }
testElements.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'elements'
});
var element = document.createElement('div');
element.setAttribute('data-test', 'test');
this.section.setItem({
id: 'dataset',
passed: 'dataset' in element,
value: 3
});
var group = this.section.getGroup({
id: 'section'
});
var elements = 'section nav article aside header footer'.split(' ');
for (var e = 0; e < elements.length; e++) {
var passed = false;
try {
var element = document.createElement(elements[e]);
document.body.appendChild(element);
try {
passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement) && this.isBlock(element) && this.closesImplicitly(elements[e]);
} catch(error) {
}
document.body.removeChild(element);
} catch(error) {
}
group.setItem({
id: elements[e],
passed: passed,
value: 1
});
}
var group = this.section.getGroup({
id: 'grouping'
});
var elements = 'main figure figcaption'.split(' ');
for (var e = 0; e < elements.length; e++) {
var passed = false;
try {
var element = document.createElement(elements[e]);
document.body.appendChild(element);
try {
passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement) && this.isBlock(element) && (elements[e] != 'figure' || this.closesImplicitly(elements[e]));
} catch(error) {
}
document.body.removeChild(element);
} catch(error) {
}
group.setItem({
id: elements[e],
passed: passed,
value: 1
});
}
var element = document.createElement('ol');
group.setItem({
id: 'ol',
passed: 'reversed' in element,
value: 1
});
var group = this.section.getGroup({
id: 'semantic'
});
var element = document.createElement('a');
group.setItem({
id: 'download',
passed: 'download' in element,
value: 1
});
group.setItem({
id: 'ping',
passed: 'ping' in element,
value: 1
});
var passed = false;
try {
var element = document.createElement('mark');
document.body.appendChild(element);
try {
passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement) && (color = this.getStyle(element, 'background-color')) && (color != 'transparent');
} catch(error) {
}
document.body.removeChild(element);
} catch(error) {
}
group.setItem({
id: 'mark',
passed: passed,
value: 1
});
var container = document.createElement('div');
document.body.appendChild(container);
container.innerHTML = "<ruby id='ruby'><rp id='rp'></rp><rt id='rt'></rt></ruby>";
var rubyElement = document.getElementById('ruby');
var rtElement = document.getElementById('rt');
var rpElement = document.getElementById('rp');
var rubySupport = false;
var rtSupport = false;
var rpSupport = false;
try {
rubySupport = rubyElement && rubyElement instanceof HTMLElement && !(element instanceof HTMLUnknownElement);
rtSupport = rtElement && rtElement instanceof HTMLElement && !(element instanceof HTMLUnknownElement);
rpSupport = rpElement && rpElement instanceof HTMLElement && !(element instanceof HTMLUnknownElement) && this.isHidden(rpElement);
} catch(error) {
}
document.body.removeChild(container);
group.setItem({
id: 'ruby',
passed: rubySupport && rtSupport && rpSupport,
value: 3
});
var passed = false;
try {
var element = document.createElement('time');
try {
passed = typeof HTMLTimeElement != 'undefined' && element instanceof HTMLTimeElement;
} catch(error) {
}
} catch(error) {
}
group.setItem({
id: 'time',
passed: passed,
value: 1
});
var passed = false;
try {
var element = document.createElement('data');
try {
passed = typeof HTMLDataElement != 'undefined' && element instanceof HTMLDataElement;
} catch(error) {
}
} catch(error) {
}
group.setItem({
id: 'data',
passed: passed,
value: 1
});
var passed = false;
try {
var element = document.createElement('wbr');
try {
passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement);
} catch(error) {
}
} catch(error) {
}
group.setItem({
id: 'wbr',
passed: passed,
value: 1
});
var group = this.section.getGroup({
id: 'interactive'
});
var passed = false;
try {
var element = document.createElement('details');
element.innerHTML = '<summary>a</summary>b';
document.body.appendChild(element);
var height = element.offsetHeight;
element.open = true;
passed = height != element.offsetHeight;
document.body.removeChild(element);
} catch(error) {
}
group.setItem({
id: 'details',
passed: passed,
value: 1
});
var passed = false;
try {
var element = document.createElement('summary');
document.body.appendChild(element);
try {
passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement);
} catch(error) {
}
document.body.removeChild(element);
} catch(error) {
}
group.setItem({
id: 'summary',
passed: passed,
value: 1
});
var passed = legacy = false;
try {
var element = document.createElement('menu');
document.body.appendChild(element);
try {
legacy = typeof HTMLMenuElement != 'undefined' && element instanceof HTMLMenuElement && 'type' in element;
} catch(error) {
}
// Check default type
if (legacy && element.type != 'list') legacy = false;
// Check type sanitization
try {
element.type = 'foobar';
} catch(error) {
}
if (legacy && element.type == 'foobar') legacy = false;
// Check if correct type sticks
try {
element.type = 'list';
} catch(error) {
legacy = false;
}
if (legacy && element.type != 'list') legacy = false;
document.body.removeChild(element);
} catch(error) {
}
try {
var element = document.createElement('menu');
document.body.appendChild(element);
try {
passed = typeof HTMLMenuElement != 'undefined' && element instanceof HTMLMenuElement && 'type' in element;
} catch(error) {
}
// Check default type
if (passed && element.type != 'toolbar') passed = false;
// Check type sanitization
try {
element.type = 'foobar';
} catch(error) {
}
if (passed && element.type == 'foobar') passed = false;
// Check if correct type sticks
try {
element.type = 'toolbar';
} catch(error) {
passed = false;
}
if (passed && element.type != 'toolbar') passed = false;
document.body.removeChild(element);
} catch(error) {
}
group.setItem({
id: 'menutoolbar',
passed: passed ? YES : legacy ? YES | OLD : NO,
value: 1
});
var passed = legacy = false;
try {
var element = document.createElement('menu');
document.body.appendChild(element);
try {
legacy = typeof HTMLMenuElement != 'undefined' && element instanceof HTMLMenuElement && 'type' in element;
} catch(error) {
}
// Check if correct type sticks
try {
element.type = 'context';
} catch(error) {
legacy = false;
}
if (legacy && element.type != 'context') legacy = false;
if (legacy) {
var item = document.createElement('menuitem');
element.appendChild(item);
if (typeof HTMLMenuItemElement == 'undefined' || ! item instanceof HTMLMenuItemElement) legacy = false;
}
document.body.removeChild(element);
} catch(error) {
}
try {
var element = document.createElement('menu');
document.body.appendChild(element);
try {
passed = typeof HTMLMenuElement != 'undefined' && element instanceof HTMLMenuElement && 'type' in element;
} catch(error) {
}
try {
element.type = 'popup';
} catch(error) {
}
// Check default type
var second = document.createElement('menu');
element.appendChild(second);
if (passed && second.type != 'popup') passed = false;
element.removeChild(second);
// Check type sanitization
try {
element.type = 'foobar';
} catch(error) {
}
if (passed && element.type == 'foobar') passed = false;
// Check if correct type sticks
try {
element.type = 'popup';
} catch(error) {
passed = false;
}
if (passed && element.type != 'popup') passed = false;
if (passed) {
var item = document.createElement('menuitem');
element.appendChild(item);
if (typeof HTMLMenuItemElement == 'undefined' || ! item instanceof HTMLMenuItemElement) passed = false;
}
document.body.removeChild(element);
} catch(error) {
}
group.setItem({
id: 'menupopup',
passed: passed ? YES : legacy ? YES | OLD : NO,
value: 2
});
var passed = false;
try {
var element = document.createElement('dialog');
try {
passed = typeof HTMLDialogElement != 'undefined' && element instanceof HTMLDialogElement;
} catch(error) {
}
} catch(error) {
}
group.setItem({
id: 'dialog',
passed: passed,
value: 1
});
var element = document.createElement('div');
this.section.setItem({
id: 'hidden',
passed: 'hidden' in element,
value: 1
});
var group = this.section.getGroup({
id: 'dynamic'
});
var element = document.createElement('div');
group.setItem({
id: 'outerHTML',
passed: !!('outerHTML' in element),
value: 1
});
group.setItem({
id: 'insertAdjacentHTML',
passed: !!('insertAdjacentHTML' in element),
value: 1
});
},
getStyle: function(elem, name) {
function camelCase(str){
return str.replace(/-\D/g, function(match){
return match.charAt(1).toUpperCase()
})
}
if (elem.style[name]) {
return elem.style[name];
} else if (elem.currentStyle) {
return elem.currentStyle[camelCase(name)];
}
else if (document.defaultView && document.defaultView.getComputedStyle) {
s = document.defaultView.getComputedStyle(elem, "");
return s && s.getPropertyValue(name);
} else {
return null;
}
},
isBlock: function(element) {
return this.getStyle(element, 'display') == 'block';
},
closesImplicitly: function(name) {
var foo = document.createElement('div');
foo.innerHTML = '<p><' + name + '></' + name + '>';
return foo.childNodes.length == 2;
},
isHidden: function(element) {
return this.getStyle(element, 'display') == 'none';
}
};
function testForm (results) { this.initialize(results); }
testForm.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'form'
});
var element = this.createInput('text');
var baseline = { field: getRenderedStyle(element.field), wrapper: getRenderedStyle(element.wrapper) };
this.removeInput(element);
/* input type=text */
var group = this.section.getGroup({
id: 'text'
});
var element = this.createInput('text');
group.setItem({
id: 'element',
passed: element.field.type == 'text'
});
group.setItem({
id: 'selection',
passed: 'selectionDirection' in element.field,
value: 2
});
this.removeInput(element);
/* input type=search */
var group = this.section.getGroup({
id: 'search'
});
var element = this.createInput('search');
group.setItem({
id: 'element',
passed: element.field.type == 'search',
value: 2
});
this.removeInput(element);
/* input type=tel */
var group = this.section.getGroup({
id: 'tel'
});
var element = this.createInput('tel');
group.setItem({
id: 'element',
passed: element.field.type == 'tel',
value: 2
});
this.removeInput(element);
/* input type=url */
var group = this.section.getGroup({
id: 'url'
});
var element = this.createInput('url');
var validation = false;
if ('validity' in element.field) {
validation = true;
element.field.value = "foo";
validation &= !element.field.validity.valid
element.field.value = "http://foo.org";
validation &= element.field.validity.valid
}
group.setItem({
id: 'element',
passed: element.field.type == 'url',
value: 2,
required: true
});
group.setItem({
id: 'validation',
passed: validation,
required: true
});
this.removeInput(element);
/* input type=email */
var group = this.section.getGroup({
id: 'email'
});
var element = this.createInput('email');
var validation = false;
if ('validity' in element.field) {
validation = true;
element.field.value = "foo";
validation &= !element.field.validity.valid
element.field.value = "foo@bar.org";
validation &= element.field.validity.valid
}
group.setItem({
id: 'element',
passed: element.field.type == 'email',
value: 2,
required: true
});
group.setItem({
id: 'validation',
passed: validation,
required: true
});
this.removeInput(element);
/* input type=date, month, week, time, datetime and datetime-local */
var types = ['date', 'month', 'week', 'time', 'datetime', 'datetime-local'];
for (var t = 0; t < types.length; t++) {
var group = this.section.getGroup({
id: types[t]
});
var element = this.createInput(types[t]);
element.field.value = "foobar";
var sanitization = element.field.value == '';
var minimal = element.field.type == types[t];
group.setItem({
id: 'element',
passed: minimal,
value: types[t] != 'datetime' ? 2 : 0,
required: true
});
group.setItem({
id: 'ui',
passed: minimal && sanitization, // Testing UI reliably is not possible, so we assume if sanitization is support we also have a UI and use the blacklist to make corrections
value: types[t] != 'datetime' ? 2 : 0
});
group.setItem({
id: 'sanitization',
passed: minimal && sanitization,
required: true
});
group.setItem({
id: 'min',
passed: minimal && 'min' in element.field,
required: true
});
group.setItem({
id: 'max',
passed: minimal && 'max' in element.field,
required: true
});
group.setItem({
id: 'step',
passed: minimal && 'step' in element.field,
required: true
});
group.setItem({
id: 'stepDown',
passed: minimal && 'stepDown' in element.field,
required: true
});
group.setItem({
id: 'stepUp',
passed: minimal && 'stepUp' in element.field,
required: true
});
if (t != 'datetime-local' && t != 'datetime') {
group.setItem({
id: 'valueAsDate',
passed: minimal && 'valueAsDate' in element.field,
required: true
});
}
group.setItem({
id: 'valueAsNumber',
passed: minimal && 'valueAsNumber' in element.field,
required: true
});
this.removeInput(element);
}
/* input type=number, range */
var types = ['number', 'range'];
for (var t = 0; t < types.length; t++) {
var group = this.section.getGroup({
id: types[t],
name: '<code>input type=' + types[t] + '</code>'
});
var element = this.createInput(types[t]);
element.field.value = "foobar";
var sanitization = element.field.value != 'foobar';
var validation = false;
if ('validity' in element.field) {
validation = true;
element.field.min = 40;
element.field.max = 50;
element.field.value = 100;
validation &= !element.field.validity.valid
element.field.value = 42;
validation &= element.field.validity.valid
}
var minimal = element.field.type == types[t];
group.setItem({
id: 'element',
passed: minimal,
value: 2,
required: true
});
group.setItem({
id: 'ui',
passed: minimal && sanitization, // Testing UI reliably is not possible, so we assume if sanitization is support we also have a UI and use the blacklist to make corrections
value: 2
});
group.setItem({
id: 'sanitization',
passed: minimal && sanitization,
required: true
});
if (types[t] != 'range') {
group.setItem({
id: 'validation',
passed: minimal && validation,
required: true
});
}
group.setItem({
id: 'min',
passed: minimal && 'min' in element.field,
required: true
});
group.setItem({
id: 'max',
passed: minimal && 'max' in element.field,
required: true
});
group.setItem({
id: 'step',
passed: minimal && 'step' in element.field,
required: true
});
group.setItem({
id: 'stepDown',
passed: minimal && 'stepDown' in element.field,
required: true
});
group.setItem({
id: 'stepUp',
passed: minimal && 'stepUp' in element.field,
required: true
});
group.setItem({
id: 'valueAsNumber',
passed: minimal && 'valueAsNumber' in element.field,
required: true
});
this.removeInput(element);
}
/* input type=color */
var group = this.section.getGroup({
id: 'color'
});
var element = this.createInput('color');
element.field.value = "foobar";
var sanitization = element.field.value != 'foobar';
group.setItem({
id: 'element',
passed: element.field.type == 'color',
value: 2
});
group.setItem({
id: 'ui',
passed: sanitization, // Testing UI reliably is not possible, so we assume if sanitization is support we also have a UI and use the blacklist to make corrections
value: 2
});
group.setItem({
id: 'sanitization',
passed: sanitization,
required: true
});
this.removeInput(element);
/* input type=checkbox */
var group = this.section.getGroup({
id: 'checkbox'
});
var element = this.createInput('checkbox');
group.setItem({
id: 'element',
passed: element.field.type == 'checkbox',
value: 0
});
group.setItem({
id: 'indeterminate',
passed: 'indeterminate' in element.field,
value: 1
});
this.removeInput(element);
/* input type=image */
var group = this.section.getGroup({
id: 'image'
});
var element = this.createInput('image');
element.field.style.display = 'inline-block';
var supportsWidth = 'width' in element.field;
var supportsHeight = 'height' in element.field;
element.field.setAttribute('width', '100');
element.field.setAttribute('height', '100');
group.setItem({
id: 'element',
passed: element.field.type == 'image',
value: 0
});
group.setItem({
id: 'width',
passed: supportsWidth && element.field.offsetWidth == 100,
value: 0
});
group.setItem({
id: 'height',
passed: supportsHeight && element.field.offsetHeight == 100,
value: 0
});
this.removeInput(element);
/* input type=file */
var group = this.section.getGroup({
id: 'file'
});
var element = this.createInput('file');
group.setItem({
id: 'element',
passed: element.field.type == 'file',
value: 0
});
group.setItem({
id: 'files',
passed: element.field.files && element.field.files instanceof FileList,
value: 1
});
group.setItem({
id: 'directory',
passed: 'directory' in element.field && window.Directory,
value: 1
});
this.removeInput(element);
/* textarea */
var group = this.section.getGroup({
id: 'textarea'
});
var element = document.createElement('textarea');
var passed = false;
try {
passed = typeof HTMLTextAreaElement != 'undefined' && element instanceof HTMLTextAreaElement;
} catch(error) {
}
group.setItem({
id: 'element',
passed: passed,
value: 0
});
group.setItem({
id: 'maxlength',
passed: 'maxLength' in element,
value: 1
});
group.setItem({
id: 'wrap',
passed: 'wrap' in element,
value: 1
});
/* select */
var group = this.section.getGroup({
id: 'select'
});
var element = document.createElement('select');
var passed = false;
try {
passed = typeof HTMLSelectElement != 'undefined' && element instanceof HTMLSelectElement;
} catch(error) {
}
group.setItem({
id: 'element',
passed: passed,
value: 0
});
group.setItem({
id: 'required',
passed: 'required' in element,
value: 1
});
/* fieldset */
var group = this.section.getGroup({
id: 'fieldset'
});
var element = document.createElement('fieldset');
var passed = false;
try {
passed = typeof HTMLFieldSetElement != 'undefined' && element instanceof HTMLFieldSetElement;
} catch(error) {
}
group.setItem({
id: 'element',
passed: passed,
value: 0
});
group.setItem({
id: 'elements',
passed: 'elements' in element,
value: 1
});
group.setItem({
id: 'disabled',
passed: 'disabled' in element,
value: 1
});
/* datalist */
var group = this.section.getGroup({
id: 'datalist'
});
var passed = false;
try {
var element = document.createElement('datalist');
try {
passed = (typeof HTMLDataListElement != 'undefined' && element instanceof HTMLDataListElement) || element.childNodes.length;
} catch(error) {
}
} catch(error) {
}
group.setItem({
id: 'element',
passed: passed,
value: 2,
required: true
});
var element = document.createElement('input');
group.setItem({
id: 'list',
passed: !!("list" in element),
required: true
});
/* keygen */
var group = this.section.getGroup({
id: 'keygen'
});
var element = document.createElement('div');
element.innerHTML = '<keygen>';
var passed = false;
try {
passed = typeof HTMLKeygenElement != 'undefined' && element.firstChild instanceof HTMLKeygenElement && 'challenge' in element.firstChild && 'keytype' in element.firstChild;
} catch(error) {
}
group.setItem({
id: 'element',
passed: passed,
value: 0,
required: true
});
group.setItem({
id: 'challenge',
passed: element.firstChild && 'challenge' in element.firstChild,
required: true
});
group.setItem({
id: 'keytype',
passed: element.firstChild && 'keytype' in element.firstChild,
required: true
});
/* output */
var group = this.section.getGroup({
id: 'output'
});
var passed = false;
try {
var element = document.createElement('output');
try {
passed = typeof HTMLOutputElement != 'undefined' && element instanceof HTMLOutputElement;
} catch(error) {
}
} catch(error) {
}
group.setItem({
id: 'element',
passed: passed,
value: 2
});
/* progress */
var group = this.section.getGroup({
id: 'progress'
});
var passed = false;
try {
var element = document.createElement('progress');
try {
passed = typeof HTMLProgressElement != 'undefined' && element instanceof HTMLProgressElement;
} catch(error) {
}
} catch(error) {
}
group.setItem({
id: 'element',
passed: passed,
value: 2
});
/* meter */
var group = this.section.getGroup({
id: 'meter'
});
var passed = false;
try {
var element = document.createElement('meter');
try {
passed = typeof HTMLMeterElement != 'undefined' && element instanceof HTMLMeterElement;
} catch(error) {
}
} catch(error) {
}
group.setItem({
id: 'element',
passed: passed,
value: 2
});
var group = this.section.getGroup({
id: 'validation'
});
var element = document.createElement('input');
var props = 'pattern required'.split(' ');
for (var p = 0; p < props.length; p++) {
group.setItem({
id: props[p],
passed: !!(props[p] in element),
value: 1
});
}
var group = this.section.getGroup({
id: 'association',
value: 2
});
var field = document.createElement('input');
field.id = "a";
document.body.appendChild(field);
var label = document.createElement("label");
label.setAttribute('for', 'a');
document.body.appendChild(label);
group.setItem({
id: 'control',
passed: label.control == field
});
document.body.removeChild(field);
document.body.removeChild(label);
var element = document.createElement('div');
document.body.appendChild(element);
element.innerHTML = '<form id="form"></form><input form="form">';
group.setItem({
id: 'form',
passed: element.lastChild.form == element.firstChild
});
document.body.removeChild(element);
var props = 'formAction formEnctype formMethod formNoValidate formTarget'.split(' ');
var element = document.createElement('input');
for (var p = 0; p < props.length; p++) {
group.setItem({
id: props[p],
passed: !!(props[p] in element)
});
}
var element = document.createElement('input');
document.body.appendChild(element);
element.id = "testFormInput";
var label = document.createElement("label");
label.setAttribute('for', 'testFormInput');
document.body.appendChild(label);
group.setItem({
id: 'labels',
passed: (!!element.labels && element.labels.length == 1 && element.labels[0] == label)
});
document.body.removeChild(label);
document.body.removeChild(element);
var group = this.section.getGroup({
id: 'other',
value: 2
});
var element = document.createElement('input');
group.setItem({
id: 'autofocus',
passed: !!('autofocus' in element)
});
var props = 'autocomplete placeholder multiple dirName'.split(' ');
for (var p = 0; p < props.length; p++) {
var prop = props[p].toLowerCase();
group.setItem({
id: prop,
passed: !!(props[p] in element),
required: props[p] != 'dirName'
});
}
var selectors = "valid invalid optional required in-range out-of-range read-write read-only".split(" ");
var res = [NO, NO, NO, NO, NO, NO, NO, NO];
var unknown = false;
/* At this time we are not testing enabled, disabled, checked and indeterminate,
because these selectors are part of the CSS 3 Selector specification and
universally implemented, see http://www.css3.info/selectors-test/
*/
if ('querySelector' in document) {
var element = document.createElement('input');
element.id = 'testFormInput';
element.setAttribute("type", "text");
document.body.appendChild(element);
try {
res[0] = !!document.querySelector("#testFormInput:valid");
} catch(e) {
res[0] = NO;
}
try {
res[6] = !!document.querySelector("#testFormInput:read-write");
} catch(e) {
res[6] = NO;
try {
res[6] = document.querySelector("#testFormInput:-moz-read-write") ? YES | PREFIX : NO;
} catch(e) {
}
}
if ("validity" in element && "setCustomValidity" in element) {
element.setCustomValidity("foo");
try {
res[1] = !!document.querySelector("#testFormInput:invalid");
} catch(e) {
res[1] = NO;
}
} else {
res[1] = NO;
}
try {
res[2] = !!document.querySelector("#testFormInput:optional");
} catch(e) {
res[2] = NO;
}
element.setAttribute("required", "true");
try {
res[3] = !!document.querySelector("#testFormInput:required");
} catch(e) {
res[3] = NO;
}
try {
element.setAttribute("type", "number");
element.setAttribute("min", "10");
element.setAttribute("max", "20");
element.setAttribute("value", "15");
res[4] = !!document.querySelector("#testFormInput:in-range");
} catch(e) {
res[4] = NO;
}
try {
element.setAttribute("type", "number");
element.setAttribute("min", "10");
element.setAttribute("max", "20");
element.setAttribute("value", "25");
res[5] = !!document.querySelector("#testFormInput:out-of-range");
} catch(e) {
res[5] = NO;
}
document.body.removeChild(element);
var element = document.createElement('input');
element.id = 'testFormInput';
element.setAttribute("type", "text");
element.setAttribute("readonly", "readonly");
document.body.appendChild(element);
try {
res[7] = !!document.querySelector("#testFormInput:read-only");
} catch(e) {
res[7] = NO;
try {
res[7] = document.querySelector("#testFormInput:-moz-read-only") ? YES | PREFIX : NO;
} catch(e) {
}
}
document.body.removeChild(element);
} else {
unknown = true;
}
var group = this.section.getGroup({
id: 'selectors',
value: 2
});
for (var i = 0; i < selectors.length; i++) {
group.setItem({
id: selectors[i],
passed: res[i],
custom: unknown ? 'unknown' : false
});
}
var group = this.section.getGroup({
id: 'events',
value: 2
});
var inputItem = group.setItem({
id: 'oninput',
passed: isEventSupported('input')
});
var changeItem = group.setItem({
id: 'onchange',
passed: isEventSupported('change')
});
var invalidItem = group.setItem({
id: 'oninvalid',
passed: isEventSupported('invalid')
});
try {
inputItem.startBackground();
changeItem.startBackground();
var event = document.createEvent("KeyboardEvent");
if (event.initKeyEvent) {
event.initKeyEvent("keypress", false, true, null, false, false, false, false, null, 65);
var input = document.createElement('input');
input.style.position = 'fixed';
input.style.left = '-500px';
input.style.top = '0px';
document.body.appendChild(input);
input.addEventListener('input', function() {
inputItem.update({
'passed': true
});
inputItem.stopBackground();
}, true);
input.addEventListener('change', function() {
changeItem.update({
'passed': true
});
changeItem.stopBackground();
}, true);
input.focus();
input.dispatchEvent(event);
input.blur();
window.setTimeout(function() {
document.body.removeChild(input);
inputItem.stopBackground();
changeItem.stopBackground();
}, 1000);
} else {
inputItem.stopBackground();
changeItem.stopBackground();
}
} catch(e) {
inputItem.stopBackground();
changeItem.stopBackground();
}
var group = this.section.getGroup({
id: 'formvalidation'
});
var element = document.createElement('form');
group.setItem({
id: 'checkValidity',
passed: 'checkValidity' in element,
value: 3
});
group.setItem({
id: 'noValidate',
passed: 'noValidate' in element,
value: 1
});
},
createInput: function(type) {
var wrapper = document.createElement('div');
document.body.appendChild(wrapper)
var field = document.createElement('input');
wrapper.appendChild(field);
try {
field.setAttribute('type', type);
} catch(e) {
}
/* Make sure our field is position absolutely for CSS style comparison */
wrapper.style.position = 'absolute';
wrapper.style.display = 'inline-block';
wrapper.style.top = '0px';
wrapper.style.left = '0px';
return { field: field, wrapper: wrapper };
},
removeInput: function(e) {
document.body.removeChild(e.wrapper);
}
};
function testInteraction (results) { this.initialize(results); }
testInteraction.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'interaction'
});
/* Drag and drop */
var element = document.createElement('div');
var group = this.section.getGroup({
id: 'dragdrop.attributes'
});
group.setItem({
id: 'draggable',
passed: 'draggable' in element,
value: 1
});
group.setItem({
id: 'dropzone',
passed: 'dropzone' in element ? YES : 'webkitdropzone' in element || 'mozdropzone' in element || 'msdropzone' in element || 'odropzone' in element ? YES | PREFIX : NO,
value: 1
});
var group = this.section.getGroup({
id: 'dragdrop.events'
});
/* We need to check if the draggable attribute is supported, because older versions of IE do
support the incompatible versions of the events below. IE 9 and up do support the HTML5
events in combination with the draggable attribute */
var supported = 'draggable' in element;
group.setItem({
id: 'ondrag',
passed: isEventSupported('drag') && supported
});
group.setItem({
id: 'ondragstart',
passed: isEventSupported('dragstart') && supported
});
group.setItem({
id: 'ondragenter',
passed: isEventSupported('dragenter') && supported
});
group.setItem({
id: 'ondragover',
passed: isEventSupported('dragover') && supported
});
group.setItem({
id: 'ondragleave',
passed: isEventSupported('dragleave') && supported
});
group.setItem({
id: 'ondragend',
passed: isEventSupported('dragend') && supported
});
group.setItem({
id: 'ondrop',
passed: isEventSupported('drop') && supported
});
/* Content editable */
var element = document.createElement('div');
var group = this.section.getGroup({
id: 'editing.elements'
});
group.setItem({
id: 'contentEditable',
passed: 'contentEditable' in element,
value: 5
});
group.setItem({
id: 'isContentEditable',
passed: 'isContentEditable' in element,
value: 1
});
var group = this.section.getGroup({
id: 'editing.documents'
});
group.setItem({
id: 'designMode',
passed: 'designMode' in document,
value: 1
});
var group = this.section.getGroup({
id: 'editing.apis',
value: 2
});
group.setItem({
id: 'execCommand',
passed: 'execCommand' in document
});
group.setItem({
id: 'queryCommandEnabled',
passed: 'queryCommandEnabled' in document
});
group.setItem({
id: 'queryCommandIndeterm',
passed: 'queryCommandIndeterm' in document
});
group.setItem({
id: 'queryCommandState',
passed: 'queryCommandState' in document
});
group.setItem({
id: 'queryCommandSupported',
passed: 'queryCommandSupported' in document
});
group.setItem({
id: 'queryCommandValue',
passed: 'queryCommandValue' in document
});
var selectors = "read-write read-only".split(" ");
var res = [false, false];
var unknown = false;
if ('querySelector' in document) {
var element = document.createElement('div');
element.id = 'testDivElement';
element.contentEditable = true;
document.body.appendChild(element);
var nested = document.createElement('div');
nested.id = 'testDivNested';
nested.contentEditable = false;
element.appendChild(nested);
try {
res[0] = document.querySelector("#testDivElement:read-write") == element;
} catch(e) {
res[0] = NO;
try {
res[0] = document.querySelector("#testDivElement:-moz-read-write") == element ? YES | PREFIX : NO;
} catch(e) {
}
}
try {
res[1] = document.querySelector("#testDivNested:read-only") == nested;
} catch(e) {
res[1] = NO;
try {
res[1] = document.querySelector("#testDivNested:-moz-read-only") == nested ? YES | PREFIX : NO;
} catch(e) {
}
}
document.body.removeChild(element);
} else {
unknown = true;
}
var group = this.section.getGroup({
id: 'editing.selectors'
});
for (var i = 0; i < selectors.length; i++) {
group.setItem({
id: selectors[i],
passed: res[i],
value: 1,
custom: unknown ? 'unknown' : false
});
}
this.section.setItem({
id: 'clipboard',
passed: !!('ClipboardEvent' in window),
value: 5
});
this.section.setItem({
id: 'spellcheck',
passed: !!('spellcheck' in element),
value: 2
});
}
};
function testHistory (results) { this.initialize(results); }
testHistory.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'history'
});
this.section.setItem({
id: 'history',
passed: !!(window.history && history.pushState),
value: 10
});
}
};
function testMicrodata (results) { this.initialize(results); }
testMicrodata.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'microdata'
});
var container = document.createElement('div');
container.innerHTML = '<div id="microdataItem" itemscope itemtype="http://example.net/user"><p>My name is <span id="microdataProperty" itemprop="name">Elizabeth</span>.</p></div>';
document.body.appendChild(container);
var item = document.getElementById('microdataItem');
var property = document.getElementById('microdataProperty');
var r = true;
// Check the element that contains the property
r = r && !!('itemValue' in property) && property.itemValue == 'Elizabeth';
// Check the element that is the item
r = r && !!('properties' in item) && item.properties['name'][0].itemValue == 'Elizabeth';
// Check the getItems method
if (!!document.getItems) {
var user = document.getItems('http://example.net/user')[0];
r = r && user.properties['name'][0].itemValue == 'Elizabeth';
}
document.body.removeChild(container);
this.section.setItem({
id: 'microdata',
passed: r,
value: 0
});
}
};
function testOffline (results) { this.initialize(results); }
testOffline.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'offline'
});
this.section.setItem({
id: 'applicationCache',
passed: !!window.applicationCache,
value: 10
});
this.section.setItem({
id: 'serviceWorkers',
passed: !!window.navigator.serviceWorker,
value: 10
});
this.section.setItem({
id: 'registerProtocolHandler',
passed: !!window.navigator.registerProtocolHandler,
value: 2
});
this.section.setItem({
id: 'registerContentHandler',
passed: !!window.navigator.registerContentHandler,
value: 2
});
var passed = false;
try {
passed = !!(window.external && typeof window.external.AddSearchProvider != 'undefined' && typeof window.external.IsSearchProviderInstalled != 'undefined');
} catch(e) {
}
this.section.setItem({
id: 'addSearchProvider',
passed: passed,
value: 1
});
}
};
function testSecurity (results) { this.initialize(results); }
testSecurity.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'security'
});
var passed = NO;
try {
var crypto = window.crypto || window.webkitCrypto || window.mozCrypto || window.msCrypto || window.oCrypto;
var available = window.crypto ? YES : window.webkitCrypto || window.mozCrypto || window.msCrypto || window.oCrypto ? YES | PREFIX : NO;
passed = !!crypto && 'subtle' in crypto ? available : NO;
} catch(e) {
}
this.section.setItem({
id: 'crypto',
passed: passed,
value: 5
});
this.section.setItem({
id: 'csp10',
passed: !(function() { try { return eval('true'); } catch (e) {} return false; })(),
value: 3
});
this.section.setItem({
id: 'csp11',
passed: 'SecurityPolicyViolationEvent' in window,
value: 2
});
this.section.setItem({
id: 'cors',
passed: !!(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()),
value: 5
});
this.section.setItem({
id: 'postMessage',
passed: !!window.postMessage,
value: 5
});
this.section.setItem({
id: 'sandbox',
passed: 'sandbox' in document.createElement('iframe'),
value: 5
});
this.section.setItem({
id: 'srcdoc',
passed: 'srcdoc' in document.createElement('iframe'),
value: 5
});
}
};
function testGeolocation (results) { this.initialize(results); }
testGeolocation.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'location'
});
this.section.setItem({
id: 'geolocation',
passed: !!navigator.geolocation,
value: 15
});
this.section.setItem({
id: 'orientation',
passed: !!window.DeviceOrientationEvent,
value: 3
});
this.section.setItem({
id: 'motion',
passed: !!window.DeviceMotionEvent,
value: 2
});
}
};
function testWebGL (results) { this.initialize(results); }
testWebGL.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'webgl'
});
var element = document.createElement('canvas');
var contexts = ['webgl', 'ms-webgl', 'experimental-webgl', 'moz-webgl', 'opera-3d', 'webkit-3d', 'ms-3d', '3d'];
var context = '';
var passed = false;
for (var b = -1, len = contexts.length; ++b < len;) {
try {
if (element.getContext(contexts[b])) {
context = contexts[b];
passed = true;
break;
};
} catch(e){ }
}
this.section.setItem({
id: 'context',
passed: passed ? (context == 'webgl' ? YES : YES | PREFIX) : NO,
value: 20
});
}
};
function testCommunication (results) { this.initialize(results); }
testCommunication.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'communication'
});
this.section.setItem({
id: 'beacon',
passed: 'sendBeacon' in navigator,
value: 2
});
this.section.setItem({
id: 'eventSource',
passed: 'EventSource' in window,
value: 5
});
this.section.setItem({
id: 'fetch',
passed: 'Promise' in window && typeof window.fetch === 'function' && window.fetch('') instanceof Promise,
value: 6
});
this.section.setItem({
id: 'xmlhttprequest2.upload',
passed: window.XMLHttpRequest && 'upload' in new XMLHttpRequest(),
value: 5
});
var group = this.section.getGroup({
id: 'xmlhttprequest2.response'
});
var item = group.setItem({
id: 'text',
passed: false,
value: 1
});
this.testResponseTypeText(item);
var item = group.setItem({
id: 'document',
passed: false,
value: 2
});
this.testResponseTypeDocument(item);
var item = group.setItem({
id: 'array',
passed: false,
value: 2
});
this.testResponseTypeArrayBuffer(item);
var item = group.setItem({
id: 'blob',
passed: false,
value: 2
});
this.testResponseTypeBlob(item);
var websocket = window.WebSocket || window.MozWebSocket;
var passed = 'WebSocket' in window ? YES : 'MozWebSocket' in window ? YES | PREFIX : NO;
if (websocket && websocket.CLOSING !== 2) passed |= OLD;
this.section.setItem({
id: 'websocket.basic',
passed: passed,
value: 10
});
var passed = false;
var protocol = 'https:' == location.protocol ? 'wss' : 'ws';
if ("WebSocket" in window) {
if ("binaryType" in WebSocket.prototype) {
passed = true;
}
else {
try {
passed = !!(new WebSocket(protocol+'://.').binaryType);
} catch (e) {
}
}
}
this.section.setItem({
id: 'websocket.binary',
passed: passed,
value: 5
});
},
testResponseTypeDocument: function(item) {
if (!window.XMLHttpRequest) return;
var xhr = new window.XMLHttpRequest();
if (typeof xhr.responseType == 'undefined') return;
var done = false;
xhr.onreadystatechange = function() {
if (this.readyState == 4 && !done) {
done = true;
passed = false;
try {
passed = !!(this.responseXML && this.responseXML.title && this.responseXML.title == "&&<");
} catch(e) {
}
item.stopBackground();
item.update({
'passed': passed
});
}
}
try {
item.startBackground();
xhr.open("GET", "/detect.html?" + Math.random().toString(36).substr(2, 5));
xhr.responseType = "document";
xhr.send();
} catch (e) {
item.stopBackground();
}
},
testResponseTypeText: function(item) {
if (!window.XMLHttpRequest) return;
var xhr = new window.XMLHttpRequest();
if (typeof xhr.responseType == 'undefined') return;
var done = false;
xhr.onreadystatechange = function() {
if (this.readyState == 4 && !done) {
done = true;
passed = false;
try {
passed = !!(this.responseText); // && this.responseText == '<title>&amp;&<</title>');
} catch(e) {
}
item.stopBackground();
item.update({
'passed': passed
});
}
}
try {
item.startBackground();
xhr.open("GET", "/detect.html?" + Math.random().toString(36).substr(2, 5));
xhr.responseType = "text";
xhr.send();
} catch (e) {
item.stopBackground();
}
},
testResponseTypeBlob: function(item) {
if (!window.XMLHttpRequest || !window.Blob) return;
var xhr = new window.XMLHttpRequest();
if (typeof xhr.responseType == 'undefined') return;
var done = false;
xhr.onreadystatechange = function() {
if (this.readyState == 4 && !done) {
done = true;
passed = false;
try {
passed = !!(this.response && this.response instanceof Blob);
} catch(e) {
}
item.stopBackground();
item.update({
'passed': passed
});
}
}
try {
item.startBackground();
xhr.open("GET", "/detect.html?" + Math.random().toString(36).substr(2, 5));
xhr.responseType = "blob";
xhr.send();
} catch (e) {
item.stopBackground();
}
},
testResponseTypeArrayBuffer: function(item) {
if (!window.XMLHttpRequest || !window.ArrayBuffer) return;
var xhr = new window.XMLHttpRequest();
if (typeof xhr.responseType == 'undefined') return;
var done = false;
xhr.onreadystatechange = function() {
if (this.readyState == 4 && !done) {
done = true;
passed = false;
try {
passed = !!(this.response && this.response instanceof ArrayBuffer);
} catch(e) {
}
item.stopBackground();
item.update({
'passed': passed
});
}
}
try {
item.startBackground();
xhr.open("GET", "/detect.html?" + Math.random().toString(36).substr(2, 5));
xhr.responseType = "arraybuffer";
xhr.send();
} catch (e) {
item.stopBackground();
}
}
};
function testStreams (results) { this.initialize(results); }
testStreams.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'streams'
});
this.section.setItem({
id: 'streams.readable',
passed: 'ReadableStream' in window,
value: 3
});
this.section.setItem({
id: 'streams.writeable',
passed: 'WriteableStream' in window,
value: 2
});
}
};
function testFiles (results) { this.initialize(results); }
testFiles.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'files'
});
this.section.setItem({
id: 'fileReader',
passed: 'FileReader' in window,
value: 7
});
this.section.setItem({
id: 'fileReader.blob',
passed: 'Blob' in window,
value: 2
});
this.section.setItem({
id: 'fileReader.dataURL',
passed: 'FileReader' in window && 'readAsDataURL' in (new FileReader()),
value: 2
});
this.section.setItem({
id: 'fileReader.arraybuffer',
passed: 'FileReader' in window && 'readAsArrayBuffer' in (new FileReader()),
value: 2
});
this.section.setItem({
id: 'fileReader.objectURL',
passed: 'URL' in window && 'createObjectURL' in URL,
value: 2
});
this.section.setItem({
id: 'fileSystem',
passed: !! window.requestFileSystem ? YES : !! window.webkitRequestFileSystem || !! window.mozRequestFileSystem || !! window.oRequestFileSystem || !! window.msRequestFileSystem ? YES | PREFIX : NO,
value: 0
});
this.section.setItem({
id: 'getFileSystem',
passed: !! navigator.getFileSystem ? YES : !! navigator.webkitGetFileSystem || !! navigator.mozGetFileSystem || !! window.msGetFileSystem ? YES | PREFIX : NO,
value: 0
});
}
};
function testStorage (results) { this.initialize(results); }
testStorage.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'storage'
});
/* Key-value storage */
this.section.setItem({
id: 'sessionStorage',
passed: 'sessionStorage' in window && window.sessionStorage != null,
value: 5
});
var passed = false;
try {
passed = 'localStorage' in window && window.localStorage != null
} catch(e) {
/* If we get a security exception we know the feature exists, but cookies are disabled */
if (e.name == 'NS_ERROR_DOM_SECURITY_ERR') {
passed = true;
}
}
this.section.setItem({
id: 'localStorage',
passed: passed,
value: 5
});
/* IndexedDB */
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.moz_indexedDB || window.oIndexedDB || window.msIndexedDB;
var passed = !! window.indexedDB ? YES : !! window.webkitIndexedDB || !! window.mozIndexedDB || !! window.moz_indexedDB || !! window.oIndexedDB || !! window.msIndexedDB ? YES | PREFIX : NO;
if (indexedDB && ! 'deleteDatabase' in indexedDB) passed != BUGGY;
this.section.setItem({
id: 'indexedDB.basic',
passed: passed,
value: 21
});
var blobitem = this.section.setItem({
id: 'indexedDB.blob',
passed: false,
value: 2
});
var arrayitem = this.section.setItem({
id: 'indexedDB.arraybuffer',
passed: false,
value: 2
});
if (indexedDB && 'deleteDatabase' in indexedDB) {
if (console && console.log) console.log('IndexedDB: starting tests');
try {
blobitem.startBackground();
arrayitem.startBackground();
if (console && console.log) console.log('IndexedDB: delete existing database (if exists)');
var request = indexedDB.deleteDatabase('html5test');
request.onerror = function(e) {
if (console && console.log) console.log('IndexedDB: error, could not delete database', e);
blobitem.stopBackground();
arrayitem.stopBackground();
};
request.onsuccess = function () {
var request = indexedDB.open('html5test', 1);
if (console && console.log) console.log('IndexedDB: opening new database');
request.onupgradeneeded = function() {
if (console && console.log) console.log('IndexedDB: creating objectStore');
request.result.createObjectStore("store");
};
request.onerror = function(event) {
if (console && console.log) console.log('IndexedDB: error opening database', event);
blobitem.stopBackground();
arrayitem.stopBackground();
};
request.onsuccess = function() {
if (console && console.log) console.log('IndexedDB: database opened');
var db = request.result;
try {
db.transaction("store", "readwrite").objectStore("store").put(new Blob(), "key");
if (console && console.log) console.log('IndexedDB: objectStore with Blob passed');
blobitem.update({
passed: true
});
} catch (e) {
if (console && console.log) console.log('IndexedDB: objectStore with Blob failed');
}
try {
db.transaction("store", "readwrite").objectStore("store").put(new ArrayBuffer(), "key");
if (console && console.log) console.log('IndexedDB: objectStore with ArrayBuffer passed');
arrayitem.update({
passed: true
});
} catch (e) {
if (console && console.log) console.log('IndexedDB: objectStore with ArrayBuffer failed');
}
blobitem.stopBackground();
arrayitem.stopBackground();
db.close();
indexedDB.deleteDatabase('html5test');
};
};
} catch (e) {
if (console && console.log) console.log('IndexedDB: exception reached during test', e);
blobitem.stopBackground();
arrayitem.stopBackground();
}
}
/* WebSQL */
this.section.setItem({
id: 'sqlDatabase',
passed: !!window.openDatabase,
value: 0,
award: ! indexedDB ? 5 : 0 /* only award points if IndexedDB is not implemented */
});
}
};
function testPerformance (results) { this.initialize(results); }
testPerformance.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'performance'
});
this.section.setItem({
id: 'worker',
passed: !!window.Worker,
value: 10
});
this.section.setItem({
id: 'sharedWorker',
passed: !!window.SharedWorker,
value: 2
});
var group = this.section.getGroup({
id: 'datatypes'
});
group.setItem({
id: 'ArrayBuffer',
passed: typeof ArrayBuffer != 'undefined',
value: 1
});
group.setItem({
id: 'Int8Array',
passed: typeof Int8Array != 'undefined',
value: 1
});
group.setItem({
id: 'Uint8Array',
passed: typeof Uint8Array != 'undefined',
value: 1
});
group.setItem({
id: 'Uint8ClampedArray',
passed: typeof Uint8ClampedArray != 'undefined',
value: 1
});
group.setItem({
id: 'Int16Array',
passed: typeof Int16Array != 'undefined',
value: 1
});
group.setItem({
id: 'Uint16Array',
passed: typeof Uint16Array != 'undefined',
value: 1
});
group.setItem({
id: 'Int32Array',
passed: typeof Int32Array != 'undefined',
value: 1
});
group.setItem({
id: 'Uint32Array',
passed: typeof Uint32Array != 'undefined',
value: 1
});
group.setItem({
id: 'Float32Array',
passed: typeof Float32Array != 'undefined',
value: 1
});
group.setItem({
id: 'Float64Array',
passed: typeof Float64Array != 'undefined',
value: 1
});
group.setItem({
id: 'DataView',
passed: typeof DataView != 'undefined',
value: 1
});
}
};
function testOutput (results) { this.initialize(results); }
testOutput.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'output'
});
this.section.setItem({
id: 'requestFullScreen',
passed: !! document.documentElement.requestFullscreen ? YES : !! document.documentElement.webkitRequestFullScreen || !! document.documentElement.mozRequestFullScreen || !! document.documentElement.msRequestFullscreen ? YES | PREFIX : NO,
value: 5
});
/* W3C standard is "new Notification()", WebKit pre-standard is "window.webkitNotifications.createNotification()", Gecko pre-standard is "window.navigator.mozNotification.createNotification()" */
this.section.setItem({
id: 'notifications',
passed: 'Notification' in window ? YES : 'webkitNotifications' in window || 'mozNotification' in window.navigator || 'oNotification' in window || 'msNotification' in window ? YES | PREFIX : NO,
value: 5
});
}
};
function testOther (results) { this.initialize(results); }
testOther.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'other'
});
this.section.setItem({
id: 'async',
passed: 'async' in document.createElement('script'),
value: 3
});
this.section.setItem({
id: 'defer',
passed: 'defer' in document.createElement('script'),
value: 1
});
this.section.setItem({
id: 'onerror',
passed: isEventSupported('error'),
value: 1
});
var executionevents = this.section.setItem({
id: 'executionevents',
passed: false,
value: 1
});
executionevents.startBackground();
var before = false;
var s = document.createElement('script');
s.src="data:text/javascript;charset=utf-8,window"
s.addEventListener('beforescriptexecute', function() {
before = true;
}, true);
s.addEventListener('afterscriptexecute', function() {
if (before) {
executionevents.update({
passed: true
});
}
executionevents.stopBackground();
}, true);
document.body.appendChild(s);
window.setTimeout(function() {
executionevents.stopBackground();
}, 500);
this.section.setItem({
id: 'base64',
passed: 'btoa' in window && 'atob' in window,
value: 1
});
this.section.setItem({
id: 'json',
passed: 'JSON' in window && 'parse' in JSON,
value: 2
});
this.section.setItem({
id: 'mutationObserver',
passed: 'MutationObserver' in window ? YES : 'WebKitMutationObserver' in window || 'MozMutationObserver' in window || 'oMutationObserver' in window || 'msMutationObserver' in window ? YES | PREFIX : NO,
value: 2
});
this.section.setItem({
id: 'url',
passed: 'URL' in window ? YES : 'WebKitURL' in window || 'MozURL' in window || 'oURL' in window || 'msURL' in window ? YES | PREFIX : NO,
value: 2
});
this.section.setItem({
id: 'encoding',
passed: 'TextEncoder' in window && 'TextDecoder' in window ? YES : NO,
value: 2
});
this.section.setItem({
id: 'i18n',
passed: 'Intl' in window ? YES : NO,
value: 2
});
var passed = 'Promise' in window ? YES | OLD : NO;
if ('Promise' in window &&
'resolve' in window.Promise &&
'reject' in window.Promise &&
'all' in window.Promise &&
'race' in window.Promise &&
(function() {
var resolve;
new window.Promise(function(r) { resolve = r; });
return typeof resolve === 'function';
}()))
{
passed = YES;
}
this.section.setItem({
id: 'promises',
passed: passed,
value: 3
});
this.section.setItem({
id: 'pagevisiblity',
passed: 'visibilityState' in document ? YES : 'webkitVisibilityState' in document || 'mozVisibilityState' in document || 'oVisibilityState' in document || 'msVisibilityState' in document ? YES | PREFIX : NO,
value: 2
});
this.section.setItem({
id: 'getSelection',
passed: !!window.getSelection,
value: 2
});
var element = document.createElement('div');
this.section.setItem({
id: 'scrollIntoView',
passed: !!element.scrollIntoView,
value: 1
});
}
};
function testAnimation (results) { this.initialize(results); }
testAnimation.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'animation'
});
this.section.setItem({
id: 'webanimation',
passed: 'animate' in document.createElement('div'),
value: 2
});
this.section.setItem({
id: 'requestAnimationFrame',
passed: !! window.requestAnimationFrame ? YES : !! window.webkitRequestAnimationFrame || !! window.mozRequestAnimationFrame || !! window.msRequestAnimationFrame || !! window.oRequestAnimationFrame ? YES | PREFIX : NO,
value: 5
});
}
};
function testComponents (results) { this.initialize(results); }
testComponents.prototype = {
initialize: function(results) {
this.section = results.getSection({
id: 'components'
});
this.section.setItem({
id: 'custom',
passed: 'registerElement' in document,
value: 4
});
this.section.setItem({
id: 'shadowdom',
passed: 'attachShadow' in document.createElement('div') ? YES : 'createShadowRoot' in document.createElement('div') || 'webkitCreateShadowRoot' in document.createElement('div') ? YES | OLD : NO,
value: 2
});
var passed = false;
try {
passed = 'content' in document.createElement('template');
} catch(error) {
}
this.section.setItem({
id: 'template',
passed: passed,
value: 2
});
this.section.setItem({
id: 'imports',
passed: 'import' in document.createElement('link'),
value: 2
});
}
};
function test (callback, error) { this.initialize(callback, error); }
test.prototype = {
tests: [
/* Semantics */ testParsing, testElements, testForm, testMicrodata,
/* Offline & Storage */ testOffline, testStorage, testFiles, testStreams,
/* Device Access */ testGeolocation, testOutput, testInput,
/* Connectivity */ testCommunication, testWebRTC,
/* Multimedia */ testVideo, testAudio,
/* 3D, Graphics & Effects */ testResponsive, testCanvas, testWebGL, testAnimation,
/* Performance & Integration */ testInteraction, testPerformance, testSecurity, testHistory, testComponents,
testOther
],
initialize: function(callback, error) {
blacklists = [
[
BLOCKED,
{
'form.file': Browsers.isDevice('Xbox 360') || Browsers.isDevice('Xbox One') || Browsers.isDevice('Playstation 4') || Browsers.isOs('Windows Phone', '<', '8.1') || Browsers.isOs('iOS', '<', '6') || Browsers.isOs('Android', '<', '2.2'),
'form.date.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || Browsers.isBrowser('UC Browser', '<', '8.6'),
'form.month.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || Browsers.isBrowser('UC Browser', '<', '8.6'),
'form.week.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || Browsers.isBrowser('UC Browser', '<', '8.6'),
'form.time.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || Browsers.isBrowser('UC Browser', '<', '8.6'),
'form.datetime-local.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || Browsers.isBrowser('UC Browser', '<', '8.6'),
'form.color.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('UC Browser', '<', '9.8'),
'form.range.ui': Browsers.isBrowser('UC Browser', '<', '9.8'),
'form.progress.element': Browsers.isBrowser('Baidu Browser'),
'files.fileSystem': Browsers.isOs('BlackBerry Tablet OS'),
'input.getUserMedia': Browsers.isDevice('webOS TV') || Browsers.isBrowser('Baidu Browser') || Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('UC Browser', '<', '9.8') || Browsers.isBrowser('Dolphin'),
'input.getGamepads': Browsers.isDevice('webOS TV') || Browsers.isDevice('Playstation 4') || Browsers.isDevice('Wii U'),
'location.geolocation': Browsers.isDevice('webOS TV') || Browsers.isDevice('Xbox One') || Browsers.isBrowser('Baidu Browser') || Browsers.isOs('Google TV'),
'location.orientation': Browsers.isBrowser('Baidu Browser'),
'output.notifications': Browsers.isBrowser('Opera', '=', '18') || Browsers.isBrowser('Baidu Browser') || Browsers.isBrowser('Sogou Explorer'),
'output.requestFullScreen': Browsers.isBrowser('Sogou Explorer') || Browsers.isOs('BlackBerry Tablet OS') || Browsers.isOs('BlackBerry OS'),
'video.subtitle': Browsers.isBrowser('Baidu Browser') || Browsers.isBrowser('Sogou Explorer'),
'webgl.context': Browsers.isBrowser('Baidu Browser'),
}
],
[
DISABLED,
{
'elements.semantic.ping': Browsers.isBrowser('Firefox') || Browsers.isBrowser('Firefox Mobile')
}
],
[
UNCONFIRMED,
{
'interaction.dragdrop': !( Browsers.isType('desktop') ||
Browsers.isType('mobile', 'tablet', 'media') && (
Browsers.isBrowser('Opera') && Browsers.isEngine('Presto')
) ||
Browsers.isType('television') && (
Browsers.isDevice('webOS TV')
)
),
'interaction.editing': !( Browsers.isType('desktop') ||
Browsers.isType('mobile', 'tablet', 'media') && (
Browsers.isOs('iOS', '>=', '5') ||
Browsers.isOs('Android', '>=', '4') ||
Browsers.isOs('Windows Phone', '>=', '7.5') ||
Browsers.isOs('BlackBerry') ||
Browsers.isOs('BlackBerry OS') ||
Browsers.isOs('BlackBerry Tablet OS') ||
Browsers.isOs('Meego') ||
Browsers.isOs('Tizen') ||
Browsers.isEngine('Gecko') ||
Browsers.isEngine('Presto') ||
Browsers.isBrowser('Chrome') ||
Browsers.isBrowser('Polaris', '>=', '8')
) ||
Browsers.isType('television') && (
Browsers.isOs('Tizen') ||
Browsers.isDevice('webOS TV') ||
Browsers.isBrowser('Espial') ||
Browsers.isBrowser('MachBlue XT') ||
Browsers.isEngine('Presto', '>=', '2.9')
) ||
Browsers.isType('gaming') && (
Browsers.isDevice('Xbox 360') ||
Browsers.isDevice('Xbox One') ||
Browsers.isDevice('Playstation 4')
)
)
}
]
];
try {
this.backgroundTasks = [];
this.backgroundIds = {};
this.backgroundId = 0;
this.callback = callback;
this.results = new results(this);
for (var s = 0; s < this.tests.length; s++) {
new (this.tests[s])(this.results);
}
this.waitForBackground();
}
catch(e) {
error(e);
}
},
waitForBackground: function() {
var that = this;
window.setTimeout(function() {
that.checkForBackground.call(that);
}, 300);
},
checkForBackground: function() {
var running = 0;
for (var task = 0; task < this.backgroundTasks.length; task++) { running += this.backgroundTasks[task] }
if (running) {
this.waitForBackground();
} else {
this.finished();
}
},
startBackground: function(id) {
var i = this.backgroundId++;
this.backgroundIds[id] = i;
this.backgroundTasks[i] = 1;
},
stopBackground: function(id) {
this.backgroundTasks[this.backgroundIds[id]] = 0;
},
finished: function() {
var results = [], points = [];
collectResults(0, '', this.results);
function collectResults(level, prefix, data) {
if (data.items) {
for (var i = 0; i < data.items.length; i++) {
if (level == 0) points.push(data.items[i].data.id + '=' + data.items[i].points + '/' + data.items[i].max);
if (typeof data.items[i].data.passed != 'undefined') results.push(prefix + data.items[i].data.id + '=' + (+data.items[i].data.passed));
if (data.items[i].items) {
collectResults(level + 1, prefix + data.items[i].data.id + '-', data.items[i]);
}
}
}
}
var uniqueid = (((1+Math.random())*0x1000000)|0).toString(16).substring(1) + ("0000000000" + (new Date().getTime() - new Date(2010,0,1).getTime()).toString(16)).slice(-10);
this.callback({
release: release,
uniqueid: uniqueid,
score: this.results.points,
results: results.join(','),
points: points.join(','),
maximum: this.results.max
});
}
};
return test;
})();