/* Polyfills */ if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; } (function(win, doc){ if(win.addEventListener)return; //No need to polyfill function docHijack(p){var old = doc[p];doc[p] = function(v){return addListen(old(v))}} function addEvent(on, fn, self){ return (self = this).attachEvent('on' + on, function(e){ var e = e || win.event; e.preventDefault = e.preventDefault || function(){e.returnValue = false} e.stopPropagation = e.stopPropagation || function(){e.cancelBubble = true} fn.call(self, e); }); } function addListen(obj, i){ if(!obj) return obj; if(i = obj.length)while(i--)obj[i].addEventListener = addEvent; else obj.addEventListener = addEvent; return obj; } addListen([doc, win]); if('Element' in win)win.Element.prototype.addEventListener = addEvent; //IE8 else{ //IE < 8 doc.attachEvent('onreadystatechange', function(){addListen(doc.all)}); //Make sure we also init at domReady docHijack('getElementsByTagName'); docHijack('getElementById'); docHijack('createElement'); addListen(doc.all); } })(window, document); /* Utility functions */ var tim = (function(){ var starts = "\\{\\{", ends = "\\}\\}", path = "[a-z0-9_][\\.a-z0-9_]*", // e.g. config.person.name pattern = new RegExp(starts + "("+ path +")" + ends, "gim"), undef; return function(template, data, notFound){ // Merge the data into the template string return template.replace(pattern, function(tag, ref){ var path = ref.split("."), len = path.length, lookup = data, i = 0; for (; i < len; i++){ lookup = lookup[path[i]]; // Error handling for when the property is not found if (lookup === undef){ // If specified, substitute with the "not found" arg if (notFound !== undef){ return notFound; } // Throw error throw "Tim: '" + path[i] + "' not found in " + tag; } // Success! Return the required value if (i === len - 1){ return lookup; } } }); }; }()); function escapeSlashes(string) { return string.replace(/\\/g, '\\\\'). replace(/\u0008/g, '\\b'). replace(/\t/g, '\\t'). replace(/\n/g, '\\n'). replace(/\f/g, '\\f'). replace(/\r/g, '\\r'). replace(/'/g, '\\\''). replace(/"/g, '\\"'); } var NO = 0, YES = 1, OLD = 2, BUGGY = 4, PREFIX = 8, BLOCKED = 16, DISABLED = 32, UNCONFIRMED = 64, UNKNOWN = 128, EXPERIMENTAL = 256; var Metadata = function() { this.initialize.apply(this, arguments) }; Metadata.prototype = { initialize: function(data) { this.data = data; this.list = {}; for (var i = 0; i < this.data.length; i++) { this.iterate(this.data[i].items, '', -1, []); } }, iterate: function(data, prefix, level, path) { for (var i = 0; i < data.length; i++) { var key; if (typeof data[i].id != 'undefined') { key = prefix + (prefix == '' ? '' : '.') + data[i].id; } if (typeof data[i].key != 'undefined') { key = data[i].key; } if (key) { path[level + 1] = key; if (typeof data[i].key == 'undefined') { data[i].key = key; } if (typeof data[i].name != 'undefined') { this.list[key] = { name: data[i].name, path: path.slice(0, level + 1) }; } if (typeof data[i].items != 'undefined') { this.iterate(data[i].items, key, level + 1, path); } } } }, getItem: function(key) { var item = this.list[key]; return item; }, getTrail: function(key, separator) { var item = this.list[key]; if (item) { var trail = []; for (var i = 0; i < item.path.length; i++) { if (typeof this.list[item.path[i]] != 'undefined') { trail.push(this.list[item.path[i]].name); } else { trail.push('?'); } } trail.push(item.name); return trail.join(separator); } return '?'; } } var Calculate = function() { this.initialize.apply(this, arguments) }; Calculate.prototype = { initialize: function(test, data) { this.parameters = { test: test, data: data }; this.maximum = 0; this.score = 0; this.points = []; for (var i = 0; i < this.parameters.data.length; i++) { this.iterate(this.parameters.data[i].items, ''); } this.points = this.points.join(','); }, iterate: function(data, prefix) { for (var i = 0; i < data.length; i++) { if (data[i].key) { if (prefix == '') { var score = this.score; var maximum = this.maximum; } if (typeof data[i].value != 'undefined') { this.calculate(data[i].key, data[i]); } if (typeof data[i].items != 'undefined') { this.iterate(data[i].items, data[i].key); } if (prefix == '') { this.points.push(data[i].id + '=' + (this.score - score) + '/' + (this.maximum - maximum)); } } } }, calculate: function(prefix, data) { var result = true; var value = typeof data.value == 'object' ? data.value : { maximum: data.value }; if (typeof data.value.conditional == 'undefined') { this.maximum += value.maximum; } if (typeof data.items == 'object') { result = 0; passed = true; for (var i = 0; i < data.items.length; i++) { if (data.items[i].key) { var r = this.getResult(data.items[i].key); passed &= r | YES; result |= r; } } if (!passed) { result = false; } } else { result = this.getResult(prefix); } if (result & YES) { var valid = true; if (typeof data.value.conditional == 'string') { if (data.value.conditional.substr(0, 1) == '!') { var conditional = this.getResult(data.value.conditional.substr(1)); if (conditional & YES) { valid = false; } } } if (valid) { if (result & PREFIX && typeof value.award == 'object' && typeof value.award.PREFIX != 'undefined') { this.score += value.award.PREFIX; } else if (result & BUGGY && typeof value.award == 'object' && typeof value.award.BUGGY != 'undefined') { this.score += value.award.BUGGY; } else if (result & OLD && typeof value.award == 'object' && typeof value.award.OLD != 'undefined') { this.score += value.award.OLD; } else { this.score += value.maximum; } } } }, getResult: function(key) { if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(this.parameters.test.results)) { return parseInt(match[1], 10); } return null; } }; /* Base UI functions */ var Index = function() { this.initialize.apply(this, arguments) }; Index.prototype = { initialize: function(options) { var that = this; this.options = options; var menu = document.createElement('div'); menu.id = 'indexmenu'; options.index.appendChild(menu); var categories = document.createElement('ul'); menu.appendChild(categories); for (var i = 0; i < options.tests.length; i++) { var category = document.createElement('li'); category.className = 'category ' + options.tests[i].id; categories.appendChild(category); var link = document.createElement('a'); link.href = '#category-' + options.tests[i].id; link.onclick = function () { that.closeIndex(); }; link.innerHTML = options.tests[i].name; category.appendChild(link); if (options.tests[i].items.length) { var items = document.createElement('ul'); category.appendChild(items); for (var j = 0; j < options.tests[i].items.length; j++) { var item = document.createElement('li'); items.appendChild(item); var link = document.createElement('a'); link.href = '#table-' + options.tests[i].items[j].id; link.onclick = function () { that.closeIndex(); }; link.innerHTML = options.tests[i].items[j].name; item.appendChild(link); } } } var button = document.createElement('button'); button.innerHTML = ''; button.id = 'indexbutton'; button.onclick = this.toggleIndex; options.index.appendChild(button); options.wrapper.onclick = this.closeIndex; }, toggleIndex: function() { if (document.body.className.indexOf(' indexVisible') == -1) { document.body.className = document.body.className.replace(' indexVisible', '') + ' indexVisible'; } else { document.body.className = document.body.className.replace(' indexVisible', ''); } }, closeIndex: function() { document.body.className = document.body.className.replace(' indexVisible', ''); } } var Confirm = function() { this.initialize.apply(this, arguments) }; Confirm.prototype = { initialize: function(parent, options) { this.options = options; this.useragent = document.createElement('p'); this.useragent.className = 'useragent'; parent.appendChild(this.useragent); this.useragent.innerHTML = 'You are using ' + Browsers; this.confirm = document.createElement('span'); this.confirm.innerHTML = 'Correct?'; this.useragent.appendChild(this.confirm); var correct = document.createElement('a'); correct.addEventListener('click', function() { this.confirmUseragent(); }.bind(this), true); correct.className = 'correct'; correct.innerHTML = '✔'; this.confirm.appendChild(correct); var wrong = document.createElement('a'); wrong.addEventListener('click', function(e) { e.stopPropagation(); this.reportUseragent(); }.bind(this), true); wrong.className = 'wrong'; wrong.innerHTML = '✘'; this.confirm.appendChild(wrong); this.thanks = document.createElement('span'); this.thanks.style.display = 'none'; this.thanks.innerHTML = 'Thanks!'; this.useragent.appendChild(this.thanks); }, confirmUseragent: function() { this.options.onConfirm(); this.showThanks(); }, reportUseragent: function() { this.options.onReport(); new Feedback(this.confirm, { suggestion: 'I am using' + ' ' + Browsers, onFeedback: function(value) { this.options.onFeedback(value); }.bind(this), onClose: function() { this.showThanks(); }.bind(this) }); }, showThanks: function() { this.confirm.style.display = 'none'; this.thanks.style.display = 'inline'; window.setTimeout(function() { this.thanks.style.display = 'none'; }.bind(this), 2500); } } var Share = function() { this.initialize.apply(this, arguments) }; Share.prototype = { initialize: function(parent, options) { var that = this; this.parent = parent; this.options = options; this.created = false; this.popup = document.createElement('div'); this.popup.className = 'popupPanel pointsLeft share'; this.popup.style.display = 'none'; this.parent.appendChild(this.popup); this.parent.addEventListener('click', this.open.bind(this), true) this.parent.addEventListener('touchstart', this.open.bind(this), true) document.addEventListener('click', this.close.bind(this), true) document.addEventListener('touchstart', this.close.bind(this), true) }, create: function() { this.created = true; this.popup.innerHTML += "
"; !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)) {js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}} (document,"script","twitter-wjs"); (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })(); }, open: function(e) { e.preventDefault(); if (!this.created) { this.create(); } this.popup.style.display = 'block'; }, close: function() { this.popup.style.display = 'none'; } } var Save = function() { this.initialize.apply(this, arguments) }; Save.prototype = { initialize: function(parent, options) { var that = this; this.parent = parent; this.options = options; this.created = false; this.popup = document.createElement('div'); this.popup.className = 'popupPanel pointsLeft save'; this.popup.style.display = 'none'; this.parent.appendChild(this.popup); document.addEventListener('click', this.close.bind(this), false) document.addEventListener('touchstart', this.close.bind(this), false) this.parent.addEventListener('click', this.open.bind(this), true) this.parent.addEventListener('touchstart', this.open.bind(this), true) }, create: function() { this.created = true; this.popup.innerHTML += "You can see the results here:
" + "html5te.st/" + this.options.id + "
" + "Or scan this QR-code:
" + "" +
"" +
"
The unique id for this test is:" + this.options.id + "