~chromium-team/chromium-browser/focal-stable

« back to all changes in this revision

Viewing changes to debian/tests/data/HTML5test/scripts/base.js

  • Committer: Olivier Tilloy
  • Date: 2019-06-12 07:56:52 UTC
  • mfrom: (1494.1.4 eoan-snap)
  • Revision ID: olivier.tilloy@canonical.com-20190612075652-2mej8weep7syq5j9
* Upstream release: 75.0.3770.80
  - CVE-2019-5828: Use after free in ServiceWorker.
  - CVE-2019-5829: Use after free in Download Manager.
  - CVE-2019-5830: Incorrectly credentialed requests in CORS.
  - CVE-2019-5831: Incorrect map processing in V8.
  - CVE-2019-5832: Incorrect CORS handling in XHR.
  - CVE-2019-5833: Inconsistent security UI placement.
  - CVE-2019-5834: URL spoof in Omnibox on iOS.
  - CVE-2019-5835: Out of bounds read in Swiftshader.
  - CVE-2019-5836: Heap buffer overflow in Angle.
  - CVE-2019-5837: Cross-origin resources size disclosure in Appcache.
  - CVE-2019-5838: Overly permissive tab access in Extensions.
  - CVE-2019-5839: Incorrect handling of certain code points in Blink.
  - CVE-2019-5840: Popup blocker bypass.
* Install the chromium snap in place of the debian packages, and make the wrapper script rename the desktop file in well-known desktop launchers (currently GNOME Shell and Unity)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
 
3
 
 
4
 
        /* Polyfills */
5
 
 
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");
11
 
            }
12
 
 
13
 
            var aArgs = Array.prototype.slice.call(arguments, 1),
14
 
                fToBind = this,
15
 
                fNOP = function () {},
16
 
                fBound = function () {
17
 
                  return fToBind.apply(this instanceof fNOP && oThis
18
 
                                         ? this
19
 
                                         : oThis,
20
 
                                       aArgs.concat(Array.prototype.slice.call(arguments)));
21
 
                };
22
 
 
23
 
            fNOP.prototype = this.prototype;
24
 
            fBound.prototype = new fNOP();
25
 
 
26
 
            return fBound;
27
 
          };
28
 
        }
29
 
 
30
 
        (function(win, doc){
31
 
                if(win.addEventListener)return;         //No need to polyfill
32
 
 
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}
39
 
                                fn.call(self, e);
40
 
                        });
41
 
                }
42
 
                function addListen(obj, i){
43
 
                        if(!obj) return obj;
44
 
                        if(i = obj.length)while(i--)obj[i].addEventListener = addEvent;
45
 
                        else obj.addEventListener = addEvent;
46
 
                        return obj;
47
 
                }
48
 
 
49
 
                addListen([doc, win]);
50
 
                if('Element' in win)win.Element.prototype.addEventListener = addEvent;                  //IE8
51
 
                else{                                                                                                                                                   //IE < 8
52
 
                        doc.attachEvent('onreadystatechange', function(){addListen(doc.all)});          //Make sure we also init at domReady
53
 
                        docHijack('getElementsByTagName');
54
 
                        docHijack('getElementById');
55
 
                        docHijack('createElement');
56
 
                        addListen(doc.all);
57
 
                }
58
 
        })(window, document);
59
 
 
60
 
 
61
 
 
62
 
 
63
 
 
64
 
        /* Utility functions */
65
 
 
66
 
        var tim = (function(){
67
 
            var starts  = "\\{\\{",
68
 
                ends    = "\\}\\}",
69
 
                path    = "[a-z0-9_][\\.a-z0-9_]*", // e.g. config.person.name
70
 
                pattern = new RegExp(starts + "("+ path +")" + ends, "gim"),
71
 
                undef;
72
 
 
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("."),
77
 
                        len = path.length,
78
 
                        lookup = data,
79
 
                        i = 0;
80
 
 
81
 
                    for (; i < len; i++){
82
 
                        lookup = lookup[path[i]];
83
 
 
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){
88
 
                                return notFound;
89
 
                            }
90
 
                            // Throw error
91
 
                            throw "Tim: '" + path[i] + "' not found in " + tag;
92
 
                        }
93
 
 
94
 
                        // Success! Return the required value
95
 
                        if (i === len - 1){
96
 
                            return lookup;
97
 
                        }
98
 
                    }
99
 
                });
100
 
            };
101
 
        }());
102
 
 
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, '\\"');
112
 
        }
113
 
 
114
 
 
115
 
        var NO = 0,
116
 
                YES = 1,
117
 
                OLD = 2,
118
 
                BUGGY = 4,
119
 
                PREFIX = 8,
120
 
                BLOCKED = 16,
121
 
                DISABLED = 32,
122
 
                UNCONFIRMED = 64,
123
 
                UNKNOWN = 128,
124
 
                EXPERIMENTAL = 256;
125
 
 
126
 
 
127
 
 
128
 
        var Metadata = function() { this.initialize.apply(this, arguments) };
129
 
        Metadata.prototype = {
130
 
                initialize: function(data) {
131
 
                        this.data = data;
132
 
                        this.list = {};
133
 
 
134
 
                        for (var i = 0; i < this.data.length; i++) {
135
 
                                this.iterate(this.data[i].items, '', -1, []);
136
 
                        }
137
 
                },
138
 
 
139
 
                iterate: function(data, prefix, level, path) {
140
 
                        for (var i = 0; i < data.length; i++) {
141
 
                                var key;
142
 
 
143
 
                                if (typeof data[i].id != 'undefined') {
144
 
                                        key = prefix + (prefix == '' ? '' : '.') + data[i].id;
145
 
                                }
146
 
 
147
 
                                if (typeof data[i].key != 'undefined') {
148
 
                                        key = data[i].key;
149
 
                                }
150
 
 
151
 
                                if (key) {
152
 
                                        path[level + 1] = key;
153
 
 
154
 
                                        if (typeof data[i].key == 'undefined') {
155
 
                                                data[i].key = key;
156
 
                                        }
157
 
 
158
 
                                        if (typeof data[i].name != 'undefined') {
159
 
                                                this.list[key] = {
160
 
                                                        name: data[i].name,
161
 
                                                        path: path.slice(0, level + 1)
162
 
                                                };
163
 
                                        }
164
 
 
165
 
                                        if (typeof data[i].items != 'undefined') {
166
 
                                                this.iterate(data[i].items, key, level + 1, path);
167
 
                                        }
168
 
                                }
169
 
                        }
170
 
                },
171
 
 
172
 
                getItem: function(key) {
173
 
                        var item = this.list[key];
174
 
                        return item;
175
 
                },
176
 
 
177
 
                getTrail: function(key, separator) {
178
 
                        var item = this.list[key];
179
 
 
180
 
                        if (item) {
181
 
                                var trail = [];
182
 
 
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);
186
 
                                        } else {
187
 
                                                trail.push('?');
188
 
                                        }
189
 
                                }
190
 
 
191
 
                                trail.push(item.name);
192
 
 
193
 
                                return trail.join(separator);
194
 
                        }
195
 
 
196
 
                        return '?';
197
 
                }
198
 
        }
199
 
 
200
 
        var Calculate = function() { this.initialize.apply(this, arguments) };
201
 
        Calculate.prototype = {
202
 
                initialize: function(test, data) {
203
 
                        this.parameters = {
204
 
                                test:           test,
205
 
                                data:           data
206
 
                        };
207
 
 
208
 
                        this.maximum = 0;
209
 
                        this.score = 0;
210
 
                        this.points = [];
211
 
 
212
 
                        for (var i = 0; i < this.parameters.data.length; i++) {
213
 
                                this.iterate(this.parameters.data[i].items, '');
214
 
                        }
215
 
 
216
 
                        this.points = this.points.join(',');
217
 
                },
218
 
 
219
 
                iterate: function(data, prefix) {
220
 
                        for (var i = 0; i < data.length; i++) {
221
 
                                if (data[i].key) {
222
 
                                        if (prefix == '') {
223
 
                                                var score = this.score;
224
 
                                                var maximum = this.maximum;
225
 
                                        }
226
 
 
227
 
                                        if (typeof data[i].value != 'undefined') {
228
 
                                                this.calculate(data[i].key, data[i]);
229
 
                                        }
230
 
 
231
 
                                        if (typeof data[i].items != 'undefined') {
232
 
                                                this.iterate(data[i].items, data[i].key);
233
 
                                        }
234
 
 
235
 
                                        if (prefix == '') {
236
 
                                                this.points.push(data[i].id + '=' + (this.score - score) + '/' + (this.maximum - maximum));
237
 
                                        }
238
 
                                }
239
 
                        }
240
 
                },
241
 
 
242
 
                calculate: function(prefix, data) {
243
 
                        var result = true;
244
 
                        var value = typeof data.value == 'object' ? data.value : { maximum: data.value };
245
 
 
246
 
                        if (typeof data.value.conditional == 'undefined') {
247
 
                                this.maximum += value.maximum;
248
 
                        }
249
 
 
250
 
                        if (typeof data.items == 'object') {
251
 
                                result = 0;
252
 
                                passed = true;
253
 
 
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);
257
 
                                                passed &= r | YES;
258
 
                                                result |= r;
259
 
                                        }
260
 
                                }
261
 
 
262
 
                                if (!passed) {
263
 
                                        result = false;
264
 
                                }
265
 
                        }
266
 
                        else {
267
 
                                result = this.getResult(prefix);
268
 
                        }
269
 
 
270
 
                        if (result & YES) {
271
 
                                var valid = true;
272
 
 
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));
276
 
 
277
 
                                                if (conditional & YES) {
278
 
                                                        valid = false;
279
 
                                                }
280
 
                                        }
281
 
                                }
282
 
 
283
 
                                if (valid) {
284
 
                                        if (result & PREFIX && typeof value.award == 'object' && typeof value.award.PREFIX != 'undefined') {
285
 
                                                this.score += value.award.PREFIX;
286
 
                                        }
287
 
                                        else if (result & BUGGY && typeof value.award == 'object' && typeof value.award.BUGGY != 'undefined') {
288
 
                                                this.score += value.award.BUGGY;
289
 
                                        }
290
 
                                        else if (result & OLD && typeof value.award == 'object' && typeof value.award.OLD != 'undefined') {
291
 
                                                this.score += value.award.OLD;
292
 
                                        }
293
 
                                        else {
294
 
                                                this.score += value.maximum;
295
 
                                        }
296
 
                                }
297
 
                        }
298
 
                },
299
 
 
300
 
                getResult: function(key) {
301
 
                        if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(this.parameters.test.results)) {
302
 
                                return parseInt(match[1], 10);
303
 
                        }
304
 
 
305
 
                        return null;
306
 
                }
307
 
        };
308
 
 
309
 
 
310
 
 
311
 
        /* Base UI functions */
312
 
 
313
 
        var Index = function() { this.initialize.apply(this, arguments) };
314
 
        Index.prototype = {
315
 
                initialize: function(options) {
316
 
                        var that = this;
317
 
                        this.options = options;
318
 
 
319
 
                        var menu = document.createElement('div');
320
 
                        menu.id = 'indexmenu';
321
 
                        options.index.appendChild(menu);
322
 
 
323
 
                        var categories = document.createElement('ul');
324
 
                        menu.appendChild(categories);
325
 
 
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);
330
 
 
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);
336
 
 
337
 
                                if (options.tests[i].items.length) {
338
 
                                        var items = document.createElement('ul');
339
 
                                        category.appendChild(items);
340
 
 
341
 
                                        for (var j = 0; j < options.tests[i].items.length; j++) {
342
 
                                                var item = document.createElement('li');
343
 
                                                items.appendChild(item);
344
 
 
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);
350
 
                                        }
351
 
                                }
352
 
                        }
353
 
 
354
 
                        var button = document.createElement('button');
355
 
                        button.innerHTML = '';
356
 
                        button.id = 'indexbutton';
357
 
                        button.onclick = this.toggleIndex;
358
 
                        options.index.appendChild(button);
359
 
 
360
 
                        options.wrapper.onclick = this.closeIndex;
361
 
                },
362
 
 
363
 
                toggleIndex: function() {
364
 
                        if (document.body.className.indexOf(' indexVisible') == -1) {
365
 
                                document.body.className = document.body.className.replace(' indexVisible', '') + ' indexVisible';
366
 
                        } else {
367
 
                                document.body.className = document.body.className.replace(' indexVisible', '');
368
 
                        }
369
 
                },
370
 
 
371
 
                closeIndex: function() {
372
 
                        document.body.className = document.body.className.replace(' indexVisible', '');
373
 
                }
374
 
        }
375
 
 
376
 
 
377
 
        var Confirm = function() { this.initialize.apply(this, arguments) };
378
 
        Confirm.prototype = {
379
 
                initialize: function(parent, options) {
380
 
                        this.options = options;
381
 
 
382
 
                        this.useragent = document.createElement('p');
383
 
                        this.useragent.className = 'useragent';
384
 
                        parent.appendChild(this.useragent);
385
 
                        this.useragent.innerHTML = 'You are using ' + Browsers;
386
 
 
387
 
                        this.confirm = document.createElement('span');
388
 
                        this.confirm.innerHTML = 'Correct?';
389
 
                        this.useragent.appendChild(this.confirm);
390
 
 
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);
396
 
 
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);
402
 
 
403
 
                        this.thanks = document.createElement('span');
404
 
                        this.thanks.style.display = 'none';
405
 
                        this.thanks.innerHTML = 'Thanks!';
406
 
                        this.useragent.appendChild(this.thanks);
407
 
                },
408
 
 
409
 
                confirmUseragent: function() {
410
 
                        this.options.onConfirm();
411
 
                        this.showThanks();
412
 
                },
413
 
 
414
 
                reportUseragent: function() {
415
 
                        this.options.onReport();
416
 
 
417
 
                        new Feedback(this.confirm, {
418
 
                                suggestion:     'I am using' + ' ' + Browsers,
419
 
 
420
 
                                onFeedback:     function(value) {
421
 
                                                                this.options.onFeedback(value);
422
 
                                                        }.bind(this),
423
 
 
424
 
                                onClose:        function() {
425
 
                                                                this.showThanks();
426
 
                                                        }.bind(this)
427
 
                        });
428
 
                },
429
 
 
430
 
                showThanks: function() {
431
 
                        this.confirm.style.display = 'none';
432
 
                        this.thanks.style.display = 'inline';
433
 
 
434
 
                        window.setTimeout(function() {
435
 
                                this.thanks.style.display = 'none';
436
 
                        }.bind(this), 2500);
437
 
                }
438
 
        }
439
 
 
440
 
 
441
 
        var Share = function() { this.initialize.apply(this, arguments) };
442
 
        Share.prototype = {
443
 
                initialize: function(parent, options) {
444
 
                        var that = this;
445
 
 
446
 
                        this.parent = parent;
447
 
                        this.options = options;
448
 
                        this.created = false;
449
 
 
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);
454
 
 
455
 
                        this.parent.addEventListener('click', this.open.bind(this), true)
456
 
                        this.parent.addEventListener('touchstart', this.open.bind(this), true)
457
 
 
458
 
                        document.addEventListener('click', this.close.bind(this), true)
459
 
                        document.addEventListener('touchstart', this.close.bind(this), true)
460
 
                },
461
 
 
462
 
                create: function() {
463
 
                        this.created = true;
464
 
 
465
 
                        this.popup.innerHTML +=
466
 
                                "<div id='share'><div>" +
467
 
 
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?' " +
473
 
                                        "data-lang='en' "+
474
 
                                        "data-count='vertical'"+
475
 
                                        ">Tweet</a>" +
476
 
                                "</div>" +
477
 
 
478
 
                                "<div id='facebook'>" +
479
 
                                "<iframe src='//www.facebook.com/plugins/like.php?href=https%3A%2F%2Fhtml5test.com&amp;width=60&amp;height=65&amp;colorscheme=light&amp;layout=box_count&amp;action=like&amp;show_faces=false&amp;send=false&amp;appId=202643099847776' scrolling='no' frameborder='0' style='border:none; overflow:hidden; width:The pixel width of the pluginpx; height:65px;' allowTransparency='true'></iframe>" +
480
 
                                "</div>" +
481
 
 
482
 
                                "<div id='google'>" +
483
 
                                "<div class='g-plusone' " +
484
 
                                        "data-href='https://html5test.com' " +
485
 
                                        "data-size='tall'" +
486
 
                                        "></div>" +
487
 
                                "</div>" +
488
 
 
489
 
                                "</div></div>";
490
 
 
491
 
 
492
 
 
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");
496
 
 
497
 
                        (function() {
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);
501
 
                        })();
502
 
                },
503
 
 
504
 
                open: function(e) {
505
 
                        e.preventDefault();
506
 
 
507
 
                        if (!this.created) {
508
 
                                this.create();
509
 
                        }
510
 
 
511
 
                        this.popup.style.display = 'block';
512
 
                },
513
 
 
514
 
                close: function() {
515
 
                        this.popup.style.display = 'none';
516
 
                }
517
 
        }
518
 
 
519
 
 
520
 
        var Save = function() { this.initialize.apply(this, arguments) };
521
 
        Save.prototype = {
522
 
                initialize: function(parent, options) {
523
 
                        var that = this;
524
 
 
525
 
                        this.parent = parent;
526
 
                        this.options = options;
527
 
                        this.created = false;
528
 
 
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);
533
 
 
534
 
                        document.addEventListener('click', this.close.bind(this), false)
535
 
                        document.addEventListener('touchstart', this.close.bind(this), false)
536
 
 
537
 
                        this.parent.addEventListener('click', this.open.bind(this), true)
538
 
                        this.parent.addEventListener('touchstart', this.open.bind(this), true)
539
 
                },
540
 
 
541
 
                create: function() {
542
 
                        this.created = true;
543
 
 
544
 
                        this.popup.innerHTML +=
545
 
                                "<div id='save'>" +
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>" +
549
 
                                "<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'>" +
551
 
                                "</p>" +
552
 
                                "<p>The unique id for this test is:<br><code>" + this.options.id + "</code></p>" +
553
 
                                "</div>";
554
 
 
555
 
                        if (this.options.onSave) {
556
 
                                this.options.onSave();
557
 
                        }
558
 
                },
559
 
 
560
 
                open: function(e) {
561
 
                        e.stopPropagation();
562
 
 
563
 
                        var ignore = false;
564
 
                        var el = e.target || e.srcElement;
565
 
 
566
 
                        while (el != this.parent && ignore == false) {
567
 
                                if (el.className.indexOf('popupPanel') != -1) ignore = true;
568
 
                                el = el.parentNode;
569
 
                        }
570
 
 
571
 
                        if (!ignore) {
572
 
                                e.preventDefault();
573
 
 
574
 
                                if (!this.created) {
575
 
                                        this.create();
576
 
                                }
577
 
 
578
 
                                this.popup.style.display = 'block';
579
 
                        }
580
 
                },
581
 
 
582
 
                close: function(e) {
583
 
                        this.popup.style.display = 'none';
584
 
                }
585
 
        }
586
 
 
587
 
 
588
 
 
589
 
        var Feedback = function() { this.initialize.apply(this, arguments) };
590
 
        Feedback.prototype = {
591
 
                initialize: function(parent, options) {
592
 
                        var that = this;
593
 
 
594
 
                        this.parent = parent;
595
 
                        this.options = options;
596
 
 
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);
601
 
 
602
 
                        this.popup.addEventListener('click', function(e) { e.stopPropagation() }, false)
603
 
                        this.popup.addEventListener('touchstart', function(e) { e.stopPropagation() }, false)
604
 
 
605
 
                        document.addEventListener('click', this.close.bind(this), false)
606
 
                        document.addEventListener('touchstart', this.close.bind(this), false)
607
 
 
608
 
                        this.create();
609
 
                },
610
 
 
611
 
                create: function() {
612
 
                        this.created = true;
613
 
 
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>" +
619
 
                                "</div>";
620
 
 
621
 
                        this.popup.style.display = 'block';
622
 
 
623
 
                        var button = document.getElementById('sendCorrection')
624
 
                        button.addEventListener('click', this.send.bind(this), false);
625
 
                },
626
 
 
627
 
                send: function() {
628
 
                        var field = document.getElementById('correction')
629
 
 
630
 
                        if (field.value != this.options.suggestion) {
631
 
                                if (this.options.onFeedback) {
632
 
                                        this.options.onFeedback(field.value);
633
 
                                }
634
 
                        }
635
 
 
636
 
                        this.close();
637
 
                },
638
 
 
639
 
                close: function(e) {
640
 
                        this.popup.style.display = 'none';
641
 
                        this.popup.parentNode.removeChild(this.popup);
642
 
 
643
 
                        if (this.options.onClose) {
644
 
                                this.options.onClose();
645
 
                        }
646
 
                }
647
 
        }
648
 
 
649
 
 
650
 
 
651
 
        var ResultsTable = function() { this.initialize.apply(this, arguments) };
652
 
        ResultsTable.prototype = {
653
 
 
654
 
                initialize: function(options) {
655
 
                        this.parent = options.parent;
656
 
                        this.tests = options.tests;
657
 
                        this.options = {
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,
667
 
 
668
 
                                onChange:               options.onChange || false
669
 
                        }
670
 
 
671
 
                        var that = this;
672
 
 
673
 
                        function close(e) {
674
 
                                if (that.panel) {
675
 
                                        var cell = that.panel.parentNode;
676
 
                                        var node = e.target;
677
 
 
678
 
                                        while (node.parentNode) {
679
 
                                                if (node == that.panel) return;
680
 
                                                node = node.parentNode;
681
 
                                        }
682
 
 
683
 
                                        that.panel.parentNode.removeChild(that.panel);
684
 
                                        that.panel = null;
685
 
 
686
 
                                        var node = e.target;
687
 
                                        while (node.parentNode) {
688
 
                                                if (node == cell) return e.stopPropagation();
689
 
                                                node = node.parentNode;
690
 
                                        }
691
 
                                }
692
 
                        }
693
 
 
694
 
                        document.addEventListener('click', close, true)
695
 
                        document.addEventListener('touchstart', close, true)
696
 
 
697
 
                        this.data = [ null ];
698
 
 
699
 
                        this.createCategories(this.parent, this.tests);
700
 
                },
701
 
 
702
 
                updateColumn: function(column, data) {
703
 
                        this.data[column] = data;
704
 
 
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];
708
 
 
709
 
                                if (typeof test != 'string') {
710
 
                                        if (typeof test != 'undefined') {
711
 
                                                var points = 0;
712
 
                                                var maximum = 0;
713
 
 
714
 
                                                if (match = (new RegExp(test.id + "=([0-9]+)(?:\\/([0-9]+))?(?:\\+([0-9]+))?")).exec(data.points)) {
715
 
                                                        points = match[1];
716
 
                                                        if (match[2]) maximum = match[2];
717
 
                                                }
718
 
 
719
 
                                                var row = document.getElementById('head-' + test.id);
720
 
                                                var cell = row.childNodes[0].firstChild.nextSibling;
721
 
 
722
 
                                                var content = "<div class='grade'>";
723
 
 
724
 
                                                if (this.options.grading) {
725
 
                                                        var grade = '';
726
 
                                                        var percent = parseInt(points / maximum * 100, 10);
727
 
                                                        switch (true) {
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;
733
 
                                                        }
734
 
 
735
 
                                                        if (points == maximum)
736
 
                                                                content += "<span class='" + grade + "'>" + points + "</span>";
737
 
                                                        else
738
 
                                                                content += "<span class='" + grade + "'>" + points + "/" + maximum + "</span>";
739
 
                                                } else {
740
 
                                                        content += "<span>" + points + "</span>";
741
 
                                                }
742
 
 
743
 
                                                content += "</div>";
744
 
 
745
 
                                                cell.innerHTML = content;
746
 
                                                this.updateItems(column, data, test.items);
747
 
                                        }
748
 
                                }
749
 
                        }
750
 
                },
751
 
 
752
 
                updateItems: function(column, data, tests) {
753
 
                        var count = [ 0, 0 ];
754
 
 
755
 
                        for (var i = 0; i < tests.length; i++) {
756
 
                                if (typeof tests[i] != 'string') {
757
 
                                        var key = tests[i].key;
758
 
 
759
 
                                        var row = document.getElementById('row-' + key);
760
 
                                        var cell = row.childNodes[column + 1];
761
 
 
762
 
                                        if (typeof tests[i].items != 'undefined') {
763
 
                                                var results = this.updateItems(column, data, tests[i].items);
764
 
 
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>';
769
 
                                                else
770
 
                                                        cell.innerHTML = '<div><span class="partially">Partial</span> <span class="partial">○</span></div>';
771
 
                                        }
772
 
 
773
 
                                        else {
774
 
                                                if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(data.results)) {
775
 
                                                        var result = parseInt(match[1], 10);
776
 
 
777
 
                                                        if (result & YES) {
778
 
                                                                switch(true) {
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;
783
 
                                                                }
784
 
                                                        }
785
 
                                                        else {
786
 
                                                                switch(true) {
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;
791
 
                                                                }
792
 
                                                        }
793
 
                                                } else {
794
 
                                                        cell.innerHTML = '<div><span class="partially">Unknown</span> <span class="partial">?</span></div>';
795
 
                                                }
796
 
                                        }
797
 
 
798
 
                                        count[0]++;
799
 
                                }
800
 
                        }
801
 
 
802
 
                        return count;
803
 
                },
804
 
 
805
 
                createCategories: function(parent, tests) {
806
 
                        var left, right;
807
 
 
808
 
                        left = document.createElement('div');
809
 
                        left.className = 'left';
810
 
                        left.innerHTML = '<div></div>';
811
 
                        parent.appendChild(left);
812
 
 
813
 
                        right = document.createElement('div');
814
 
                        right.className = 'right';
815
 
                        right.innerHTML = '<div></div>';
816
 
                        parent.appendChild(right);
817
 
 
818
 
 
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;
823
 
 
824
 
                                var div = document.createElement('div');
825
 
                                div.className = 'category ' + tests[i].id;
826
 
                                div.id = 'category-' + tests[i].id;
827
 
                                container.appendChild(div);
828
 
 
829
 
                                var h2 = document.createElement('h2');
830
 
                                h2.innerHTML = tests[i].name;
831
 
                                div.appendChild(h2);
832
 
 
833
 
                                this.createSections(div, tests[i].items);
834
 
                        }
835
 
                },
836
 
 
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);
843
 
 
844
 
                                var thead = document.createElement('thead');
845
 
                                table.appendChild(thead);
846
 
 
847
 
                                var tr = document.createElement('tr');
848
 
                                tr.id = 'head-' + tests[i].id;
849
 
                                thead.appendChild(tr);
850
 
 
851
 
                                var th = document.createElement('th');
852
 
                                th.innerHTML = tests[i].name + "<div></div>";
853
 
                                th.colSpan = this.options.columns + 1;
854
 
                                tr.appendChild(th);
855
 
 
856
 
                                if (typeof tests[i].items != 'undefined') {
857
 
                                        var tbody = document.createElement('tbody');
858
 
                                        table.appendChild(tbody);
859
 
 
860
 
                                        var status = typeof tests[i].status != 'undefined' ? tests[i].status : '';
861
 
 
862
 
                                        this.createItems(tbody, 0, tests[i].items, {
863
 
                                                id:             tests[i].id,
864
 
                                                status: status,
865
 
                                                urls:   []
866
 
                                        });
867
 
                                }
868
 
                        }
869
 
                },
870
 
 
871
 
                createItems: function(parent, level, tests, data) {
872
 
                        var ids = [];
873
 
 
874
 
                        for (var i = 0; i < tests.length; i++) {
875
 
                                var tr = document.createElement('tr');
876
 
                                parent.appendChild(tr);
877
 
 
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';
883
 
                                                tr.appendChild(th);
884
 
 
885
 
                                                th.innerHTML = tests[i];
886
 
                                        }
887
 
                                } else {
888
 
                                        var key = tests[i].key;
889
 
 
890
 
                                        var th = document.createElement('th');
891
 
                                        th.innerHTML = "<div><span>" + tests[i].name + "</span></div>";
892
 
                                        tr.appendChild(th);
893
 
 
894
 
                                        for (var c = 0; c < this.options.columns; c++) {
895
 
                                                var td = document.createElement('td');
896
 
                                                tr.appendChild(td);
897
 
                                        }
898
 
 
899
 
                                        tr.id = 'row-' + key;
900
 
 
901
 
                                        if (level > 0) {
902
 
                                                tr.className = 'isChild';
903
 
                                        }
904
 
 
905
 
                                        if (typeof tests[i].items != 'undefined') {
906
 
                                                var urls = null;
907
 
 
908
 
                                                if (this.options.links) {
909
 
                                                        if (typeof tests[i].urls != 'undefined') {
910
 
                                                                urls = tests[i].urls;
911
 
                                                        }
912
 
                                                        else if (typeof tests[i].url != 'undefined') {
913
 
                                                                urls = { 'w3c': tests[i].url };
914
 
                                                        }
915
 
                                                }
916
 
 
917
 
                                                tr.className += 'hasChild';
918
 
 
919
 
                                                var children = this.createItems(parent, level + 1, tests[i].items, {
920
 
                                                        id:     key,
921
 
                                                        status: typeof tests[i].status != 'undefined' ? tests[i].status : data.status,
922
 
                                                        urls:   urls
923
 
                                                });
924
 
 
925
 
                                                this.hideChildren(tr, children);
926
 
 
927
 
                                                (function(that, tr, th, children) {
928
 
                                                        th.onclick = function() {
929
 
                                                                that.toggleChildren(tr, children);
930
 
                                                        };
931
 
                                                })(this, tr, th, children);
932
 
                                        } else {
933
 
                                                var urls;
934
 
                                                var value = 0;
935
 
                                                var showLinks = false;
936
 
 
937
 
                                                if (typeof tests[i].value != 'undefined') {
938
 
                                                        value = tests[i].value;
939
 
                                                }
940
 
 
941
 
                                                if (typeof tests[i].urls != 'undefined') {
942
 
                                                        urls = tests[i].urls;
943
 
                                                        showLinks = true;
944
 
                                                }
945
 
                                                else if (typeof tests[i].url != 'undefined') {
946
 
                                                        urls = [ [ 'w3c', tests[i].url ] ];
947
 
                                                        showLinks = true;
948
 
                                                }
949
 
 
950
 
                                                th.className = 'hasLink';
951
 
 
952
 
                                                (function(that, th, data) {
953
 
                                                        th.onclick = function() {
954
 
                                                                new FeaturePopup(th, data);
955
 
                                                        };
956
 
                                                })(this, th, {
957
 
                                                        id:             key,
958
 
                                                        name:   tests[i].name,
959
 
                                                        value:  value,
960
 
                                                        status: typeof tests[i].status != 'undefined' ? tests[i].status : data.status,
961
 
                                                        urls:   (urls || []).concat(data.urls || [])
962
 
                                                });
963
 
                                        }
964
 
 
965
 
                                        ids.push(tr.id);
966
 
                                }
967
 
                        }
968
 
 
969
 
                        return ids;
970
 
                },
971
 
 
972
 
                toggleChildren: function(element, ids) {
973
 
                        if (element.className.indexOf(' hidden') == -1) {
974
 
                                this.hideChildren(element, ids);
975
 
                        } else {
976
 
                                this.showChildren(element, ids);
977
 
                        }
978
 
                },
979
 
 
980
 
                showChildren: function(element, ids) {
981
 
                        element.className = element.className.replace(' hidden', '');
982
 
 
983
 
                        for (var i = 0; i < ids.length; i++) {
984
 
                                var e = document.getElementById(ids[i]);
985
 
                                e.style.display = 'table-row';
986
 
                        }
987
 
                },
988
 
 
989
 
                hideChildren: function(element, ids) {
990
 
                        element.className = element.className.replace(' hidden', '');
991
 
                        element.className += ' hidden';
992
 
 
993
 
                        for (var i = 0; i < ids.length; i++) {
994
 
                                var e = document.getElementById(ids[i]);
995
 
                                e.style.display = 'none';
996
 
                        }
997
 
                }
998
 
        }
999
 
 
1000
 
 
1001
 
 
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();
1008
 
                        }
1009
 
 
1010
 
                        var maximum = data.value;
1011
 
                        if (typeof maximum == 'object') {
1012
 
                                maximum = maximum.maximum;
1013
 
                        }
1014
 
 
1015
 
                        var content = "";
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'>";
1022
 
 
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>";
1034
 
                        }
1035
 
 
1036
 
                        content += "</div>";
1037
 
 
1038
 
                        this.panel = document.createElement('div');
1039
 
                        this.panel.className = 'linksPanel popupPanel pointsLeft';
1040
 
                        this.panel.innerHTML = content;
1041
 
                        parent.appendChild(this.panel);
1042
 
 
1043
 
                        FeaturePopup.current = this;
1044
 
                },
1045
 
 
1046
 
                close: function() {
1047
 
                        this.panel.parentNode.removeChild(this.panel);
1048
 
                        FeaturePopup.current = null;
1049
 
                }
1050
 
        }
1051
 
 
1052
 
        document.addEventListener('click', function() { if (FeaturePopup.current) FeaturePopup.current.close() }, true)
1053
 
        document.addEventListener('touchstart', function() { if (FeaturePopup.current) FeaturePopup.current.close() }, true)