PageSourceSearch

https://html5test.co/scripts/base.js

js html5test.co collected 2026-10-02 07:37:49 UTC 31,227 bytes, 1,054 lines download raw bytes

1var API_BASE = 'https://proxy.corsfix.com/?http://html5test.eu5.net/api'
2
3/* Polyfills */
4
5if (!Function.prototype.bind) {
6	Function.prototype.bind = function (oThis) {
7		if (typeof this !== "function") {
8			// closest thing possible to the ECMAScript 5 internal IsCallable function
9			throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
10		}
11
12		var aArgs = Array.prototype.slice.call(arguments, 1),
13			fToBind = this,
14			fNOP = function () {},
15			fBound = function () {
16				return fToBind.apply(this instanceof fNOP && oThis
17					? this
18					: oThis,
19					aArgs.concat(Array.prototype.slice.call(arguments)));
20			};
21
22		fNOP.prototype = this.prototype;
23		fBound.prototype = new fNOP();
24
25		return fBound;
26	};
27}
28
29(function (win, doc) {
30	if (win.addEventListener) return;		//No need to polyfill
31
32	function docHijack(p) { var old = doc[p]; doc[p] = function (v) { return addListen(old(v)) } }
33	function addEvent(on, fn, self) {
34		return (self = this).attachEvent('on' + on, function (e) {
35			var e = e || win.event;
36			e.preventDefault = e.preventDefault || function () { e.returnValue = false }
37			e.stopPropagation = e.stopPropagation || function () { e.cancelBubble = true }
38			fn.call(self, e);
39		});
40	}
41	function addListen(obj, i) {
42		if (!obj) return obj;
43		if (i = obj.length) while (i--) obj[i].addEventListener = addEvent;
44		else obj.addEventListener = addEvent;
45		return obj;
46	}
47
48	addListen([doc, win]);
49	if ('Element' in win) win.Element.prototype.addEventListener = addEvent;			//IE8
50	else {																			//IE < 8
51		doc.attachEvent('onreadystatechange', function () { addListen(doc.all) });		//Make sure we also init at domReady
52		docHijack('getElementsByTagName');
53		docHijack('getElementById');
54		docHijack('createElement');
55		addListen(doc.all);
56	}
57})(window, document);
58
59
60/* Utility functions */
61
62function loadWhichBrowser(cb) {
63	var callback = cb;
64
65	var p = [], w = window, d = document, e = f = 0; p.push('ua=' + encodeURIComponent(navigator.userAgent)); e |= w.ActiveXObject ? 1 : 0; e |= w.opera ? 2 : 0; e |= w.chrome ? 4 : 0;
66	e |= 'getBoxObjectFor' in d || 'mozInnerScreenX' in w ? 8 : 0; e |= ('WebKitCSSMatrix' in w || 'WebKitPoint' in w || 'webkitStorageInfo' in w || 'webkitURL' in w) ? 16 : 0;
67	e |= (e & 16 && ({}.toString).toString().indexOf("\n") === -1) ? 32 : 0; p.push('e=' + e); f |= 'sandbox' in d.createElement('iframe') ? 1 : 0; f |= 'WebSocket' in w ? 2 : 0;
68	f |= w.Worker ? 4 : 0; f |= w.applicationCache ? 8 : 0; f |= w.history && history.pushState ? 16 : 0; f |= d.documentElement.webkitRequestFullScreen ? 32 : 0; f |= 'FileReader' in w ? 64 : 0;
69	p.push('f=' + f); p.push('r=' + Math.random().toString(36).substring(7)); p.push('w=' + screen.width); p.push('h=' + screen.height);
70
71	var timeout = null;
72
73	function load() {
74		if (typeof WhichBrowser != 'undefined') {
75			return;
76		}
77
78		var script = document.createElement('script');
79		script.src = 'https://api.whichbrowser.net/rel/detect.js?' + p.join('&');
80		document.getElementsByTagName('head')[0].appendChild(script);
81
82		wait();
83	}
84
85	function wait() {
86		if (!timeout) {
87			timeout = window.setTimeout(load, 3000);
88		}
89
90		if (typeof WhichBrowser == 'undefined') {
91			window.setTimeout(wait, 100)
92		}
93		else {
94			window.clearTimeout(timeout);
95			callback();
96		}
97	}
98
99	load();
100}
101
102function submit(method, payload) {
103	var httpRequest;
104	if (window.XMLHttpRequest) {
105		httpRequest = new XMLHttpRequest();
106	} else if (window.ActiveXObject) {
107		httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
108	}
109
110	httpRequest.open('POST', API_BASE + '/' + method, true);
111	httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
112	httpRequest.send('payload=' + encodeURIComponent(payload));
113}
114
115function decodeParameters() {
116	var params = {};
117
118	if (location.search) {
119		var parts = location.search.substring(1).split('&');
120
121		for (var i = 0; i < parts.length; i++) {
122			var nv = parts[i].split('=');
123			if (!nv[0]) continue;
124			params[nv[0]] = decodeURIComponent(nv[1]) || true;
125		}
126	}
127
128	return params;
129}
130
131function submitResults(r, c) {
132	var parameters = decodeParameters();
133	var identifier = typeof parameters.identifier != 'undefined' ? parameters.identifier : '';
134	var source = typeof parameters.source != 'undefined' ? parameters.source : '';
135	var task = typeof parameters.task != 'undefined' ? parameters.task : '';
136
137	try {
138		var payload = '{' +
139			'"release": "' + r.release + '",' +
140			'"source": "' + source + '",' +
141			'"protocol": "' + location.protocol + '",' +
142			'"identifier": "' + escapeSlashes(identifier) + '",' +
143			'"task": "' + task + '",' +
144			'"uniqueid": "' + r.uniqueid + '",' +
145			'"score": ' + c.score + ',' +
146			'"maximum": ' + c.maximum + ',' +
147			'"camouflage": "' + (Browsers.camouflage ? '1' : '0') + '",' +
148			'"features": "' + (Browsers.features.join(',')) + '",' +
149			'"browserName": "' + (Browsers.browser.name ? Browsers.browser.name : '') + '",' +
150			'"browserChannel": "' + (Browsers.browser.channel ? Browsers.browser.channel : '') + '",' +
151			'"browserVersion": "' + (Browsers.browser.version ? Browsers.browser.version.toString() : '') + '",' +
152			'"browserVersionType": "' + (Browsers.browser.version ? Browsers.browser.version.type : '') + '",' +
153			'"browserVersionMajor": "' + (Browsers.browser.version ? Browsers.browser.version.major : '') + '",' +
154			'"browserVersionMinor": "' + (Browsers.browser.version ? Browsers.browser.version.minor : '') + '",' +
155			'"browserVersionOriginal": "' + (Browsers.browser.version ? Browsers.browser.version.original : '') + '",' +
156			'"browserMode": "' + (Browsers.browser.mode ? Browsers.browser.mode : '') + '",' +
157			'"engineName": "' + (Browsers.engine.name ? Browsers.engine.name : '') + '",' +
158			'"engineVersion": "' + (Browsers.engine.version ? Browsers.engine.version.toString() : '') + '",' +
159			'"osName": "' + (Browsers.os.name ? Browsers.os.name : '') + '",' +
160			'"osFamily": "' + (Browsers.os.family ? Browsers.os.family : '') + '",' +
161			'"osVersion": "' + (Browsers.os.version ? Browsers.os.version.toString() : '') + '",' +
162			'"deviceManufacturer": "' + (Browsers.device.manufacturer ? Browsers.device.manufacturer : '') + '",' +
163			'"deviceModel": "' + (Browsers.device.model ? Browsers.device.model : '') + '",' +
164			'"deviceSeries": "' + (Browsers.device.series ? Browsers.device.series : '') + '",' +
165			'"deviceType": "' + (Browsers.device.type ? Browsers.device.type : '') + '",' +
166			'"deviceIdentified": "' + (Browsers.device.identified ? '1' : '0') + '",' +
167			'"deviceWidth": "' + (screen.width) + '",' +
168			'"deviceHeight": "' + (screen.height) + '",' +
169			'"useragent": "' + navigator.userAgent + '",' +
170			'"humanReadable": "' + Browsers.toString() + '",' +
171			'"points": "' + c.points + '",' +
172			'"results": "' + escapeSlashes(r.results) + '"' +
173			'}';
174
175		submit('submit', payload);
176	} catch (e) {
177		alert('Could not submit results: ' + e.message);
178	}
179}
180
181function escapeSlashes(string) {
182	return string.replace(/\\/g, '\\\\').
183		replace(/\u0008/g, '\\b').
184		replace(/\t/g, '\\t').
185		replace(/\n/g, '\\n').
186		replace(/\f/g, '\\f').
187		replace(/\r/g, '\\r').
188		replace(/'/g, '\\\'').
189		replace(/"/g, '\\"');
190}
191
192
193var NO = 0,
194	YES = 1,
195	OLD = 2,
196	BUGGY = 4,
197	PREFIX = 8,
198	BLOCKED = 16,
199	DISABLED = 32,
200	UNCONFIRMED = 64,
201	UNKNOWN = 128,
202	EXPERIMENTAL = 256;
203
204
205
206var Metadata = function () { this.initialize.apply(this, arguments) };
207Metadata.prototype = {
208	initialize: function (data) {
209		this.data = data;
210		this.list = {};
211
212		for (var i = 0; i < this.data.length; i++) {
213			this.iterate(this.data[i].items, '', -1, []);
214		}
215	},
216
217	iterate: function (data, prefix, level, path) {
218		for (var i = 0; i < data.length; i++) {
219			var key;
220
221			if (typeof data[i].id != 'undefined') {
222				key = prefix + (prefix == '' ? '' : '.') + data[i].id;
223			}
224
225			if (typeof data[i].key != 'undefined') {
226				key = data[i].key;
227			}
228
229			if (key) {
230				path[level + 1] = key;
231
232				if (typeof data[i].key == 'undefined') {
233					data[i].key = key;
234				}
235
236				if (typeof data[i].name != 'undefined') {
237					this.list[key] = {
238						name: data[i].name,
239						path: path.slice(0, level + 1)
240					};
241				}
242
243				if (typeof data[i].items != 'undefined') {
244					this.iterate(data[i].items, key, level + 1, path);
245				}
246			}
247		}
248	},
249
250	getItem: function (key) {
251		var item = this.list[key];
252		return item;
253	},
254
255	getTrail: function (key, separator) {
256		var item = this.list[key];
257
258		if (item) {
259			var trail = [];
260
261			for (var i = 0; i < item.path.length; i++) {
262				if (typeof this.list[item.path[i]] != 'undefined') {
263					trail.push(this.list[item.path[i]].name);
264				} else {
265					trail.push('?');
266				}
267			}
268
269			trail.push(item.name);
270
271			return trail.join(separator);
272		}
273
274		return '?';
275	}
276}
277
278var Calculate = function () { this.initialize.apply(this, arguments) };
279Calculate.prototype = {
280	initialize: function (test, data) {
281		this.parameters = {
282			test: test,
283			data: data
284		};
285
286		this.maximum = 0;
287		this.score = 0;
288		this.points = [];
289
290		for (var i = 0; i < this.parameters.data.length; i++) {
291			this.iterate(this.parameters.data[i].items, '');
292		}
293
294		this.points = this.points.join(',');
295	},
296
297	iterate: function (data, prefix) {
298		for (var i = 0; i < data.length; i++) {
299			if (data[i].key) {
300				if (prefix == '') {
301					var score = this.score;
302					var maximum = this.maximum;
303				}
304
305				if (typeof data[i].value != 'undefined') {
306					this.calculate(data[i].key, data[i]);
307				}
308
309				if (typeof data[i].items != 'undefined') {
310					this.iterate(data[i].items, data[i].key);
311				}
312
313				if (prefix == '') {
314					this.points.push(data[i].id + '=' + (this.score - score) + '/' + (this.maximum - maximum));
315				}
316			}
317		}
318	},
319
320	calculate: function (prefix, data) {
321		var result = true;
322		var value = typeof data.value == 'object' ? data.value : { maximum: data.value };
323
324		if (typeof data.value.conditional == 'undefined') {
325			this.maximum += value.maximum;
326		}
327
328		if (typeof data.items == 'object') {
329			result = 0;
330			passed = true;
331
332			for (var i = 0; i < data.items.length; i++) {
333				if (data.items[i].key) {
334					var r = this.getResult(data.items[i].key);
335					passed &= r | YES;
336					result |= r;
337				}
338			}
339
340			if (!passed) {
341				result = false;
342			}
343		}
344		else {
345			result = this.getResult(prefix);
346		}
347
348		if (result & YES) {
349			var valid = true;
350
351			if (typeof data.value.conditional == 'string') {
352				if (data.value.conditional.substr(0, 1) == '!') {
353					var conditional = this.getResult(data.value.conditional.substr(1));
354
355					if (conditional & YES) {
356						valid = false;
357					}
358				}
359			}
360
361			if (valid) {
362				if (result & PREFIX && typeof value.award == 'object' && typeof value.award.PREFIX != 'undefined') {
363					this.score += value.award.PREFIX;
364				}
365				else if (result & BUGGY && typeof value.award == 'object' && typeof value.award.BUGGY != 'undefined') {
366					this.score += value.award.BUGGY;
367				}
368				else if (result & OLD && typeof value.award == 'object' && typeof value.award.OLD != 'undefined') {
369					this.score += value.award.OLD;
370				}
371				else {
372					this.score += value.maximum;
373				}
374			}
375		}
376	},
377
378	getResult: function (key) {
379		if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(this.parameters.test.results)) {
380			return parseInt(match[1], 10);
381		}
382
383		return null;
384	}
385};
386
387
388
389/* Base UI functions */
390
391var Index = function () { this.initialize.apply(this, arguments) };
392Index.prototype = {
393	initialize: function (options) {
394		var that = this;
395		this.options = options;
396
397		var menu = document.createElement('div');
398		menu.id = 'indexmenu';
399		options.index.appendChild(menu);
400
401		var categories = document.createElement('ul');
402		menu.appendChild(categories);
403
404		for (var i = 0; i < options.tests.length; i++) {
405			var category = document.createElement('li');
406			category.className = 'category ' + options.tests[i].id;
407			categories.appendChild(category);
408
409			var link = document.createElement('a');
410			link.href = '#category-' + options.tests[i].id;
411			link.onclick = function () { that.closeIndex(); };
412			link.innerHTML = options.tests[i].name;
413			category.appendChild(link);
414
415			if (options.tests[i].items.length) {
416				var items = document.createElement('ul');
417				category.appendChild(items);
418
419				for (var j = 0; j < options.tests[i].items.length; j++) {
420					var item = document.createElement('li');
421					items.appendChild(item);
422
423					var link = document.createElement('a');
424					link.href = '#table-' + options.tests[i].items[j].id;
425					link.onclick = function () { that.closeIndex(); };
426					link.innerHTML = options.tests[i].items[j].name;
427					item.appendChild(link);
428				}
429			}
430		}
431
432		var button = document.createElement('button');
433		button.innerHTML = '';
434		button.id = 'indexbutton';
435		button.onclick = this.toggleIndex;
436		options.index.appendChild(button);
437
438		options.wrapper.onclick = this.closeIndex;
439	},
440
441	toggleIndex: function () {
442		if (document.body.className.indexOf(' indexVisible') == -1) {
443			document.body.className = document.body.className.replace(' indexVisible', '') + ' indexVisible';
444		} else {
445			document.body.className = document.body.className.replace(' indexVisible', '');
446		}
447	},
448
449	closeIndex: function () {
450		document.body.className = document.body.className.replace(' indexVisible', '');
451	}
452}
453
454
455var Confirm = function () { this.initialize.apply(this, arguments) };
456Confirm.prototype = {
457	initialize: function (parent, options) {
458		this.options = options;
459
460		this.useragent = document.createElement('p');
461		this.useragent.className = 'useragent';
462		parent.appendChild(this.useragent);
463		this.useragent.innerHTML = 'You are using ' + Browsers;
464
465		this.confirm = document.createElement('span');
466		this.confirm.innerHTML = 'Correct?';
467		this.useragent.appendChild(this.confirm);
468
469		var correct = document.createElement('a');
470		correct.addEventListener('click', function () { this.confirmUseragent(); }.bind(this), true);
471		correct.className = 'correct';
472		correct.innerHTML = '✔';
473		this.confirm.appendChild(correct);
474
475		var wrong = document.createElement('a');
476		wrong.addEventListener('click', function (e) { e.stopPropagation(); this.reportUseragent(); }.bind(this), true);
477		wrong.className = 'wrong';
478		wrong.innerHTML = '✘';
479		this.confirm.appendChild(wrong);
480
481		this.thanks = document.createElement('span');
482		this.thanks.style.display = 'none';
483		this.thanks.innerHTML = 'Thanks!';
484		this.useragent.appendChild(this.thanks);
485	},
486
487	confirmUseragent: function () {
488		this.options.onConfirm();
489		this.showThanks();
490	},
491
492	reportUseragent: function () {
493		this.options.onReport();
494
495		new Feedback(this.confirm, {
496			suggestion: 'I am using' + ' ' + Browsers,
497
498			onFeedback: function (value) {
499				this.options.onFeedback(value);
500			}.bind(this),
501
502			onClose: function () {
503				this.showThanks();
504			}.bind(this)
505		});
506	},
507
508	showThanks: function () {
509		this.confirm.style.display = 'none';
510		this.thanks.style.display = 'inline';
511
512		window.setTimeout(function () {
513			this.thanks.style.display = 'none';
514		}.bind(this), 2500);
515	}
516}
517
518var Save = function () { this.initialize.apply(this, arguments) };
519Save.prototype = {
520	initialize: function (parent, options) {
521		var that = this;
522
523		this.parent = parent;
524		this.options = options;
525		this.created = false;
526
527		this.popup = document.createElement('div');
528		this.popup.className = 'popupPanel pointsLeft save';
529		this.popup.style.display = 'none';
530		this.parent.appendChild(this.popup);
531
532		document.addEventListener('click', this.close.bind(this), false)
533		document.addEventListener('touchstart', this.close.bind(this), false)
534
535		this.parent.addEventListener('click', this.open.bind(this), true)
536		this.parent.addEventListener('touchstart', this.open.bind(this), true)
537	},
538
539	create: function () {
540		this.created = true;
541
542		this.popup.innerHTML +=
543			"<div id='save'>" +
544			"<p>You can see the results here:</p>" +
545			"<p><a href='https://html5test.co/s/?" + this.options.id + "'>html5test.co/s/?" + this.options.id + "</a></p>" +
546			"<p>Or scan this QR-code:</p>" +
547			"<p>" +
548			"<img src='https://quickchart.io/qr?size=200&text=" + encodeURIComponent("https://html5test.co/s/?" + this.options.id) + "' width='200' height='200'>" +
549			"</p>" +
550			"<p>The unique id for this test is:<br><code>" + this.options.id + "</code></p>" +
551			"</div>";
552
553		if (this.options.onSave) {
554			this.options.onSave();
555		}
556	},
557
558	open: function (e) {
559		e.stopPropagation();
560
561		var ignore = false;
562		var el = e.target || e.srcElement;
563
564		while (el != this.parent && ignore == false) {
565			if (el.className.indexOf('popupPanel') != -1) ignore = true;
566			el = el.parentNode;
567		}
568
569		if (!ignore) {
570			e.preventDefault();
571
572			if (!this.created) {
573				this.create();
574			}
575
576			this.popup.style.display = 'block';
577		}
578	},
579
580	close: function (e) {
581		this.popup.style.display = 'none';
582	}
583}
584
585
586
587var Feedback = function () { this.initialize.apply(this, arguments) };
588Feedback.prototype = {
589	initialize: function (parent, options) {
590		var that = this;
591
592		this.parent = parent;
593		this.options = options;
594
595		this.popup = document.createElement('div');
596		this.popup.className = 'popupPanel pointsRight feedback';
597		this.popup.style.display = 'none';
598		this.parent.appendChild(this.popup);
599
600		this.popup.addEventListener('click', function (e) { e.stopPropagation() }, false)
601		this.popup.addEventListener('touchstart', function (e) { e.stopPropagation() }, false)
602
603		document.addEventListener('click', this.close.bind(this), false)
604		document.addEventListener('touchstart', this.close.bind(this), false)
605
606		this.create();
607	},
608
609	create: function () {
610		this.created = true;
611
612		this.popup.innerHTML +=
613			"<div id='feedback'>" +
614			"<h3>What is wrong with this identification?</h3>" +
615			"<textarea id='correction'>" + this.options.suggestion + "</textarea>" +
616			"<button id='sendCorrection'><span>✔</span>Send correction</button>" +
617			"</div>";
618
619		this.popup.style.display = 'block';
620
621		var button = document.getElementById('sendCorrection')
622		button.addEventListener('click', this.send.bind(this), false);
623	},
624
625	send: function () {
626		var field = document.getElementById('correction')
627
628		if (field.value != this.options.suggestion) {
629			if (this.options.onFeedback) {
630				this.options.onFeedback(field.value);
631			}
632		}
633
634		this.close();
635	},
636
637	close: function (e) {
638		this.popup.style.display = 'none';
639		this.popup.parentNode.removeChild(this.popup);
640
641		if (this.options.onClose) {
642			this.options.onClose();
643		}
644	}
645}
646
647
648
649var ResultsTable = function () { this.initialize.apply(this, arguments) };
650ResultsTable.prototype = {
651
652	initialize: function (options) {
653		this.parent = options.parent;
654		this.tests = options.tests;
655		this.options = {
656			title: options.title || '',
657			browsers: options.browsers || [],
658			columns: options.columns || 2,
659			distribute: options.distribute || false,
660			header: options.header || false,
661			links: options.links || false,
662			grading: options.grading || false,
663			features: options.features || false,
664			explainations: options.explainations || false,
665
666			onChange: options.onChange || false
667		}
668
669		var that = this;
670
671		function close(e) {
672			if (that.panel) {
673				var cell = that.panel.parentNode;
674				var node = e.target;
675
676				while (node.parentNode) {
677					if (node == that.panel) return;
678					node = node.parentNode;
679				}
680
681				that.panel.parentNode.removeChild(that.panel);
682				that.panel = null;
683
684				var node = e.target;
685				while (node.parentNode) {
686					if (node == cell) return e.stopPropagation();
687					node = node.parentNode;
688				}
689			}
690		}
691
692		document.addEventListener('click', close, true)
693		document.addEventListener('touchstart', close, true)
694
695		this.data = [null];
696
697		this.createCategories(this.parent, this.tests);
698	},
699
700	updateColumn: function (column, data) {
701		this.data[column] = data;
702
703		for (var c = 0; c < this.tests.length; c++)
704			for (var i = 0; i < this.tests[c].items.length; i++) {
705				var test = this.tests[c].items[i];
706
707				if (typeof test != 'string') {
708					if (typeof test != 'undefined') {
709						var points = 0;
710						var maximum = 0;
711
712						if (match = (new RegExp(test.id + "=([0-9]+)(?:\\/([0-9]+))?(?:\\+([0-9]+))?")).exec(data.points)) {
713							points = match[1];
714							if (match[2]) maximum = match[2];
715						}
716
717						var row = document.getElementById('head-' + test.id);
718						var cell = row.childNodes[0].firstChild.nextSibling;
719
720						var content = "<div class='grade'>";
721
722						if (this.options.grading) {
723							var grade = '';
724							var percent = parseInt(points / maximum * 100, 10);
725							switch (true) {
726								case percent == 0: grade = 'none'; break;
727								case percent <= 30: grade = 'badly'; break;
728								case percent <= 60: grade = 'reasonable'; break;
729								case percent <= 95: grade = 'good'; break;
730								default: grade = 'great'; break;
731							}
732
733							if (points == maximum)
734								content += "<span class='" + grade + "'>" + points + "</span>";
735							else
736								content += "<span class='" + grade + "'>" + points + "/" + maximum + "</span>";
737						} else {
738							content += "<span>" + points + "</span>";
739						}
740
741						content += "</div>";
742
743						cell.innerHTML = content;
744						this.updateItems(column, data, test.items);
745					}
746				}
747			}
748	},
749
750	updateItems: function (column, data, tests) {
751		var count = [0, 0];
752
753		for (var i = 0; i < tests.length; i++) {
754			if (typeof tests[i] != 'string') {
755				var key = tests[i].key;
756
757				var row = document.getElementById('row-' + key);
758				var cell = row.childNodes[column + 1];
759
760				if (typeof tests[i].items != 'undefined') {
761					var results = this.updateItems(column, data, tests[i].items);
762
763					if (results[0] == results[1]) {
764						cell.innerHTML = '<div>Yes <span class="check">✔</span></div>';
765					} else if (results[1] == 0) {
766						cell.innerHTML = '<div>No <span class="ballot">✘</span></div>';
767						row.className += ' missing';
768					} else {
769						cell.innerHTML = '<div><span class="partially">Partial</span> <span class="partial">○</span></div>';
770						row.className += ' missing';
771					}
772				} else {
773					if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(data.results)) {
774						var result = parseInt(match[1], 10);
775
776						if (result & YES) {
777							switch (true) {
778								case !!(result & BUGGY): cell.innerHTML = '<div>Buggy <span class="buggy"></span></div>'; row.className += ' missing'; break;
779								case !!(result & OLD): cell.innerHTML = '<div>Partial <span class="partial">○</span></div>'; row.className += ' missing'; count[1]++; break;
780								case !!(result & PREFIX): cell.innerHTML = '<div>Prefixed <span class="check">✔</span></div>'; count[1]++; break;
781								default: cell.innerHTML = '<div>Yes <span class="check">✔</span></div>'; count[1]++; break;
782							}
783						}
784						else {
785							switch (true) {
786								case !!(result & UNKNOWN): cell.innerHTML = '<div>Unknown <span class="partial">?</span></div>'; break;
787								case !!(result & BLOCKED): cell.innerHTML = '<div>Not functional <span class="buggy">!</span></div>'; break;
788								case !!(result & DISABLED): cell.innerHTML = '<div>Disabled <span class="ballot">✘</span></div>'; break;
789								default: cell.innerHTML = '<div>No <span class="ballot">✘</span></div>'; break;
790							}
791							row.className += ' missing';
792						}
793					} else {
794						cell.innerHTML = '<div><span class="partially">Unknown</span> <span class="partial">?</span></div>';
795						row.className += ' missing';
796					}
797				}
798
799				count[0]++;
800			}
801		}
802
803		return count;
804	},
805
806	createCategories: function (parent, tests) {
807		var left, right;
808
809		left = document.createElement('div');
810		left.className = 'left';
811		left.innerHTML = '<div></div>';
812		parent.appendChild(left);
813
814		right = document.createElement('div');
815		right.className = 'right';
816		right.innerHTML = '<div></div>';
817		parent.appendChild(right);
818
819
820		for (var i = 0; i < tests.length; i++) {
821			var container = parent;
822			if (tests[i].column == 'left') container = left.firstChild;
823			if (tests[i].column == 'right') container = right.firstChild;
824
825			var div = document.createElement('div');
826			div.className = 'category ' + tests[i].id;
827			div.id = 'category-' + tests[i].id;
828			container.appendChild(div);
829
830			var h2 = document.createElement('h2');
831			h2.innerHTML = tests[i].name;
832			div.appendChild(h2);
833
834			this.createSections(div, tests[i].items);
835		}
836	},
837
838	createSections: function (parent, tests) {
839		for (var i = 0; i < tests.length; i++) {
840			var table = document.createElement('table');
841			table.cellSpacing = 0;
842			table.id = 'table-' + tests[i].id;
843			parent.appendChild(table);
844
845			var thead = document.createElement('thead');
846			table.appendChild(thead);
847
848			var tr = document.createElement('tr');
849			tr.id = 'head-' + tests[i].id;
850			thead.appendChild(tr);
851
852			var th = document.createElement('th');
853			th.innerHTML = tests[i].name + "<div></div>";
854			th.colSpan = this.options.columns + 1;
855			tr.appendChild(th);
856
857			if (typeof tests[i].items != 'undefined') {
858				var tbody = document.createElement('tbody');
859				table.appendChild(tbody);
860
861				var status = typeof tests[i].status != 'undefined' ? tests[i].status : '';
862
863				this.createItems(tbody, 0, tests[i].items, {
864					id: tests[i].id,
865					status: status,
866					urls: []
867				});
868			}
869		}
870	},
871
872	createItems: function (parent, level, tests, data) {
873		var ids = [];
874
875		for (var i = 0; i < tests.length; i++) {
876			var tr = document.createElement('tr');
877			parent.appendChild(tr);
878
879			if (typeof tests[i] == 'string') {
880				if (this.options.explainations || tests[i].substr(0, 4) != '<em>') {
881					var th = document.createElement('th');
882					th.colSpan = this.options.columns + 1;
883					th.className = 'details';
884					tr.appendChild(th);
885
886					th.innerHTML = tests[i];
887				}
888			} else {
889				var key = tests[i].key;
890
891				var th = document.createElement('th');
892				th.innerHTML = "<div class='status " + (typeof tests[i].status != 'undefined' ? tests[i].status : data.status) + "'><span>" + tests[i].name + "</span></div>";
893				tr.appendChild(th);
894
895				for (var c = 0; c < this.options.columns; c++) {
896					var td = document.createElement('td');
897					tr.appendChild(td);
898				}
899
900				tr.id = 'row-' + key;
901
902				if (level > 0) {
903					tr.className = 'isChild';
904				}
905
906				if (typeof tests[i].items != 'undefined') {
907					var urls = null;
908
909					if (this.options.links) {
910						if (typeof tests[i].urls != 'undefined') {
911							urls = tests[i].urls;
912						}
913						else if (typeof tests[i].url != 'undefined') {
914							urls = { 'w3c': tests[i].url };
915						}
916					}
917
918					tr.className += 'hasChild';
919
920					var children = this.createItems(parent, level + 1, tests[i].items, {
921						id: key,
922						status: typeof tests[i].status != 'undefined' ? tests[i].status : data.status,
923						urls: urls
924					});
925
926					this.hideChildren(tr, children);
927
928					(function (that, tr, th, children) {
929						th.onclick = function () {
930							that.toggleChildren(tr, children);
931						};
932					})(this, tr, th, children);
933				} else {
934					var urls;
935					var value = 0;
936					var showLinks = false;
937
938					if (typeof tests[i].value != 'undefined') {
939						value = tests[i].value;
940					}
941
942					if (typeof tests[i].urls != 'undefined') {
943						urls = tests[i].urls;
944						showLinks = true;
945					}
946					else if (typeof tests[i].url != 'undefined') {
947						urls = [['w3c', tests[i].url]];
948						showLinks = true;
949					}
950
951					th.className = 'hasLink';
952
953					(function (that, th, data) {
954						th.onclick = function () {
955							new FeaturePopup(th, data);
956						};
957					})(this, th, {
958						id: key,
959						name: tests[i].name,
960						value: value,
961						status: typeof tests[i].status != 'undefined' ? tests[i].status : data.status,
962						urls: (urls || []).concat(data.urls || [])
963					});
964				}
965
966				ids.push(tr.id);
967			}
968		}
969
970		return ids;
971	},
972
973	toggleChildren: function (element, ids) {
974		if (element.className.indexOf(' hidden') == -1) {
975			this.hideChildren(element, ids);
976		} else {
977			this.showChildren(element, ids);
978		}
979	},
980
981	showChildren: function (element, ids) {
982		element.className = element.className.replace(' hidden', '');
983
984		for (var i = 0; i < ids.length; i++) {
985			var e = document.getElementById(ids[i]);
986			e.style.display = 'table-row';
987		}
988	},
989
990	hideChildren: function (element, ids) {
991		element.className = element.className.replace(' hidden', '');
992		element.className += ' hidden';
993
994		for (var i = 0; i < ids.length; i++) {
995			var e = document.getElementById(ids[i]);
996			e.style.display = 'none';
997		}
998	}
999}
1000
1001
1002
1003var FeaturePopup = function () { this.initialize.apply(this, arguments) };
1004FeaturePopup.current = null;
1005FeaturePopup.prototype = {
1006	initialize: function (parent, data) {
1007		if (FeaturePopup.current) {
1008			FeaturePopup.current.close();
1009		}
1010
1011		var maximum = data.value;
1012		if (typeof maximum == 'object') {
1013			maximum = maximum.maximum;
1014		}
1015
1016		var content = "";
1017		content += "<div class='info'>";
1018		content += "<div class='column left status " + data.status + "'><span>" + data.status + "</span></div>";
1019		content += "<div class='column middle" + (maximum ? '' : ' none') + "'><em>" + (maximum || '✘') + "</em> <span>" + (maximum != 1 ? 'Points' : 'Point') + "</span></div>";
1020		content += "<div class='column right'><a href='/compare/feature/" + data.id + ".html' class='compare'><span>Compare</span></a></div>";
1021		content += "</div>";
1022		content += "<div class='links'>";
1023
1024		for (var i = 0; i < data.urls.length; i++) {
1025			if (data.urls[i][0] == 'w3c') content += "<a href='" + data.urls[i][1] + "' class='w3c'>Go to the specification at W3C.org</a>";
1026			if (data.urls[i][0] == 'whatwg') content += "<a href='" + data.urls[i][1] + "' class='whatwg'>Go to the specification at Whatwg.org</a>";
1027			if (data.urls[i][0] == 'khronos') content += "<a href='" + data.urls[i][1] + "' class='khronos'>Go to the specification at Khronos.org</a>";
1028			if (data.urls[i][0] == 'ietf') content += "<a href='" + data.urls[i][1] + "' class='ietf'>Go to the specification at IETF.org</a>";
1029			if (data.urls[i][0] == 'webm') content += "<a href='" + data.urls[i][1] + "' class='webm'>Go to the specification at WebMproject.org</a>";
1030			if (data.urls[i][0] == 'xiph') content += "<a href='" + data.urls[i][1] + "' class='xiph'>Go to the specification at Xiph.org</a>";
1031			if (data.urls[i][0] == 'ecma') content += "<a href='" + data.urls[i][1] + "' class='ecma'>Go to the specification at ECMA</a>";
1032			if (data.urls[i][0] == 'ricg') content += "<a href='" + data.urls[i][1] + "' class='ricg'>Go to Responsive Images Community Group</a>";
1033			if (data.urls[i][0] == 'wp') content += "<a href='https://docs.webplatform.org/wiki" + data.urls[i][1] + "' class='wp'>Documentation at WebPlatform.org</a>";
1034			if (data.urls[i][0] == 'mdn') content += "<a href='https://developer.mozilla.org/en-US/docs" + data.urls[i][1] + "' class='mdn'>Documentation at Mozilla Developer Network</a>";
1035		}
1036
1037		content += "</div>";
1038
1039		this.panel = document.createElement('div');
1040		this.panel.className = 'linksPanel popupPanel pointsLeft';
1041		this.panel.innerHTML = content;
1042		parent.appendChild(this.panel);
1043
1044		FeaturePopup.current = this;
1045	},
1046
1047	close: function () {
1048		this.panel.parentNode.removeChild(this.panel);
1049		FeaturePopup.current = null;
1050	}
1051}
1052
1053document.addEventListener('click', function () { if (FeaturePopup.current) FeaturePopup.current.close() }, true)
1054document.addEventListener('touchstart', function () { if (FeaturePopup.current) FeaturePopup.current.close() }, true)

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.