6
if (!Function.prototype.bind) {
7
Function.prototype.bind = function (oThis) {
8
if (typeof this !== "function") {
9
// closest thing possible to the ECMAScript 5 internal IsCallable function
10
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
13
var aArgs = Array.prototype.slice.call(arguments, 1),
15
fNOP = function () {},
16
fBound = function () {
17
return fToBind.apply(this instanceof fNOP && oThis
20
aArgs.concat(Array.prototype.slice.call(arguments)));
23
fNOP.prototype = this.prototype;
24
fBound.prototype = new fNOP();
31
if(win.addEventListener)return; //No need to polyfill
33
function docHijack(p){var old = doc[p];doc[p] = function(v){return addListen(old(v))}}
34
function addEvent(on, fn, self){
35
return (self = this).attachEvent('on' + on, function(e){
36
var e = e || win.event;
37
e.preventDefault = e.preventDefault || function(){e.returnValue = false}
38
e.stopPropagation = e.stopPropagation || function(){e.cancelBubble = true}
42
function addListen(obj, i){
44
if(i = obj.length)while(i--)obj[i].addEventListener = addEvent;
45
else obj.addEventListener = addEvent;
49
addListen([doc, win]);
50
if('Element' in win)win.Element.prototype.addEventListener = addEvent; //IE8
52
doc.attachEvent('onreadystatechange', function(){addListen(doc.all)}); //Make sure we also init at domReady
53
docHijack('getElementsByTagName');
54
docHijack('getElementById');
55
docHijack('createElement');
64
/* Utility functions */
66
var tim = (function(){
67
var starts = "\\{\\{",
69
path = "[a-z0-9_][\\.a-z0-9_]*", // e.g. config.person.name
70
pattern = new RegExp(starts + "("+ path +")" + ends, "gim"),
73
return function(template, data, notFound){
74
// Merge the data into the template string
75
return template.replace(pattern, function(tag, ref){
76
var path = ref.split("."),
82
lookup = lookup[path[i]];
84
// Error handling for when the property is not found
85
if (lookup === undef){
86
// If specified, substitute with the "not found" arg
87
if (notFound !== undef){
91
throw "Tim: '" + path[i] + "' not found in " + tag;
94
// Success! Return the required value
103
function escapeSlashes(string) {
104
return string.replace(/\\/g, '\\\\').
105
replace(/\u0008/g, '\\b').
106
replace(/\t/g, '\\t').
107
replace(/\n/g, '\\n').
108
replace(/\f/g, '\\f').
109
replace(/\r/g, '\\r').
110
replace(/'/g, '\\\'').
111
replace(/"/g, '\\"');
128
var Metadata = function() { this.initialize.apply(this, arguments) };
129
Metadata.prototype = {
130
initialize: function(data) {
134
for (var i = 0; i < this.data.length; i++) {
135
this.iterate(this.data[i].items, '', -1, []);
139
iterate: function(data, prefix, level, path) {
140
for (var i = 0; i < data.length; i++) {
143
if (typeof data[i].id != 'undefined') {
144
key = prefix + (prefix == '' ? '' : '.') + data[i].id;
147
if (typeof data[i].key != 'undefined') {
152
path[level + 1] = key;
154
if (typeof data[i].key == 'undefined') {
158
if (typeof data[i].name != 'undefined') {
161
path: path.slice(0, level + 1)
165
if (typeof data[i].items != 'undefined') {
166
this.iterate(data[i].items, key, level + 1, path);
172
getItem: function(key) {
173
var item = this.list[key];
177
getTrail: function(key, separator) {
178
var item = this.list[key];
183
for (var i = 0; i < item.path.length; i++) {
184
if (typeof this.list[item.path[i]] != 'undefined') {
185
trail.push(this.list[item.path[i]].name);
191
trail.push(item.name);
193
return trail.join(separator);
200
var Calculate = function() { this.initialize.apply(this, arguments) };
201
Calculate.prototype = {
202
initialize: function(test, data) {
212
for (var i = 0; i < this.parameters.data.length; i++) {
213
this.iterate(this.parameters.data[i].items, '');
216
this.points = this.points.join(',');
219
iterate: function(data, prefix) {
220
for (var i = 0; i < data.length; i++) {
223
var score = this.score;
224
var maximum = this.maximum;
227
if (typeof data[i].value != 'undefined') {
228
this.calculate(data[i].key, data[i]);
231
if (typeof data[i].items != 'undefined') {
232
this.iterate(data[i].items, data[i].key);
236
this.points.push(data[i].id + '=' + (this.score - score) + '/' + (this.maximum - maximum));
242
calculate: function(prefix, data) {
244
var value = typeof data.value == 'object' ? data.value : { maximum: data.value };
246
if (typeof data.value.conditional == 'undefined') {
247
this.maximum += value.maximum;
250
if (typeof data.items == 'object') {
254
for (var i = 0; i < data.items.length; i++) {
255
if (data.items[i].key) {
256
var r = this.getResult(data.items[i].key);
267
result = this.getResult(prefix);
273
if (typeof data.value.conditional == 'string') {
274
if (data.value.conditional.substr(0, 1) == '!') {
275
var conditional = this.getResult(data.value.conditional.substr(1));
277
if (conditional & YES) {
284
if (result & PREFIX && typeof value.award == 'object' && typeof value.award.PREFIX != 'undefined') {
285
this.score += value.award.PREFIX;
287
else if (result & BUGGY && typeof value.award == 'object' && typeof value.award.BUGGY != 'undefined') {
288
this.score += value.award.BUGGY;
290
else if (result & OLD && typeof value.award == 'object' && typeof value.award.OLD != 'undefined') {
291
this.score += value.award.OLD;
294
this.score += value.maximum;
300
getResult: function(key) {
301
if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(this.parameters.test.results)) {
302
return parseInt(match[1], 10);
311
/* Base UI functions */
313
var Index = function() { this.initialize.apply(this, arguments) };
315
initialize: function(options) {
317
this.options = options;
319
var menu = document.createElement('div');
320
menu.id = 'indexmenu';
321
options.index.appendChild(menu);
323
var categories = document.createElement('ul');
324
menu.appendChild(categories);
326
for (var i = 0; i < options.tests.length; i++) {
327
var category = document.createElement('li');
328
category.className = 'category ' + options.tests[i].id;
329
categories.appendChild(category);
331
var link = document.createElement('a');
332
link.href = '#category-' + options.tests[i].id;
333
link.onclick = function () { that.closeIndex(); };
334
link.innerHTML = options.tests[i].name;
335
category.appendChild(link);
337
if (options.tests[i].items.length) {
338
var items = document.createElement('ul');
339
category.appendChild(items);
341
for (var j = 0; j < options.tests[i].items.length; j++) {
342
var item = document.createElement('li');
343
items.appendChild(item);
345
var link = document.createElement('a');
346
link.href = '#table-' + options.tests[i].items[j].id;
347
link.onclick = function () { that.closeIndex(); };
348
link.innerHTML = options.tests[i].items[j].name;
349
item.appendChild(link);
354
var button = document.createElement('button');
355
button.innerHTML = '';
356
button.id = 'indexbutton';
357
button.onclick = this.toggleIndex;
358
options.index.appendChild(button);
360
options.wrapper.onclick = this.closeIndex;
363
toggleIndex: function() {
364
if (document.body.className.indexOf(' indexVisible') == -1) {
365
document.body.className = document.body.className.replace(' indexVisible', '') + ' indexVisible';
367
document.body.className = document.body.className.replace(' indexVisible', '');
371
closeIndex: function() {
372
document.body.className = document.body.className.replace(' indexVisible', '');
377
var Confirm = function() { this.initialize.apply(this, arguments) };
378
Confirm.prototype = {
379
initialize: function(parent, options) {
380
this.options = options;
382
this.useragent = document.createElement('p');
383
this.useragent.className = 'useragent';
384
parent.appendChild(this.useragent);
385
this.useragent.innerHTML = 'You are using ' + Browsers;
387
this.confirm = document.createElement('span');
388
this.confirm.innerHTML = 'Correct?';
389
this.useragent.appendChild(this.confirm);
391
var correct = document.createElement('a');
392
correct.addEventListener('click', function() { this.confirmUseragent(); }.bind(this), true);
393
correct.className = 'correct';
394
correct.innerHTML = '✔';
395
this.confirm.appendChild(correct);
397
var wrong = document.createElement('a');
398
wrong.addEventListener('click', function(e) { e.stopPropagation(); this.reportUseragent(); }.bind(this), true);
399
wrong.className = 'wrong';
400
wrong.innerHTML = '✘';
401
this.confirm.appendChild(wrong);
403
this.thanks = document.createElement('span');
404
this.thanks.style.display = 'none';
405
this.thanks.innerHTML = 'Thanks!';
406
this.useragent.appendChild(this.thanks);
409
confirmUseragent: function() {
410
this.options.onConfirm();
414
reportUseragent: function() {
415
this.options.onReport();
417
new Feedback(this.confirm, {
418
suggestion: 'I am using' + ' ' + Browsers,
420
onFeedback: function(value) {
421
this.options.onFeedback(value);
424
onClose: function() {
430
showThanks: function() {
431
this.confirm.style.display = 'none';
432
this.thanks.style.display = 'inline';
434
window.setTimeout(function() {
435
this.thanks.style.display = 'none';
441
var Share = function() { this.initialize.apply(this, arguments) };
443
initialize: function(parent, options) {
446
this.parent = parent;
447
this.options = options;
448
this.created = false;
450
this.popup = document.createElement('div');
451
this.popup.className = 'popupPanel pointsLeft share';
452
this.popup.style.display = 'none';
453
this.parent.appendChild(this.popup);
455
this.parent.addEventListener('click', this.open.bind(this), true)
456
this.parent.addEventListener('touchstart', this.open.bind(this), true)
458
document.addEventListener('click', this.close.bind(this), true)
459
document.addEventListener('touchstart', this.close.bind(this), true)
465
this.popup.innerHTML +=
466
"<div id='share'><div>" +
468
"<div id='twitter'>" +
469
"<a href='https://twitter.com/share' class='twitter-share-button' " +
470
"data-url='https://html5test.com' " +
471
"data-related='rakaz' " +
472
"data-text='" + this.options.browser + " scored " + this.options.score + " points. How well does your browser support HTML5?' " +
474
"data-count='vertical'"+
478
"<div id='facebook'>" +
479
"<iframe src='//www.facebook.com/plugins/like.php?href=https%3A%2F%2Fhtml5test.com&width=60&height=65&colorscheme=light&layout=box_count&action=like&show_faces=false&send=false&appId=202643099847776' scrolling='no' frameborder='0' style='border:none; overflow:hidden; width:The pixel width of the pluginpx; height:65px;' allowTransparency='true'></iframe>" +
482
"<div id='google'>" +
483
"<div class='g-plusone' " +
484
"data-href='https://html5test.com' " +
493
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id))
494
{js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}
495
(document,"script","twitter-wjs");
498
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
499
po.src = 'https://apis.google.com/js/plusone.js';
500
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
511
this.popup.style.display = 'block';
515
this.popup.style.display = 'none';
520
var Save = function() { this.initialize.apply(this, arguments) };
522
initialize: function(parent, options) {
525
this.parent = parent;
526
this.options = options;
527
this.created = false;
529
this.popup = document.createElement('div');
530
this.popup.className = 'popupPanel pointsLeft save';
531
this.popup.style.display = 'none';
532
this.parent.appendChild(this.popup);
534
document.addEventListener('click', this.close.bind(this), false)
535
document.addEventListener('touchstart', this.close.bind(this), false)
537
this.parent.addEventListener('click', this.open.bind(this), true)
538
this.parent.addEventListener('touchstart', this.open.bind(this), true)
544
this.popup.innerHTML +=
546
"<p>You can see the results here:</p>" +
547
"<p><a href='" + document.location.protocol + "//html5te.st/" + this.options.id + "'>html5te.st/" + this.options.id + "</a></p>" +
548
"<p>Or scan this QR-code:</p>" +
550
"<img src='https://chart.googleapis.com/chart?chs=200x200&cht=qr&chl=" + encodeURIComponent(document.location.protocol + "//html5te.st/" + this.options.id) + "&choe=UTF-8' width='200' height='200'>" +
552
"<p>The unique id for this test is:<br><code>" + this.options.id + "</code></p>" +
555
if (this.options.onSave) {
556
this.options.onSave();
564
var el = e.target || e.srcElement;
566
while (el != this.parent && ignore == false) {
567
if (el.className.indexOf('popupPanel') != -1) ignore = true;
578
this.popup.style.display = 'block';
583
this.popup.style.display = 'none';
589
var Feedback = function() { this.initialize.apply(this, arguments) };
590
Feedback.prototype = {
591
initialize: function(parent, options) {
594
this.parent = parent;
595
this.options = options;
597
this.popup = document.createElement('div');
598
this.popup.className = 'popupPanel pointsRight feedback';
599
this.popup.style.display = 'none';
600
this.parent.appendChild(this.popup);
602
this.popup.addEventListener('click', function(e) { e.stopPropagation() }, false)
603
this.popup.addEventListener('touchstart', function(e) { e.stopPropagation() }, false)
605
document.addEventListener('click', this.close.bind(this), false)
606
document.addEventListener('touchstart', this.close.bind(this), false)
614
this.popup.innerHTML +=
615
"<div id='feedback'>" +
616
"<h3>What is wrong with this identification?</h3>" +
617
"<textarea id='correction'>" + this.options.suggestion + "</textarea>"+
618
"<button id='sendCorrection'><span>✔</span>Send correction</button>" +
621
this.popup.style.display = 'block';
623
var button = document.getElementById('sendCorrection')
624
button.addEventListener('click', this.send.bind(this), false);
628
var field = document.getElementById('correction')
630
if (field.value != this.options.suggestion) {
631
if (this.options.onFeedback) {
632
this.options.onFeedback(field.value);
640
this.popup.style.display = 'none';
641
this.popup.parentNode.removeChild(this.popup);
643
if (this.options.onClose) {
644
this.options.onClose();
651
var ResultsTable = function() { this.initialize.apply(this, arguments) };
652
ResultsTable.prototype = {
654
initialize: function(options) {
655
this.parent = options.parent;
656
this.tests = options.tests;
658
title: options.title || '',
659
browsers: options.browsers || [],
660
columns: options.columns || 2,
661
distribute: options.distribute || false,
662
header: options.header || false,
663
links: options.links || false,
664
grading: options.grading || false,
665
features: options.features || false,
666
explainations: options.explainations || false,
668
onChange: options.onChange || false
675
var cell = that.panel.parentNode;
678
while (node.parentNode) {
679
if (node == that.panel) return;
680
node = node.parentNode;
683
that.panel.parentNode.removeChild(that.panel);
687
while (node.parentNode) {
688
if (node == cell) return e.stopPropagation();
689
node = node.parentNode;
694
document.addEventListener('click', close, true)
695
document.addEventListener('touchstart', close, true)
697
this.data = [ null ];
699
this.createCategories(this.parent, this.tests);
702
updateColumn: function(column, data) {
703
this.data[column] = data;
705
for (var c = 0; c < this.tests.length; c++)
706
for (var i = 0; i < this.tests[c].items.length; i++) {
707
var test = this.tests[c].items[i];
709
if (typeof test != 'string') {
710
if (typeof test != 'undefined') {
714
if (match = (new RegExp(test.id + "=([0-9]+)(?:\\/([0-9]+))?(?:\\+([0-9]+))?")).exec(data.points)) {
716
if (match[2]) maximum = match[2];
719
var row = document.getElementById('head-' + test.id);
720
var cell = row.childNodes[0].firstChild.nextSibling;
722
var content = "<div class='grade'>";
724
if (this.options.grading) {
726
var percent = parseInt(points / maximum * 100, 10);
728
case percent == 0: grade = 'none'; break;
729
case percent <= 30: grade = 'badly'; break;
730
case percent <= 60: grade = 'reasonable'; break;
731
case percent <= 95: grade = 'good'; break;
732
default: grade = 'great'; break;
735
if (points == maximum)
736
content += "<span class='" + grade + "'>" + points + "</span>";
738
content += "<span class='" + grade + "'>" + points + "/" + maximum + "</span>";
740
content += "<span>" + points + "</span>";
745
cell.innerHTML = content;
746
this.updateItems(column, data, test.items);
752
updateItems: function(column, data, tests) {
753
var count = [ 0, 0 ];
755
for (var i = 0; i < tests.length; i++) {
756
if (typeof tests[i] != 'string') {
757
var key = tests[i].key;
759
var row = document.getElementById('row-' + key);
760
var cell = row.childNodes[column + 1];
762
if (typeof tests[i].items != 'undefined') {
763
var results = this.updateItems(column, data, tests[i].items);
765
if (results[0] == results[1])
766
cell.innerHTML = '<div>Yes <span class="check">✔</span></div>';
767
else if (results[1] == 0)
768
cell.innerHTML = '<div>No <span class="ballot">✘</span></div>';
770
cell.innerHTML = '<div><span class="partially">Partial</span> <span class="partial">○</span></div>';
774
if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(data.results)) {
775
var result = parseInt(match[1], 10);
779
case !! (result & BUGGY): cell.innerHTML = '<div>Buggy <span class="buggy"></span></div>'; break;
780
case !! (result & OLD): cell.innerHTML = '<div>Partial <span class="partial">○</span></div>'; count[1]++; break;
781
case !! (result & PREFIX): cell.innerHTML = '<div>Prefixed <span class="check">✔</span></div>'; count[1]++; break;
782
default: cell.innerHTML = '<div>Yes <span class="check">✔</span></div>'; count[1]++; break;
787
case !! (result & UNKNOWN): cell.innerHTML = '<div>Unknown <span class="partial">?</span></div>'; break;
788
case !! (result & BLOCKED): cell.innerHTML = '<div>Not functional <span class="buggy">!</span></div>'; break;
789
case !! (result & DISABLED): cell.innerHTML = '<div>Disabled <span class="ballot">✘</span></div>'; break;
790
default: cell.innerHTML = '<div>No <span class="ballot">✘</span></div>'; break;
794
cell.innerHTML = '<div><span class="partially">Unknown</span> <span class="partial">?</span></div>';
805
createCategories: function(parent, tests) {
808
left = document.createElement('div');
809
left.className = 'left';
810
left.innerHTML = '<div></div>';
811
parent.appendChild(left);
813
right = document.createElement('div');
814
right.className = 'right';
815
right.innerHTML = '<div></div>';
816
parent.appendChild(right);
819
for (var i = 0; i < tests.length; i++) {
820
var container = parent;
821
if (tests[i].column == 'left') container = left.firstChild;
822
if (tests[i].column == 'right') container = right.firstChild;
824
var div = document.createElement('div');
825
div.className = 'category ' + tests[i].id;
826
div.id = 'category-' + tests[i].id;
827
container.appendChild(div);
829
var h2 = document.createElement('h2');
830
h2.innerHTML = tests[i].name;
833
this.createSections(div, tests[i].items);
837
createSections: function(parent, tests) {
838
for (var i = 0; i < tests.length; i++) {
839
var table = document.createElement('table');
840
table.cellSpacing = 0;
841
table.id = 'table-' + tests[i].id;
842
parent.appendChild(table);
844
var thead = document.createElement('thead');
845
table.appendChild(thead);
847
var tr = document.createElement('tr');
848
tr.id = 'head-' + tests[i].id;
849
thead.appendChild(tr);
851
var th = document.createElement('th');
852
th.innerHTML = tests[i].name + "<div></div>";
853
th.colSpan = this.options.columns + 1;
856
if (typeof tests[i].items != 'undefined') {
857
var tbody = document.createElement('tbody');
858
table.appendChild(tbody);
860
var status = typeof tests[i].status != 'undefined' ? tests[i].status : '';
862
this.createItems(tbody, 0, tests[i].items, {
871
createItems: function(parent, level, tests, data) {
874
for (var i = 0; i < tests.length; i++) {
875
var tr = document.createElement('tr');
876
parent.appendChild(tr);
878
if (typeof tests[i] == 'string') {
879
if (this.options.explainations || tests[i].substr(0, 4) != '<em>') {
880
var th = document.createElement('th');
881
th.colSpan = this.options.columns + 1;
882
th.className = 'details';
885
th.innerHTML = tests[i];
888
var key = tests[i].key;
890
var th = document.createElement('th');
891
th.innerHTML = "<div><span>" + tests[i].name + "</span></div>";
894
for (var c = 0; c < this.options.columns; c++) {
895
var td = document.createElement('td');
899
tr.id = 'row-' + key;
902
tr.className = 'isChild';
905
if (typeof tests[i].items != 'undefined') {
908
if (this.options.links) {
909
if (typeof tests[i].urls != 'undefined') {
910
urls = tests[i].urls;
912
else if (typeof tests[i].url != 'undefined') {
913
urls = { 'w3c': tests[i].url };
917
tr.className += 'hasChild';
919
var children = this.createItems(parent, level + 1, tests[i].items, {
921
status: typeof tests[i].status != 'undefined' ? tests[i].status : data.status,
925
this.hideChildren(tr, children);
927
(function(that, tr, th, children) {
928
th.onclick = function() {
929
that.toggleChildren(tr, children);
931
})(this, tr, th, children);
935
var showLinks = false;
937
if (typeof tests[i].value != 'undefined') {
938
value = tests[i].value;
941
if (typeof tests[i].urls != 'undefined') {
942
urls = tests[i].urls;
945
else if (typeof tests[i].url != 'undefined') {
946
urls = [ [ 'w3c', tests[i].url ] ];
950
th.className = 'hasLink';
952
(function(that, th, data) {
953
th.onclick = function() {
954
new FeaturePopup(th, data);
960
status: typeof tests[i].status != 'undefined' ? tests[i].status : data.status,
961
urls: (urls || []).concat(data.urls || [])
972
toggleChildren: function(element, ids) {
973
if (element.className.indexOf(' hidden') == -1) {
974
this.hideChildren(element, ids);
976
this.showChildren(element, ids);
980
showChildren: function(element, ids) {
981
element.className = element.className.replace(' hidden', '');
983
for (var i = 0; i < ids.length; i++) {
984
var e = document.getElementById(ids[i]);
985
e.style.display = 'table-row';
989
hideChildren: function(element, ids) {
990
element.className = element.className.replace(' hidden', '');
991
element.className += ' hidden';
993
for (var i = 0; i < ids.length; i++) {
994
var e = document.getElementById(ids[i]);
995
e.style.display = 'none';
1002
var FeaturePopup = function() { this.initialize.apply(this, arguments) };
1003
FeaturePopup.current = null;
1004
FeaturePopup.prototype = {
1005
initialize: function(parent, data) {
1006
if (FeaturePopup.current) {
1007
FeaturePopup.current.close();
1010
var maximum = data.value;
1011
if (typeof maximum == 'object') {
1012
maximum = maximum.maximum;
1016
content += "<div class='info'>";
1017
content += "<div class='column left status " + data.status + "'><span>" + data.status + "</span></div>";
1018
content += "<div class='column middle" + (maximum ? '' : ' none') + "'><em>" + ( maximum || '✘' ) + "</em> <span>" + (maximum != 1 ? 'Points' : 'Point') + "</span></div>";
1019
content += "<div class='column right'><a href='/compare/feature/" + data.id +".html' class='compare'><span>Compare</span></a></div>";
1020
content += "</div>";
1021
content += "<div class='links'>";
1023
for (var i = 0; i < data.urls.length; i++) {
1024
if (data.urls[i][0] == 'w3c') content += "<a href='" + data.urls[i][1] + "' class='w3c'>Go to the specification at W3C.org</a>";
1025
if (data.urls[i][0] == 'whatwg') content += "<a href='" + data.urls[i][1] + "' class='whatwg'>Go to the specification at Whatwg.org</a>";
1026
if (data.urls[i][0] == 'khronos') content += "<a href='" + data.urls[i][1] +"' class='khronos'>Go to the specification at Khronos.org</a>";
1027
if (data.urls[i][0] == 'ietf') content += "<a href='" + data.urls[i][1] +"' class='ietf'>Go to the specification at IETF.org</a>";
1028
if (data.urls[i][0] == 'webm') content += "<a href='" + data.urls[i][1] +"' class='webm'>Go to the specification at WebMproject.org</a>";
1029
if (data.urls[i][0] == 'xiph') content += "<a href='" + data.urls[i][1] +"' class='xiph'>Go to the specification at Xiph.org</a>";
1030
if (data.urls[i][0] == 'ecma') content += "<a href='" + data.urls[i][1] +"' class='ecma'>Go to the specification at ECMA</a>";
1031
if (data.urls[i][0] == 'ricg') content += "<a href='" + data.urls[i][1] +"' class='ricg'>Go to Responsive Images Community Group</a>";
1032
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>";
1033
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>";
1036
content += "</div>";
1038
this.panel = document.createElement('div');
1039
this.panel.className = 'linksPanel popupPanel pointsLeft';
1040
this.panel.innerHTML = content;
1041
parent.appendChild(this.panel);
1043
FeaturePopup.current = this;
1047
this.panel.parentNode.removeChild(this.panel);
1048
FeaturePopup.current = null;
1052
document.addEventListener('click', function() { if (FeaturePopup.current) FeaturePopup.current.close() }, true)
1053
document.addEventListener('touchstart', function() { if (FeaturePopup.current) FeaturePopup.current.close() }, true)