~chromium-team/chromium-browser/trusty-beta

« back to all changes in this revision

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

  • Committer: Olivier Tilloy
  • Date: 2017-10-23 16:46:53 UTC
  • mfrom: (1172.1.38 trusty-stable)
  • Revision ID: olivier.tilloy@canonical.com-20171023164653-zmpfzblxxnkj0jhj
Merge back changes from stable branch:
  * debian/control: bump Standards-Version to 4.1.1
  * debian/patches/set-rpath-on-chromium-executables.patch: updated
  * debian/tests/*:
    - removed stale autopkgtests
    - added new autopkgtests based on chromium's new headless mode
  * debian/source/include-binaries: updated to reflect new binary data in tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
        var DeviceTable = function() { this.initialize.apply(this, arguments) };
 
3
        DeviceTable.prototype = {
 
4
 
 
5
                initialize: function(options) {
 
6
                        this.parent = options.parent;
 
7
 
 
8
                        document.body.addEventListener("click", this.onClose.bind(this), true);
 
9
 
 
10
                        var rows = this.parent.querySelectorAll('tbody tr');
 
11
                        for (var r = 0; r < rows.length; r++) {
 
12
                                rows[r].addEventListener("click", this.onShow.bind(this, rows[r]), true);
 
13
                        }
 
14
 
 
15
                        this.popup = null;
 
16
                },
 
17
 
 
18
                onClose: function(e) {
 
19
                        var child = false;
 
20
 
 
21
                        var el = e.target;
 
22
                        while (el.parentNode) {
 
23
                                if (el == this.parent) {
 
24
                                        child = true;
 
25
                                        break;
 
26
                                }
 
27
 
 
28
                                el = el.parentNode;
 
29
                        }
 
30
 
 
31
                        if (!child) this.close();
 
32
                },
 
33
 
 
34
                onShow: function(row) {
 
35
                        this.close();
 
36
 
 
37
                        var httpRequest;
 
38
                        if (window.XMLHttpRequest) {
 
39
                                httpRequest = new XMLHttpRequest();
 
40
                        } else if (window.ActiveXObject) {
 
41
                                httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
 
42
                        }
 
43
 
 
44
                        httpRequest.open('POST','/api/loadLabDevice', true);
 
45
                        httpRequest.onreadystatechange = process.bind(this);
 
46
                        httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 
47
                        httpRequest.send('id=' + encodeURIComponent(row.getAttribute('data-id')));
 
48
 
 
49
                        function process() {
 
50
                                if (httpRequest.readyState == 4 && httpRequest.responseText != '') {
 
51
                                        var data = JSON.parse(httpRequest.responseText);
 
52
 
 
53
                                        var bb = [
 
54
                                                [ 'ie5', 'ie6', 'IE 5'],
 
55
                                                [ 'ie6', 'ie6', 'IE 6'],
 
56
                                                [ 'ie7', 'ie7', 'IE 7'],
 
57
                                                [ 'ie8', 'ie7', 'IE 8'],
 
58
                                                [ 'ie9', 'ie9', 'IE 9'],
 
59
                                                [ 'ie10', 'ie10', 'IE 10'],
 
60
                                                [ 'ie11', 'ie10', 'IE 11'],
 
61
                                                [ 'ie10_metro', 'ie-metro', 'IE 10'],
 
62
                                                [ 'ie11_metro', 'ie-metro', 'IE 11'],
 
63
                                                [ 'wp7', 'ie-metro', 'IE 9'],
 
64
                                                [ 'wp8', 'ie-metro', 'IE 10'],
 
65
                                                [ 'safari_ios_6', 'safari-ios-6', 'Safari'],
 
66
                                                [ 'safari_ios_7', 'safari-ios-7', 'Safari'],
 
67
                                                [ 'xpress', 'nokia', 'Xpress'],
 
68
                                                [ 'nokia', 'nokia', 'Nokia'],
 
69
                                                [ 'maemo', 'nokia', 'Nokia'],
 
70
                                                [ 'meego', 'nokia', 'Nokia'],
 
71
                                                [ 'webos', 'webos', 'webOS'],
 
72
                                                [ 'android', 'android', 'Android'],
 
73
                                                [ 'bb', 'bb', 'Browser'],
 
74
                                                [ 'bb10', 'bb10', 'Browser'],
 
75
                                                [ 'tabletos', 'tabletos', 'Browser'],
 
76
 
 
77
                                                [ 'chrome', 'chrome', 'Chrome'],
 
78
                                                [ 'coast', 'coast', 'Coast'],
 
79
                                                [ 'diigo', 'diigo', 'Diigo'],
 
80
                                                [ 'dolphin', 'dolphin', 'Dolphin'],
 
81
                                                [ 'firefox', 'firefox', 'Firefox'],
 
82
                                                [ 'ilunascape', 'ilunascape', 'iLunascape'],
 
83
                                                [ 'maxthon', 'maxthon', 'Maxthon'],
 
84
                                                [ 'mercury', 'mercury', 'Mercury'],
 
85
                                                [ 'puffin', 'puffin', 'Puffin'],
 
86
                                                [ 'silk', 'silk', 'Silk'],
 
87
                                                [ 'sleipnir', 'sleipnir', 'Sleipnir'],
 
88
                                                [ 'one', 'one', 'One'],
 
89
                                                [ 'opera', 'opera', 'Opera'],
 
90
                                                [ 'qq', 'qq', 'QQ'],
 
91
                                                [ 'uc-iphone', 'uc-iphone', 'UC Web'],
 
92
                                                [ 'uc-old', 'uc-old', 'UC Web'],
 
93
                                        ]
 
94
 
 
95
                                        var browsers = "<ul class='browsers'>";
 
96
 
 
97
                                        if (data.defaultBrowser || data.defaultFingerprint) {
 
98
                                                browsers += "<li class='default'>";
 
99
                                                var stock = true;
 
100
 
 
101
                                                if (data.defaultBrowser) {
 
102
                                                        for (var b = 0; b < bb.length; b++) {
 
103
                                                                if (data.defaultBrowser == bb[b][0]) {
 
104
                                                                        stock = false;
 
105
                                                                        browsers += "<img src='/images/browsers/" + bb[b][1] +".png'><span>" + bb[b][2] + "</span>";
 
106
                                                                }
 
107
                                                        }
 
108
                                                }
 
109
 
 
110
                                                if (stock) {
 
111
                                                        browsers += "<span class='stock'>Default browser</span>";
 
112
                                                }
 
113
 
 
114
                                                if (data.defaultFingerprint) {
 
115
                                                        browsers += "<div class='score'><a href='" + document.location.protocol + "//html5te.st/" + data.defaultFingerprint + "'>" +  data.defaultResults.score + "</a></div>";
 
116
                                                }
 
117
 
 
118
                                                browsers += "</li>";
 
119
                                        }
 
120
 
 
121
 
 
122
                                        for (var b = 0; b < bb.length; b++) {
 
123
                                                if (data.otherBrowsers[bb[b][0]]) browsers += "<li><img src='/images/browsers/" + bb[b][1] +".png'><span>" + bb[b][2] + "</span></li>";
 
124
                                        }
 
125
 
 
126
                                        if (data.hasInspect) browsers += "<li><img src='/images/browsers/inspect.png'><span>Inspect</span></li>";
 
127
                                        browsers += "</ul>";
 
128
 
 
129
                                        this.popup = document.createElement('div');
 
130
                                        this.popup.className = 'popupPanel pointsLeft deviceInfo';
 
131
                                        this.popup.innerHTML =
 
132
                                                "<h2>" + data.deviceManufacturer + " " + data.deviceModel + ( data.url ? "<a class='external' href='" + data.url + "'></a>" : "") + "</h2>" +
 
133
                                                "<div class='image'" + (data.image ? " style='background-image: url(/images/devices/" + data.image + ");'" : "") + "></div>" +
 
134
                                                "<div class='information'>" +
 
135
                                                        "<table>" +
 
136
                                                                "<tr><th>Type</th><td>" + data.type + (data.deviceSize ? ", " + data.deviceSize + "&nbsp;inch": "") + "</td></tr>" +
 
137
                                                                "<tr><th>Display</th><td>" + (data.deviceWidth && data.deviceHeight ? data.deviceWidth + "&nbsp;x&nbsp;" + data.deviceHeight + "&nbsp;pixels" : "") + (data.devicePPI ? ", " + data.devicePPI + "&nbsp;ppi" : "") + "</td></tr>" +
 
138
                                                                "<tr><th>OS</th><td>" + (data.osName ? data.osName + (data.osVersion ? " " + data.osVersion : "") : "-") + "</td></tr>" +
 
139
                                                                "<tr><th>Wi-Fi</th><td>" + (data.hasWifi ? "<span class='check'>✔</span> Yes" : "<span class='ballot'>✘</span> No") + "</td></tr>" +
 
140
                                                                "<tr><th>Cellular</th><td>" + (data.simSize ? "<span class='check'>✔</span> " + data.simSize + " sim" + (data.simLocked ? ', locked' : ', unlocked') : "<span class='ballot'>✘</span> No") + "</td></tr>" +
 
141
                                                        "</table>" +
 
142
                                                        browsers +
 
143
 
 
144
                                                        (data.comment ? "<div class='comment'>" + data.comment + "</div>" : "" ) +
 
145
                                                "</div>";
 
146
 
 
147
                                        row.cells[0].appendChild(this.popup);
 
148
                                }
 
149
                        }
 
150
                },
 
151
 
 
152
                close: function() {
 
153
                        if (this.popup) {
 
154
                                this.popup.parentNode.removeChild(this.popup);
 
155
                                this.popup = null;
 
156
                        }
 
157
                }
 
158
        }
 
159
 
 
160
 
 
161
 
 
162
        var SearchField = function() { this.initialize.apply(this, arguments) };
 
163
        SearchField.prototype = {
 
164
 
 
165
                initialize: function(options) {
 
166
                        this.parent = options.parent;
 
167
                        this.options = {
 
168
                                value:          options.value || null,
 
169
                                onQuery:        options.onQuery || null,
 
170
                                onSubmit:       options.onSubmit || null
 
171
                        }
 
172
 
 
173
                        this.interval = null;
 
174
 
 
175
                        this.container = document.createElement('div');
 
176
                        this.container.className = 'search';
 
177
                        this.parent.appendChild(this.container);
 
178
 
 
179
                        this.container.innerHTML =
 
180
                                "<input type='text' placeholder='Search...' value='" + (this.options.value || "") + "'>" +
 
181
                                "<button>×</button>";
 
182
 
 
183
                        this.container.addEventListener("click", this.onClick.bind(this), false);
 
184
 
 
185
                        this.container.firstChild.addEventListener("keyup", this.onUpdate.bind(this), true);
 
186
                        this.container.firstChild.addEventListener("change", this.onUpdate.bind(this), true);
 
187
                        this.container.firstChild.nextSibling.addEventListener("click", this.onClear.bind(this), true);
 
188
                },
 
189
 
 
190
                onUpdate: function(e) {
 
191
                        if (this.interval) {
 
192
                                window.clearTimeout(this.interval);
 
193
                        }
 
194
 
 
195
                        this.interval = window.setTimeout(this.onQuery.bind(this), 250);
 
196
 
 
197
                        if (e.keyCode == 13) {
 
198
                                if (this.options.onSubmit) {
 
199
                                        this.options.onSubmit(this.container.firstChild.value);
 
200
                                }
 
201
                        }
 
202
                },
 
203
 
 
204
                onClick: function(e) {
 
205
                        e.stopPropagation();
 
206
                },
 
207
 
 
208
                onClear: function(e) {
 
209
                        e.stopPropagation();
 
210
 
 
211
                        this.clear();
 
212
 
 
213
                        if (this.options.onQuery) {
 
214
                                this.options.onQuery('');
 
215
                        }
 
216
 
 
217
                        if (this.options.onSubmit) {
 
218
                                this.options.onSubmit('');
 
219
                        }
 
220
                },
 
221
 
 
222
                onQuery: function() {
 
223
                        var value = this.container.firstChild.value;
 
224
                        if (value.length < 3) return;
 
225
 
 
226
                        if (this.options.onQuery) {
 
227
                                this.options.onQuery(value);
 
228
                        }
 
229
                },
 
230
 
 
231
                clear: function() {
 
232
                        this.container.firstChild.value = '';
 
233
                }
 
234
        }
 
235
 
 
236
        var ToggleSwitch = function() { this.initialize.apply(this, arguments) };
 
237
        ToggleSwitch.prototype = {
 
238
 
 
239
                initialize: function(options) {
 
240
                        this.parent = options.parent;
 
241
                        this.options = {
 
242
                                inactive:       options.inactive || '',
 
243
                                active:         options.active || '',
 
244
                                onChange:       options.onChange || null
 
245
                        }
 
246
 
 
247
                        this.active = false;
 
248
 
 
249
                        this.container = document.createElement('div');
 
250
                        this.container.className = 'toggle';
 
251
                        this.parent.appendChild(this.container);
 
252
 
 
253
                        this.container.innerHTML =
 
254
                                "<div class='background'></div>" +
 
255
                                "<div class='part first'>" + this.options.inactive + "</div>" +
 
256
                                "<div class='part second'>" + this.options.active + "</div>";
 
257
 
 
258
 
 
259
                        this.container.addEventListener("click", this.onToggle.bind(this), true);
 
260
                },
 
261
 
 
262
                onToggle: function() {
 
263
                        this.active = ! this.active;
 
264
 
 
265
                        if (this.active) {
 
266
                                this.container.className += ' selected';
 
267
                        } else {
 
268
                                this.container.className = this.container.className.replace(' selected', '');
 
269
                        }
 
270
 
 
271
                        if (this.options.onChange) {
 
272
                                this.options.onChange(this.active);
 
273
                        }
 
274
                },
 
275
 
 
276
                activate: function() {
 
277
                        if (!this.active) {
 
278
                                this.active = true;
 
279
                                this.container.className += ' selected';
 
280
                        }
 
281
                },
 
282
 
 
283
                deactivate: function() {
 
284
                        if (this.active) {
 
285
                                this.active = false;
 
286
                                this.container.className = this.container.className.replace(' selected', '');
 
287
                        }
 
288
                }
 
289
        }
 
290
 
 
291
 
 
292
        var FeatureTable = function() { this.initialize.apply(this, arguments) };
 
293
        FeatureTable.prototype = {
 
294
 
 
295
                initialize: function(options) {
 
296
                        this.parent = options.parent;
 
297
                        this.tests = options.tests;
 
298
                        this.options = {
 
299
                                title:                  options.title || '',
 
300
                                browsers:               options.browsers || [],
 
301
                                columns:                options.columns || 2,
 
302
                                distribute:             options.distribute || false,
 
303
                                header:                 options.header || false,
 
304
                                links:                  options.links || false,
 
305
                                grading:                options.grading || false,
 
306
                                features:               options.features || false,
 
307
                                explainations:  options.explainations || false,
 
308
                                filter:                 null,
 
309
 
 
310
                                onChange:               options.onChange || false
 
311
                        }
 
312
 
 
313
                        this.panel = null;
 
314
 
 
315
                        this.diff = [];
 
316
 
 
317
                        this.data = [];
 
318
                        for (var i = 0; i < this.options.columns; i++) {
 
319
                                this.data[i] = null;
 
320
                        }
 
321
 
 
322
                        this.createCategories(this.parent, this.tests)
 
323
                        this.results = document.createElement('div');
 
324
                        this.parent.appendChild(this.results);
 
325
 
 
326
                        this.filter(options.filter || '');
 
327
                },
 
328
 
 
329
                filter: function(filter, force) {
 
330
                        var that = this;
 
331
 
 
332
                        if (!force && this.options.filter == filter) {
 
333
                                return;
 
334
                        }
 
335
 
 
336
                        this.options.filter = filter;
 
337
 
 
338
                        if (filter == '') {
 
339
                                this.results.innerHTML = '';
 
340
                                for (var i = 0; i < this.tests.length; i++) {
 
341
                                        this.createSections(this.results, this.tests[i].items);
 
342
                                }
 
343
 
 
344
                                this.update();
 
345
                                return;
 
346
                        }
 
347
 
 
348
                        var result = [];
 
349
                        var status = [];
 
350
                        var name = [];
 
351
 
 
352
                        function retrieveItems(items, level) {
 
353
                                for (var i = 0; i < items.length; i++) {
 
354
                                        if (typeof items[i] == 'object') {
 
355
                                                name[level] = items[i].name;
 
356
                                                status[level] = items[i].status || null;
 
357
 
 
358
                                                if (!filterItem(items[i], level)) {
 
359
                                                        if (items[i].items) {
 
360
                                                                retrieveItems(items[i].items, level + 1);
 
361
                                                        }
 
362
                                                }
 
363
                                        }
 
364
                                }
 
365
                        }
 
366
 
 
367
                        function addItems(items, level) {
 
368
                                for (var i = 0; i < items.length; i++) {
 
369
                                        if (typeof items[i] == 'object') {
 
370
                                                name[level] = items[i].name;
 
371
                                                status[level] = items[i].status || null;
 
372
 
 
373
                                                if (level > 1) {
 
374
                                                        var s = "";
 
375
                                                        for (var l = level; l >= 0; l--) {
 
376
                                                                if (status[l]) {
 
377
                                                                        s = status[l];
 
378
                                                                        break;
 
379
                                                                }
 
380
                                                        }
 
381
 
 
382
                                                        var r = {
 
383
                                                                key:    items[i].key,
 
384
                                                                name:   items[i].name,
 
385
                                                                status: s
 
386
                                                        }
 
387
                                                        if (items[i].value) r.value = items[i].value;
 
388
                                                        if (items[i].url) r.url = items[i].url;
 
389
                                                        if (items[i].urls) r.urls = items[i].urls;
 
390
                                                        if (items[i].items) r.items = items[i].items;
 
391
 
 
392
                                                        result.push(r);
 
393
                                                }
 
394
                                                else if (items[i].items) {
 
395
                                                        addItems(items[i].items, level + 1);
 
396
                                                }
 
397
                                        }
 
398
                                }
 
399
 
 
400
                                return true;
 
401
                        }
 
402
 
 
403
                        function filterItem(item, level) {
 
404
                                name[level] = item.name;
 
405
                                status[level] = item.status || null;
 
406
 
 
407
                                var selected = true;
 
408
                                if (filter == ':diff')
 
409
                                        selected = level > 1 ? that.diff[item.key] : false;
 
410
                                else
 
411
                                        selected = item.name.toLowerCase().indexOf(filter) != -1;
 
412
 
 
413
 
 
414
                                if (selected) {
 
415
                                        if (level > 1) {
 
416
                                                var s = "";
 
417
                                                for (var l = level; l >= 0; l--) {
 
418
                                                        if (status[l]) {
 
419
                                                                s = status[l];
 
420
                                                                break;
 
421
                                                        }
 
422
                                                }
 
423
 
 
424
                                                var r = {
 
425
                                                        key:    item.key,
 
426
                                                        name:   name.slice(2, level + 1).join(' ▸ '),
 
427
                                                        status: s
 
428
                                                }
 
429
                                                if (item.value) r.value = item.value;
 
430
                                                if (item.url) r.url = item.url;
 
431
                                                if (item.urls) r.urls = item.urls;
 
432
                                                if (item.items) r.items = item.items;
 
433
 
 
434
                                                return result.push(r);
 
435
                                        }
 
436
                                        else if (item.items) {
 
437
                                                return addItems(item.items, level + 1);
 
438
                                        }
 
439
                                }
 
440
                        }
 
441
 
 
442
 
 
443
                        retrieveItems(this.tests, 0);
 
444
 
 
445
                        this.results.innerHTML = '';
 
446
                        this.createSections(this.results, [{ name: filter == ':diff' ? 'Difference' : filter, items: result }]);
 
447
 
 
448
                        this.update();
 
449
                },
 
450
 
 
451
                loadColumn: function(column, browser) {
 
452
                        var id = browser;
 
453
 
 
454
                        if (typeof browser == 'object') {
 
455
                                id = browser.platform + (browser.version ? '-' + browser.version : '');
 
456
                        }
 
457
 
 
458
                        var that = this;
 
459
 
 
460
                        var httpRequest;
 
461
                        if (window.XMLHttpRequest) {
 
462
                                httpRequest = new XMLHttpRequest();
 
463
                        } else if (window.ActiveXObject) {
 
464
                                httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
 
465
                        }
 
466
 
 
467
                        httpRequest.open('POST','/api/loadBrowser', true);
 
468
                        httpRequest.onreadystatechange = process;
 
469
                        httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 
470
                        httpRequest.send('id=' + encodeURIComponent(id));
 
471
 
 
472
                        function process() {
 
473
                                if (httpRequest.readyState == 4 && httpRequest.responseText != '') {
 
474
                                        var data = JSON.parse(httpRequest.responseText);
 
475
 
 
476
                                        var f = that.options.filter;
 
477
                                        that.filter('');
 
478
                                        that.updateColumn(column, data);
 
479
                                        that.filter(f);
 
480
                                }
 
481
                        }
 
482
                },
 
483
 
 
484
                calculateColumn: function(column) {
 
485
                        var that = this;
 
486
 
 
487
                        new Test(process);
 
488
 
 
489
                        function process(r) {
 
490
                                var c = new Calculate(r, tests);
 
491
 
 
492
                                that.updateColumn(column, {
 
493
                                        id:                             'mybrowser',
 
494
                                        nickname:               'My browser',
 
495
                                        score:                  c.score,
 
496
                                        points:                 c.points,
 
497
                                        results:                r.results
 
498
                                })
 
499
                        }
 
500
                },
 
501
 
 
502
                clearColumn: function(column) {
 
503
                        this.data[column] = null;
 
504
                        this.diff = [];
 
505
 
 
506
                        if (this.options.onChange) {
 
507
                                var ids = [];
 
508
                                for (var i = 0; i < this.options.columns; i++) {
 
509
                                        if (this.data[i]) {
 
510
                                                if (this.data[i].id)
 
511
                                                        ids.push(this.data[i].id);
 
512
                                                else if (this.data[i].version)
 
513
                                                        ids.push(this.data[i].platform + '-' + this.data[i].version);
 
514
                                                else
 
515
                                                        ids.push(this.data[i].platform);
 
516
                                        }
 
517
                                }
 
518
 
 
519
                                this.options.onChange(ids);
 
520
                        }
 
521
 
 
522
                        var row = document.getElementById('row-header');
 
523
                        var cell = row.childNodes[column + 1];
 
524
                        cell.className = 'empty';
 
525
                        cell.firstChild.firstChild.innerHTML = '';
 
526
                        cell.firstChild.childNodes[1].selectedIndex = 0;
 
527
 
 
528
                        for (var c = 0; c < this.tests.length; c++)
 
529
                        for (var i = 0; i < this.tests[c].items.length; i++) {
 
530
                                var test = this.tests[c].items[i];
 
531
 
 
532
                                if (typeof test != 'string') {
 
533
                                        if (typeof test.items != 'undefined') {
 
534
                                                var row = document.getElementById('head-' + test.id);
 
535
                                                if (row) {
 
536
                                                        var cell = row.childNodes[column + 1];
 
537
                                                        cell.innerHTML = '';
 
538
                                                }
 
539
 
 
540
                                                this.clearItems(column, test.id, test.items);
 
541
                                        }
 
542
                                }
 
543
                        }
 
544
 
 
545
                        this.filter(this.options.filter, true);
 
546
                },
 
547
 
 
548
                clearItems: function(column, id, tests) {
 
549
                        for (var i = 0; i < tests.length; i++) {
 
550
                                if (typeof tests[i] != 'string') {
 
551
                                        var key = tests[i].key;
 
552
 
 
553
                                        var row = document.getElementById('row-' + key);
 
554
                                        if (row) {
 
555
                                                var cell = row.childNodes[column + 1];
 
556
                                                cell.innerHTML = '';
 
557
                                                cell.className = '';
 
558
                                        }
 
559
 
 
560
                                        if (typeof tests[i].items != 'undefined') {
 
561
                                                this.clearItems(column, key, tests[i].items);
 
562
                                        }
 
563
 
 
564
 
 
565
                                        var base = null;
 
566
                                        var diff = false;
 
567
 
 
568
                                        for (var c = 0; c < this.options.columns; c++) {
 
569
                                                if (this.data[c]) {
 
570
                                                        if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(this.data[c].results)) {
 
571
                                                                var result = parseInt(match[1], 10);
 
572
 
 
573
                                                                if (base === null) {
 
574
                                                                        base = result;
 
575
                                                                }
 
576
                                                                else {
 
577
                                                                        if (result != base) {
 
578
                                                                                diff = true;
 
579
                                                                                break;
 
580
                                                                        }
 
581
                                                                }
 
582
                                                        }
 
583
                                                }
 
584
                                        }
 
585
 
 
586
                                        this.diff[key] = diff;
 
587
                                }
 
588
                        }
 
589
                },
 
590
 
 
591
                update: function() {
 
592
                        for (var i = 0; i < this.options.columns; i++) {
 
593
                                if (this.data[i]) {
 
594
                                        this.updateColumn(i, this.data[i]);
 
595
                                }
 
596
                        }
 
597
                },
 
598
 
 
599
                updateColumn: function(column, data) {
 
600
                        this.data[column] = data;
 
601
                        this.diff = [];
 
602
 
 
603
                        if (this.options.onChange) {
 
604
                                var ids = [];
 
605
 
 
606
 
 
607
                                for (var i = 0; i < this.options.columns; i++) {
 
608
                                        if (this.data[i]) {
 
609
                                                if (this.data[i].id)
 
610
                                                        ids.push(this.data[i].id);
 
611
                                                else if (this.data[i].version)
 
612
                                                        ids.push(this.data[i].platform + '-' + this.data[i].version);
 
613
                                                else
 
614
                                                        ids.push(this.data[i].platform);
 
615
                                        }
 
616
                                }
 
617
 
 
618
                                this.options.onChange(ids);
 
619
                        }
 
620
 
 
621
                        var row = document.getElementById('row-header');
 
622
                        var cell = row.childNodes[column + 1];
 
623
                        cell.className = '';
 
624
                        cell.firstChild.firstChild.innerHTML = '<span class="nickname">' + data.nickname + '</span><span class="score">' + data.score + '</span>';
 
625
 
 
626
                        for (var c = 0; c < this.tests.length; c++)
 
627
                        for (var i = 0; i < this.tests[c].items.length; i++) {
 
628
                                var test = this.tests[c].items[i];
 
629
 
 
630
                                if (typeof test != 'string') {
 
631
                                        if (typeof test != 'undefined') {
 
632
                                                var points = 0;
 
633
                                                var maximum = 0;
 
634
 
 
635
                                                if (match = (new RegExp(test.id + "=([0-9]+)(?:\\/([0-9]+))?(?:\\+([0-9]+))?")).exec(data.points)) {
 
636
                                                        points = match[1];
 
637
                                                        if (match[2]) maximum = match[2];
 
638
                                                }
 
639
 
 
640
                                                var row = document.getElementById('head-' + test.id);
 
641
                                                if (row) {
 
642
                                                        var cell = row.childNodes[column + 1];
 
643
 
 
644
                                                        var content = "<div><div class='grade'>";
 
645
 
 
646
                                                        if (this.options.grading) {
 
647
                                                                var grade = '';
 
648
                                                                var percent = parseInt(points / maximum * 100, 10);
 
649
                                                                switch (true) {
 
650
                                                                        case percent == 0:      grade = 'none'; break;
 
651
                                                                        case percent <= 30: grade = 'badly'; break;
 
652
                                                                        case percent <= 60: grade = 'reasonable'; break;
 
653
                                                                        case percent <= 95: grade = 'good'; break;
 
654
                                                                        default:                        grade = 'great'; break;
 
655
                                                                }
 
656
 
 
657
                                                                if (points == maximum)
 
658
                                                                        content += "<span class='" + grade + "'>" + points + "</span>";
 
659
                                                                else
 
660
                                                                        content += "<span class='" + grade + "'>" + points + "/" + maximum + "</span>";
 
661
                                                        } else {
 
662
                                                                content += "<span>" + points + "</span>";
 
663
                                                        }
 
664
 
 
665
                                                        content += "</div></div>";
 
666
                                                        cell.innerHTML = content;
 
667
                                                }
 
668
 
 
669
                                                this.updateItems(column, data, test.items);
 
670
                                        }
 
671
                                }
 
672
                        }
 
673
                },
 
674
 
 
675
                updateItems: function(column, data, tests) {
 
676
                        var count = [ 0, 0 ];
 
677
 
 
678
                        for (var i = 0; i < tests.length; i++) {
 
679
                                if (typeof tests[i] != 'string') {
 
680
                                        var key = tests[i].key;
 
681
 
 
682
                                        var row = document.getElementById('row-' + key);
 
683
                                        if (row) {
 
684
                                                var cell = row.childNodes[column + 1];
 
685
                                                var classes = [ 'used' ];
 
686
 
 
687
                                                if (typeof tests[i].items != 'undefined') {
 
688
                                                        var results = this.updateItems(column, data, tests[i].items);
 
689
 
 
690
                                                        if (results[0] == results[1]) {
 
691
                                                                cell.innerHTML = '<div>' + 'Yes' + ' <span class="check">✔</span></div>';
 
692
                                                                classes.push('yes');
 
693
                                                        } else if (results[1] == 0) {
 
694
                                                                cell.innerHTML = '<div>' + 'No' + ' <span class="ballot">✘</span></div>';
 
695
                                                                classes.push('no');
 
696
                                                        } else {
 
697
                                                                cell.innerHTML = '<div><span class="partially">' + 'Partial' + '</span> <span class="partial">○</span></div>';
 
698
                                                        }
 
699
                                                }
 
700
 
 
701
                                                else {
 
702
                                                        if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(data.results)) {
 
703
                                                                var result = parseInt(match[1], 10);
 
704
 
 
705
                                                                if (result & YES) {
 
706
                                                                        switch(true) {
 
707
                                                                                case !! (result & BUGGY):                       cell.innerHTML = '<div>Buggy <span class="buggy"></span></div>'; break;
 
708
                                                                                case !! (result & OLD):                         cell.innerHTML = '<div>Partial <span class="partial">○</span></div>'; count[1]++; break;
 
709
                                                                                case !! (result & PREFIX):                      cell.innerHTML = '<div>Prefixed <span class="check">✔</span></div>'; classes.push('yes'); count[1]++; break;
 
710
                                                                                case !! (result & EXPERIMENTAL):        cell.innerHTML = '<div>Prefixed <span class="check">✔</span></div>'; classes.push('yes'); count[1]++; break;
 
711
                                                                                default:                                                        cell.innerHTML = '<div>Yes <span class="check">✔</span></div>'; classes.push('yes'); count[1]++; break;
 
712
                                                                        }
 
713
                                                                }
 
714
                                                                else {
 
715
                                                                        switch(true) {
 
716
                                                                                case !! (result & UNKNOWN):                     cell.innerHTML = '<div>Unknown <span class="buggy">?</span></div>'; break;
 
717
                                                                                case !! (result & BLOCKED):                     cell.innerHTML = '<div>Broken <span class="buggy">!</span></div>'; classes.push('no'); break;
 
718
                                                                                case !! (result & DISABLED):            cell.innerHTML = '<div>Disabled <span class="ballot">✘</span></div>'; classes.push('no'); break;
 
719
                                                                                default:                                                        cell.innerHTML = '<div>No <span class="ballot">✘</span></div>'; classes.push('no'); break;
 
720
                                                                        }
 
721
                                                                }
 
722
                                                        } else {
 
723
                                                                cell.innerHTML = '<div><span class="partially">Unknown</span> <span class="partial">?</span></div>';
 
724
                                                        }
 
725
                                                }
 
726
 
 
727
                                                cell.className = classes.join(' ');
 
728
 
 
729
 
 
730
                                                var base = null;
 
731
                                                var diff = false;
 
732
 
 
733
                                                for (var c = 0; c < this.options.columns; c++) {
 
734
                                                        if (this.data[c]) {
 
735
                                                                if (match = (new RegExp(key + '=(-?[0-9]+)')).exec(this.data[c].results)) {
 
736
                                                                        var result = parseInt(match[1], 10);
 
737
 
 
738
                                                                        if (base === null) {
 
739
                                                                                base = result;
 
740
                                                                        }
 
741
                                                                        else {
 
742
                                                                                if (result != base) {
 
743
                                                                                        diff = true;
 
744
                                                                                        break;
 
745
                                                                                }
 
746
                                                                        }
 
747
                                                                }
 
748
                                                        }
 
749
                                                }
 
750
 
 
751
                                                this.diff[key] = diff;
 
752
                                        } else {
 
753
                                                if (typeof tests[i].items != 'undefined') {
 
754
                                                        var results = this.updateItems(column, data, tests[i].items);
 
755
                                                }
 
756
                                        }
 
757
 
 
758
                                        count[0]++;
 
759
                                }
 
760
                        }
 
761
 
 
762
                        return count;
 
763
                },
 
764
 
 
765
                askForUniqueId: function(c) {
 
766
                        var id = prompt('Enter the unique id of the results you want to see')
 
767
                        if (id) {
 
768
                                this.loadColumn(c, 'custom:' + id);
 
769
                        }
 
770
                },
 
771
 
 
772
                createCategories: function(parent, tests) {
 
773
                        var table = document.createElement('table');
 
774
                        table.id = 'table-header';
 
775
                        parent.appendChild(table);
 
776
 
 
777
                        var tbody = document.createElement('tbody');
 
778
                        table.appendChild(tbody);
 
779
 
 
780
                        var tr = document.createElement('tr');
 
781
                        tr.id = 'row-header';
 
782
                        tbody.appendChild(tr);
 
783
 
 
784
                        var th = document.createElement('th');
 
785
                        th.innerHTML = this.options.title;
 
786
                        tr.appendChild(th);
 
787
 
 
788
                        for (var c = 0; c < this.options.columns; c++) {
 
789
                                var that = this;
 
790
 
 
791
                                var td = document.createElement('td');
 
792
                                td.className = 'empty';
 
793
                                tr.appendChild(td);
 
794
 
 
795
                                var wrapper = document.createElement('div');
 
796
                                td.appendChild(wrapper);
 
797
 
 
798
                                var div = document.createElement('div');
 
799
                                div.className = 'name';
 
800
                                wrapper.appendChild(div);
 
801
 
 
802
                                var menu = document.createElement('div');
 
803
                                menu.className = 'popup popupPanel pointsRight hasSearch';
 
804
                                wrapper.appendChild(menu);
 
805
 
 
806
                                var header = document.createElement('div');
 
807
                                header.className = 'toolbar';
 
808
                                menu.appendChild(header);
 
809
 
 
810
                                var scroll = document.createElement('div');
 
811
                                scroll.className = 'scroll';
 
812
                                menu.appendChild(scroll);
 
813
 
 
814
                                var list = document.createElement('ul');
 
815
                                scroll.appendChild(list);
 
816
 
 
817
 
 
818
                                (function(c, menu, list, header) {
 
819
                                        var search = new SearchField({
 
820
                                                parent:         header,
 
821
                                                onQuery:        function(query) {
 
822
                                                                                build(list, that.options.browsers, query != "", query);
 
823
                                                                        }
 
824
                                        });
 
825
 
 
826
 
 
827
                                        document.body.addEventListener('click', function(e) {
 
828
                                                menu.className = menu.className.replace(' visible', '');
 
829
                                        }, false);
 
830
 
 
831
                                        div.addEventListener('click', function(e) {
 
832
                                                if (that.data[c] == null) {
 
833
                                                        if (e.altKey) {
 
834
                                                                that.askForUniqueId(c);
 
835
                                                        }
 
836
                                                        else
 
837
                                                                menu.className += ' visible';
 
838
                                                }
 
839
                                                else
 
840
                                                        that.clearColumn(c);
 
841
 
 
842
                                                e.stopPropagation();
 
843
                                        }, true);
 
844
 
 
845
                                        list.addEventListener('click', function(e) {
 
846
                                                var close = true;
 
847
 
 
848
                                                if (e.target) {
 
849
                                                        var target = e.target;
 
850
 
 
851
                                                        while (target.tagName != 'LI' && target.parentNode) {
 
852
                                                                target = target.parentNode;
 
853
                                                        }
 
854
 
 
855
                                                        if (target.hasAttribute('data-action')) {
 
856
                                                                var action = target.getAttribute('data-action');
 
857
 
 
858
                                                                if (action == 'more') {
 
859
                                                                        build(list, that.options.browsers, true);
 
860
                                                                        close = false;
 
861
                                                                }
 
862
 
 
863
                                                                if (action == 'less') {
 
864
                                                                        build(list, that.options.browsers, false);
 
865
                                                                        close = false;
 
866
                                                                }
 
867
 
 
868
                                                                if (action == 'calculate') {
 
869
                                                                        that.calculateColumn(c);
 
870
                                                                }
 
871
 
 
872
                                                                if (action == 'custom') {
 
873
                                                                        window.setTimeout(function() {
 
874
                                                                                that.askForUniqueId(c);
 
875
                                                                        }, 0);
 
876
                                                                }
 
877
 
 
878
                                                                if (action == 'load') {
 
879
                                                                        var id = target.getAttribute('data-id');
 
880
                                                                        that.loadColumn(c, that.options.browsers[id]);
 
881
                                                                }
 
882
                                                        }
 
883
                                                }
 
884
 
 
885
                                                if (close) {
 
886
                                                        menu.className = menu.className.replace(' visible', '');
 
887
                                                }
 
888
 
 
889
                                                e.stopPropagation();
 
890
                                        }, true);
 
891
                                })(c, menu, list, header);
 
892
 
 
893
 
 
894
                                build(list, this.options.browsers, false);
 
895
 
 
896
 
 
897
                                function build(list, browsers, all, filter) {
 
898
                                        list.innerHTML = '';
 
899
 
 
900
                                        if (!filter) {
 
901
                                                var item = document.createElement('li');
 
902
                                                item.setAttribute('data-action', 'calculate');
 
903
                                                item.innerHTML = 'My browser';
 
904
                                                list.appendChild(item);
 
905
 
 
906
                                                var item = document.createElement('li');
 
907
                                                item.setAttribute('data-action', 'custom');
 
908
                                                item.innerHTML = 'Enter unique id...';
 
909
                                                list.appendChild(item);
 
910
                                        }
 
911
 
 
912
                                        var type = null;
 
913
 
 
914
                                        for (var i = 0; i < browsers.length; i++) {
 
915
                                                if (!filter || browsers[i].nickname.toLowerCase().indexOf(filter.toLowerCase()) != -1) {
 
916
                                                        if (all || browsers[i].visible) {
 
917
                                                                if (type != browsers[i].type) {
 
918
                                                                        var item = document.createElement('li');
 
919
                                                                        item.className = 'indent-0 title';
 
920
                                                                        list.appendChild(item);
 
921
 
 
922
                                                                        switch(browsers[i].type) {
 
923
                                                                                case 'desktop':         item.innerHTML = 'Desktop browsers'; break;
 
924
                                                                                case 'gaming':          item.innerHTML = 'Gaming'; break;
 
925
                                                                                case 'mobile':          item.innerHTML = 'Mobiles'; break;
 
926
                                                                                case 'tablet':          item.innerHTML = 'Tablets'; break;
 
927
                                                                                case 'television':      item.innerHTML = 'Television'; break;
 
928
                                                                        }
 
929
                                                                }
 
930
 
 
931
                                                                var item = document.createElement('li');
 
932
                                                                item.setAttribute('data-action', 'load');
 
933
                                                                item.setAttribute('data-id', i);
 
934
                                                                item.innerHTML = browsers[i].nickname + (browsers[i].details ? ' <em>(' + browsers[i].details + ')</em>' : '');
 
935
                                                                list.appendChild(item);
 
936
 
 
937
                                                                type = browsers[i].type;
 
938
                                                        }
 
939
                                                }
 
940
                                        }
 
941
 
 
942
                                        if (!filter) {
 
943
                                                if (!all) {
 
944
                                                        var item = document.createElement('li');
 
945
                                                        item.className = 'more';
 
946
                                                        item.setAttribute('data-action', 'more');
 
947
                                                        item.innerHTML = 'Show more';
 
948
                                                        list.appendChild(item);
 
949
                                                } else {
 
950
                                                        var item = document.createElement('li');
 
951
                                                        item.className = 'less';
 
952
                                                        item.setAttribute('data-action', 'less');
 
953
                                                        item.innerHTML = 'Show less';
 
954
                                                        list.appendChild(item);
 
955
                                                }
 
956
                                        }
 
957
                                }
 
958
                        }
 
959
                },
 
960
 
 
961
                createSections: function(parent, tests) {
 
962
                        for (var i = 0; i < tests.length; i++) {
 
963
                                if (typeof tests[i] == 'string') {
 
964
                                        var h2 = document.createElement('h2');
 
965
                                        h2.innerHTML = tests[i];
 
966
                                        parent.appendChild(h2);
 
967
                                } else {
 
968
                                        var table = document.createElement('table');
 
969
                                        if (tests[i].id) table.id = 'table-' + tests[i].id;
 
970
                                        parent.appendChild(table);
 
971
 
 
972
                                        var thead = document.createElement('thead');
 
973
                                        table.appendChild(thead);
 
974
 
 
975
                                        var tr = document.createElement('tr');
 
976
                                        if (tests[i].id) tr.id = 'head-' + tests[i].id;
 
977
                                        thead.appendChild(tr);
 
978
 
 
979
                                        var th = document.createElement('th');
 
980
                                        if (tests[i].name) th.innerHTML = tests[i].name;
 
981
                                        tr.appendChild(th);
 
982
 
 
983
                                        for (var c = 0; c < this.options.columns; c++) {
 
984
                                                var td = document.createElement('td');
 
985
                                                tr.appendChild(td);
 
986
                                        }
 
987
 
 
988
                                        if (typeof tests[i].items != 'undefined') {
 
989
                                                var tbody = document.createElement('tbody');
 
990
                                                table.appendChild(tbody);
 
991
 
 
992
                                                var status = typeof tests[i].status != 'undefined' ? tests[i].status : '';
 
993
 
 
994
                                                this.createItems(tbody, 0, tests[i].items, {
 
995
                                                        id:             tests[i].id,
 
996
                                                        status: status,
 
997
                                                        urls:   []
 
998
                                                });
 
999
                                        }
 
1000
                                }
 
1001
                        }
 
1002
                },
 
1003
 
 
1004
                createItems: function(parent, level, tests, data) {
 
1005
                        var ids = [];
 
1006
 
 
1007
                        for (var i = 0; i < tests.length; i++) {
 
1008
                                var tr = document.createElement('tr');
 
1009
                                parent.appendChild(tr);
 
1010
 
 
1011
                                if (typeof tests[i] == 'string') {
 
1012
                                        if (this.options.explainations || tests[i].substr(0, 4) != '<em>') {
 
1013
                                                var th = document.createElement('th');
 
1014
                                                th.colSpan = this.options.columns + 1;
 
1015
                                                th.className = 'details';
 
1016
                                                tr.appendChild(th);
 
1017
 
 
1018
                                                th.innerHTML = tests[i];
 
1019
                                        }
 
1020
                                } else {
 
1021
                                        var key = tests[i].key;
 
1022
 
 
1023
                                        var th = document.createElement('th');
 
1024
                                        th.innerHTML = "<div><span>" + tests[i].name + "</span></div>";
 
1025
                                        tr.appendChild(th);
 
1026
 
 
1027
                                        for (var c = 0; c < this.options.columns; c++) {
 
1028
                                                var td = document.createElement('td');
 
1029
                                                tr.appendChild(td);
 
1030
                                        }
 
1031
 
 
1032
                                        tr.id = 'row-' + key;
 
1033
 
 
1034
                                        if (level > 0) {
 
1035
                                                tr.className = 'isChild';
 
1036
                                        }
 
1037
 
 
1038
                                        if (typeof tests[i].items != 'undefined') {
 
1039
                                                var urls = null;
 
1040
 
 
1041
                                                if (this.options.links) {
 
1042
                                                        if (typeof tests[i].urls != 'undefined') {
 
1043
                                                                urls = tests[i].urls;
 
1044
                                                        }
 
1045
                                                        else if (typeof tests[i].url != 'undefined') {
 
1046
                                                                urls = { 'w3c': tests[i].url };
 
1047
                                                        }
 
1048
                                                }
 
1049
 
 
1050
                                                tr.className += 'hasChild';
 
1051
 
 
1052
                                                var children = this.createItems(parent, level + 1, tests[i].items, {
 
1053
                                                        id:     key,
 
1054
                                                        status: typeof tests[i].status != 'undefined' ? tests[i].status : data.status,
 
1055
                                                        urls:   urls
 
1056
                                                });
 
1057
 
 
1058
                                                this.hideChildren(tr, children);
 
1059
 
 
1060
                                                (function(that, tr, th, children) {
 
1061
                                                        th.onclick = function() {
 
1062
                                                                that.toggleChildren(tr, children);
 
1063
                                                        };
 
1064
                                                })(this, tr, th, children);
 
1065
                                        } else {
 
1066
                                                var urls;
 
1067
                                                var value = 0;
 
1068
 
 
1069
                                                if (typeof tests[i].value != 'undefined') {
 
1070
                                                        value = tests[i].value;
 
1071
                                                }
 
1072
 
 
1073
                                                if (typeof tests[i].urls != 'undefined') {
 
1074
                                                        urls = tests[i].urls;
 
1075
                                                }
 
1076
                                                else if (typeof tests[i].url != 'undefined') {
 
1077
                                                        urls = [ [ 'w3c', tests[i].url ] ];
 
1078
                                                }
 
1079
 
 
1080
                                                th.className = 'hasLink';
 
1081
 
 
1082
                                                (function(th, data){
 
1083
                                                        th.onclick = function() {
 
1084
                                                                new FeaturePopup(th, data);
 
1085
                                                        };
 
1086
                                                })(th, {
 
1087
                                                        id:             key,
 
1088
                                                        name:   tests[i].name,
 
1089
                                                        value:  value,
 
1090
                                                        status: typeof tests[i].status != 'undefined' ? tests[i].status : data.status,
 
1091
                                                        urls:   (urls || []).concat(data.urls || [])
 
1092
                                                });
 
1093
                                        }
 
1094
 
 
1095
                                        ids.push(tr.id);
 
1096
                                }
 
1097
                        }
 
1098
 
 
1099
                        return ids;
 
1100
                },
 
1101
 
 
1102
                toggleChildren: function(element, ids) {
 
1103
                        if (element.className.indexOf(' hidden') == -1) {
 
1104
                                this.hideChildren(element, ids);
 
1105
                        } else {
 
1106
                                this.showChildren(element, ids);
 
1107
                        }
 
1108
                },
 
1109
 
 
1110
                showChildren: function(element, ids) {
 
1111
                        element.className = element.className.replace(' hidden', '');
 
1112
 
 
1113
                        for (var i = 0; i < ids.length; i++) {
 
1114
                                var e = document.getElementById(ids[i]);
 
1115
                                e.style.display = '';
 
1116
                        }
 
1117
                },
 
1118
 
 
1119
                hideChildren: function(element, ids) {
 
1120
                        element.className = element.className.replace(' hidden', '');
 
1121
                        element.className += ' hidden';
 
1122
 
 
1123
                        for (var i = 0; i < ids.length; i++) {
 
1124
                                var e = document.getElementById(ids[i]);
 
1125
                                e.style.display = 'none';
 
1126
                        }
 
1127
                }
 
1128
        }
 
1129
 
 
1130
 
 
1131
 
 
1132
        var BrowserTable = function() { this.initialize.apply(this, arguments) };
 
1133
        BrowserTable.prototype = {
 
1134
 
 
1135
                initialize: function(options) {
 
1136
                        this.parent = options.parent;
 
1137
                        this.browsers = options.browsers;
 
1138
                        this.options = {
 
1139
                                title:                  options.title || '',
 
1140
                                tests:                  options.tests || [],
 
1141
                                columns:                options.columns || 2,
 
1142
                                header:                 options.header || false,
 
1143
                                links:                  options.links || false,
 
1144
                                grading:                options.grading || false,
 
1145
                                explainations:  options.explainations || false,
 
1146
                                filter:                 '',
 
1147
 
 
1148
                                onChange:               options.onChange || false
 
1149
                        }
 
1150
 
 
1151
                        this.data = [];
 
1152
                        for (var i = 0; i < this.options.columns; i++) {
 
1153
                                this.data[i] = null;
 
1154
                        }
 
1155
 
 
1156
                        this.createSections(this.parent);
 
1157
 
 
1158
                        this.filter(options.filter || '');
 
1159
                },
 
1160
 
 
1161
                filter: function(filter) {
 
1162
                        if (this.options.filter != filter) {
 
1163
                                this.options.filter = filter;
 
1164
 
 
1165
                                filter = filter.toLowerCase();
 
1166
 
 
1167
                                for (var i = 0; i < this.browsers.length; i++) {
 
1168
                                        var row = document.getElementById('row-' + this.browsers[i].uid);
 
1169
                                        var visible = true;
 
1170
 
 
1171
                                        if (filter != '') {
 
1172
                                                if (filter == ':mostused') {
 
1173
                                                        visible = this.browsers[i].visible;
 
1174
                                                }
 
1175
 
 
1176
                                                else {
 
1177
                                                        visible = this.browsers[i].nickname.toLowerCase().indexOf(filter) != -1
 
1178
                                                }
 
1179
                                        }
 
1180
 
 
1181
                                        row.style.display = visible ? '' : 'none';
 
1182
                                }
 
1183
                        }
 
1184
                },
 
1185
 
 
1186
                loadColumn: function(column, key) {
 
1187
                        var httpRequest;
 
1188
                        if (window.XMLHttpRequest) {
 
1189
                                httpRequest = new XMLHttpRequest();
 
1190
                        } else if (window.ActiveXObject) {
 
1191
                                httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
 
1192
                        }
 
1193
 
 
1194
                        httpRequest.open('POST','/api/loadFeature', true);
 
1195
                        httpRequest.onreadystatechange = process;
 
1196
                        httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 
1197
                        httpRequest.send('key=' + encodeURIComponent(key));
 
1198
 
 
1199
                        var that = this;
 
1200
 
 
1201
                        function process() {
 
1202
                                if (httpRequest.readyState == 4 && httpRequest.responseText != '') {
 
1203
                                        var data = JSON.parse(httpRequest.responseText);
 
1204
                                        that.updateColumn(column, data);
 
1205
                                }
 
1206
                        }
 
1207
                },
 
1208
 
 
1209
                clearColumn: function(column) {
 
1210
                        this.data[column] = null;
 
1211
 
 
1212
                        if (this.options.onChange) {
 
1213
                                var ids = [];
 
1214
                                for (var i = 0; i < this.options.columns; i++) {
 
1215
                                        if (this.data[i])
 
1216
                                                ids.push(this.data[i].id);
 
1217
                                }
 
1218
 
 
1219
                                this.options.onChange(ids);
 
1220
                        }
 
1221
 
 
1222
                        if (this.options.header) {
 
1223
                                var row = document.getElementById('row-header');
 
1224
                                var cell = row.childNodes[column + 1];
 
1225
                                cell.className = 'empty';
 
1226
                                cell.firstChild.firstChild.innerHTML = '';
 
1227
                                cell.firstChild.childNodes[1].selectedIndex = 0;
 
1228
                        }
 
1229
 
 
1230
                        for (var i = 0; i < this.browsers.length; i++) {
 
1231
                                var row = document.getElementById('row-' + this.browsers[i].uid);
 
1232
                                var cell = row.childNodes[column + 1];
 
1233
                                cell.className = '';
 
1234
                                cell.innerHTML = '';
 
1235
                        }
 
1236
                },
 
1237
 
 
1238
                updateColumn: function(column, data) {
 
1239
                        this.data[column] = data;
 
1240
 
 
1241
                        if (this.options.onChange) {
 
1242
                                var keys = [];
 
1243
                                for (var i = 0; i < this.options.columns; i++) {
 
1244
                                        if (this.data[i])
 
1245
                                                keys.push(this.data[i].key);
 
1246
                                }
 
1247
 
 
1248
                                this.options.onChange(keys);
 
1249
                        }
 
1250
 
 
1251
                        if (this.options.header) {
 
1252
                                var row = document.getElementById('row-header');
 
1253
                                var cell = row.childNodes[column + 1];
 
1254
                                cell.className = '';
 
1255
 
 
1256
                                var item, parent;
 
1257
                                if (item = this.getItemByKey(this.options.tests, data.key)) {
 
1258
                                        if (data.key.split('.').length > 2) {
 
1259
                                                if (parent = this.getItemByKey(this.options.tests, data.key.split('.').slice(0,-1).join('.'))) {
 
1260
                                                        cell.firstChild.firstChild.innerHTML = '<span class="feature">' + parent.name + '<hr>' + item.name + '</span>';
 
1261
                                                } else {
 
1262
                                                        cell.firstChild.firstChild.innerHTML = '<span class="feature">' + item.name + '</span>';
 
1263
                                                }
 
1264
                                        }
 
1265
                                        else {
 
1266
                                                cell.firstChild.firstChild.innerHTML = '<span class="feature">' + item.name + '</span>';
 
1267
                                        }
 
1268
                                }
 
1269
                        }
 
1270
 
 
1271
                        for (var i = 0; i < this.browsers.length; i++) {
 
1272
                                var row = document.getElementById('row-' + this.browsers[i].uid);
 
1273
                                var cell = row.childNodes[column + 1];
 
1274
                                var classes = [ 'used' ];
 
1275
 
 
1276
                                cell.className = 'used';
 
1277
 
 
1278
                                if (match = (new RegExp(this.browsers[i].platform + '-' + this.browsers[i].version + '=(-?[0-9]+)')).exec(data.supported)) {
 
1279
                                        var result = parseInt(match[1], 10);
 
1280
 
 
1281
                                        if (result & YES) {
 
1282
                                                switch(true) {
 
1283
                                                        case !! (result & BUGGY):                       cell.innerHTML = '<div>Buggy <span class="buggy"></span></div>'; break;
 
1284
                                                        case !! (result & OLD):                         cell.innerHTML = '<div>Partial <span class="partial">○</span></div>'; break;
 
1285
                                                        case !! (result & PREFIX):                      cell.innerHTML = '<div>Prefixed <span class="check">✔</span></div>'; classes.push('yes'); break;
 
1286
                                                        case !! (result & EXPERIMENTAL):        cell.innerHTML = '<div>Prefixed <span class="check">✔</span></div>'; classes.push('yes'); break;
 
1287
                                                        default:                                                        cell.innerHTML = '<div>Yes <span class="check">✔</span></div>'; classes.push('yes'); break;
 
1288
                                                }
 
1289
                                        }
 
1290
                                        else {
 
1291
                                                switch(true) {
 
1292
                                                        case !! (result & UNKNOWN):                     cell.innerHTML = '<div>Unknown <span class="partial">?</span></div>'; break;
 
1293
                                                        case !! (result & BLOCKED):                     cell.innerHTML = '<div>Broken <span class="buggy">!</span></div>'; classes.push('no'); break;
 
1294
                                                        case !! (result & DISABLED):            cell.innerHTML = '<div>Disabled <span class="ballot">✘</span></div>'; classes.push('no'); break;
 
1295
                                                        default:                                                        cell.innerHTML = '<div>No <span class="ballot">✘</span></div>'; classes.push('no'); break;
 
1296
                                                }
 
1297
                                        }
 
1298
                                }
 
1299
                                else
 
1300
                                        cell.innerHTML = '<div><span class="partially">Unknown</span> <span class="partial">?</span></div>';
 
1301
 
 
1302
                                cell.className = classes.join(' ');
 
1303
                        }
 
1304
                },
 
1305
 
 
1306
                createSections: function(parent) {
 
1307
                        var that = this;
 
1308
                        var tests = this.getList(this.options.tests);
 
1309
 
 
1310
                        if (this.options.header) {
 
1311
                                var table = document.createElement('table');
 
1312
                                table.id = 'table-header';
 
1313
                                parent.appendChild(table);
 
1314
 
 
1315
                                var tbody = document.createElement('tbody');
 
1316
                                table.appendChild(tbody);
 
1317
 
 
1318
                                var tr = document.createElement('tr');
 
1319
                                tr.id = 'row-header';
 
1320
                                tbody.appendChild(tr);
 
1321
 
 
1322
                                var th = document.createElement('th');
 
1323
                                th.innerHTML = this.options.title;
 
1324
                                tr.appendChild(th);
 
1325
 
 
1326
                                for (var c = 0; c < this.options.columns; c++) {
 
1327
                                        var td = document.createElement('td');
 
1328
                                        td.className = 'empty';
 
1329
                                        tr.appendChild(td);
 
1330
 
 
1331
                                        var wrapper = document.createElement('div');
 
1332
                                        td.appendChild(wrapper);
 
1333
 
 
1334
                                        var div = document.createElement('div');
 
1335
                                        div.className = 'name';
 
1336
                                        wrapper.appendChild(div);
 
1337
 
 
1338
                                        var menu = document.createElement('div');
 
1339
                                        menu.className = 'popup popupPanel pointsRight hasSearch';
 
1340
                                        wrapper.appendChild(menu);
 
1341
 
 
1342
                                        var header = document.createElement('div');
 
1343
                                        header.className = 'toolbar';
 
1344
                                        menu.appendChild(header);
 
1345
 
 
1346
                                        var scroll = document.createElement('div');
 
1347
                                        scroll.className = 'scroll';
 
1348
                                        menu.appendChild(scroll);
 
1349
 
 
1350
                                        var list = document.createElement('ul');
 
1351
                                        scroll.appendChild(list);
 
1352
 
 
1353
 
 
1354
                                        (function(c, menu, list, header) {
 
1355
                                                var search = new SearchField({
 
1356
                                                        parent:         header,
 
1357
                                                        onQuery:        function(query) {
 
1358
                                                                                        build(list, tests, query);
 
1359
                                                                                }
 
1360
                                                });
 
1361
 
 
1362
 
 
1363
                                                document.body.addEventListener('click', function(e) {
 
1364
                                                        menu.className = menu.className.replace(' visible', '');
 
1365
                                                }, false);
 
1366
 
 
1367
                                                div.addEventListener('click', function(e) {
 
1368
                                                        if (that.data[c] == null)
 
1369
                                                                menu.className += ' visible';
 
1370
                                                        else
 
1371
                                                                that.clearColumn(c);
 
1372
 
 
1373
                                                        e.stopPropagation();
 
1374
                                                }, true);
 
1375
 
 
1376
                                                list.addEventListener('click', function(e) {
 
1377
                                                        var close = true;
 
1378
 
 
1379
                                                        if (e.target) {
 
1380
                                                                var target = e.target;
 
1381
 
 
1382
                                                                while (target.tagName != 'LI' && target.parentNode) {
 
1383
                                                                        target = target.parentNode;
 
1384
                                                                }
 
1385
 
 
1386
                                                                if (target.hasAttribute('data-action')) {
 
1387
                                                                        var action = target.getAttribute('data-action');
 
1388
 
 
1389
                                                                        if (action == 'load') {
 
1390
                                                                                var key = target.getAttribute('data-key');
 
1391
                                                                                that.loadColumn(c, key);
 
1392
                                                                        }
 
1393
                                                                }
 
1394
                                                        }
 
1395
 
 
1396
                                                        if (close) {
 
1397
                                                                menu.className = menu.className.replace(' visible', '');
 
1398
                                                        }
 
1399
 
 
1400
                                                        e.stopPropagation();
 
1401
                                                }, true);
 
1402
                                        })(c, menu, list, header);
 
1403
 
 
1404
 
 
1405
                                        build(list, tests);
 
1406
 
 
1407
 
 
1408
                                        function build(list, tests, filter) {
 
1409
                                                list.innerHTML = '';
 
1410
 
 
1411
                                                var type = null;
 
1412
 
 
1413
                                                for (var i = 0; i < tests.length; i++) {
 
1414
                                                        if (!filter || (typeof tests[i].key != 'undefined' && tests[i].name.toLowerCase().indexOf(filter.toLowerCase()) != -1)) {
 
1415
                                                                var item = document.createElement('li');
 
1416
 
 
1417
                                                                if (!filter) item.className = 'indent-' + tests[i].indent;
 
1418
 
 
1419
                                                                if (typeof tests[i].key != 'undefined') {
 
1420
                                                                        item.setAttribute('data-action', 'load');
 
1421
                                                                        item.setAttribute('data-key', tests[i].key);
 
1422
                                                                } else {
 
1423
                                                                        item.className += ' title';
 
1424
                                                                }
 
1425
 
 
1426
                                                                item.innerHTML = tests[i].name;
 
1427
                                                                list.appendChild(item);
 
1428
                                                        }
 
1429
                                                }
 
1430
                                        }
 
1431
                                }
 
1432
                        }
 
1433
 
 
1434
                        var table = document.createElement('table');
 
1435
                        parent.appendChild(table);
 
1436
 
 
1437
                        var tbody = document.createElement('tbody');
 
1438
                        table.appendChild(tbody);
 
1439
 
 
1440
                        var type = null;
 
1441
                        for (var i = 0; i < this.browsers.length; i++) {
 
1442
                                if (type != this.browsers[i].type) {
 
1443
                                        var tr = document.createElement('tr');
 
1444
                                        tbody.appendChild(tr);
 
1445
 
 
1446
                                        var th = document.createElement('th');
 
1447
                                        th.className = 'details';
 
1448
                                        th.colSpan = this.options.columns + 1;
 
1449
                                        tr.appendChild(th);
 
1450
 
 
1451
                                        switch(this.browsers[i].type) {
 
1452
                                                case 'desktop':         th.innerHTML = '<h3>Desktop browsers</h3>'; break;
 
1453
                                                case 'gaming':          th.innerHTML = '<h3>Gaming</h3>'; break;
 
1454
                                                case 'mobile':          th.innerHTML = '<h3>Mobiles</h3>'; break;
 
1455
                                                case 'tablet':          th.innerHTML = '<h3>Tablets</h3>'; break;
 
1456
                                                case 'television':      th.innerHTML = '<h3>Television</h3>'; break;
 
1457
                                        }
 
1458
                                }
 
1459
 
 
1460
                                var tr = document.createElement('tr');
 
1461
                                tr.id = 'row-' + this.browsers[i].uid;
 
1462
                                tbody.appendChild(tr);
 
1463
 
 
1464
                                var th = document.createElement('th');
 
1465
                                th.className = 'hasLink';
 
1466
                                th.innerHTML =  this.browsers[i].nickname + (this.browsers[i].details ? ' <em>(' + this.browsers[i].details + ')</em>' : '');
 
1467
                                tr.appendChild(th);
 
1468
 
 
1469
                                (function(th, type, data){
 
1470
                                        th.onclick = function() {
 
1471
                                                new BrowserPopup(th, type, data);
 
1472
                                        };
 
1473
                                })(th, type, {
 
1474
                                        platform:       this.browsers[i].platform,
 
1475
                                        version:        this.browsers[i].version,
 
1476
                                        id:                     this.browsers[i].id,
 
1477
                                        name:           this.browsers[i].nickname,
 
1478
                                        score:          this.browsers[i].score,
 
1479
                                        urls:           []
 
1480
                                });
 
1481
 
 
1482
                                for (var c = 0; c < this.options.columns; c++) {
 
1483
                                        var td = document.createElement('td');
 
1484
                                        tr.appendChild(td);
 
1485
                                }
 
1486
 
 
1487
                                type = this.browsers[i].type;
 
1488
                        }
 
1489
                },
 
1490
 
 
1491
                getList: function(items, level) {
 
1492
                        if (typeof level == 'undefined') level = 0;
 
1493
 
 
1494
                        var result = [];
 
1495
 
 
1496
                        for (var i = 0; i < items.length; i++) {
 
1497
                                if (typeof items[i] == 'object') {
 
1498
                                        if (typeof items[i].items == 'undefined') {
 
1499
                                                if (level > 0) {
 
1500
                                                        result.push({
 
1501
                                                                key:    items[i].key,
 
1502
                                                                name:   items[i].name,
 
1503
                                                                indent: level
 
1504
                                                        })
 
1505
                                                }
 
1506
                                        }
 
1507
 
 
1508
                                        if (typeof items[i].items != 'undefined') {
 
1509
                                                if (level > 0) {
 
1510
                                                        result.push({
 
1511
                                                                name:   items[i].name,
 
1512
                                                                indent: level
 
1513
                                                        })
 
1514
                                                }
 
1515
 
 
1516
                                                if (children = this.getList(items[i].items, level + 1)) {
 
1517
                                                        for (var c = 0; c < children.length; c++) {
 
1518
                                                                result.push(children[c]);
 
1519
                                                        }
 
1520
                                                }
 
1521
                                        }
 
1522
                                }
 
1523
                        }
 
1524
 
 
1525
                        return result;
 
1526
                },
 
1527
 
 
1528
                getItemByKey: function(items, key, level) {
 
1529
                        if (typeof level == 'undefined') level = 0;
 
1530
 
 
1531
                        for (var i = 0; i < items.length; i++) {
 
1532
                                if (typeof items[i] == 'object') {
 
1533
                                        if (items[i].key == key) return items[i];
 
1534
                                        if (typeof items[i].items != 'undefined') {
 
1535
                                                if (result = this.getItemByKey(items[i].items, key, level + 1)) {
 
1536
                                                        return result;
 
1537
                                                }
 
1538
                                        }
 
1539
                                }
 
1540
                        }
 
1541
                }
 
1542
        }
 
1543
 
 
1544
 
 
1545
 
 
1546
 
 
1547
 
 
1548
        var DiffTable = function() { this.initialize.apply(this, arguments) };
 
1549
        DiffTable.prototype = {
 
1550
 
 
1551
                initialize: function(options) {
 
1552
                        this.parent = options.parent;
 
1553
                        this.metadata = options.metadata;
 
1554
                        this.data = options.data;
 
1555
 
 
1556
                        this.createSections(this.parent);
 
1557
                },
 
1558
 
 
1559
                createSections: function(parent) {
 
1560
                        var table = document.createElement('table');
 
1561
                        parent.appendChild(table);
 
1562
 
 
1563
                        var tbody = document.createElement('tbody');
 
1564
                        table.appendChild(tbody);
 
1565
 
 
1566
                        for (var i = 0; i < this.data.length; i++) {
 
1567
                                if (this.metadata.getItem(this.data[i].id)) {
 
1568
                                        var tr = document.createElement('tr');
 
1569
                                        tbody.appendChild(tr);
 
1570
 
 
1571
                                        var th = document.createElement('th');
 
1572
                                        th.innerHTML = "<a href='/compare/feature/" + this.data[i].id + ".html'>" + this.metadata.getTrail(this.data[i].id, ' ▸ ') + "</a>";
 
1573
                                        tr.appendChild(th);
 
1574
 
 
1575
                                        var td = document.createElement('td');
 
1576
                                        td.innerHTML = "<div>" + this.getStatus(this.data[i].from) + " <span>→</span> " + this.getStatus(this.data[i].to) + "</div>";
 
1577
                                        tr.appendChild(td);
 
1578
                                }
 
1579
                        }
 
1580
                },
 
1581
 
 
1582
                getStatus: function(status) {
 
1583
                        html = '';
 
1584
                        status = parseInt(status, 10);
 
1585
 
 
1586
                        if (status & YES) {
 
1587
                                switch(true) {
 
1588
                                        case !! (status & BUGGY):                       html = '<div>Buggy <span class="buggy"></span></div>'; break;
 
1589
                                        case !! (status & OLD):                         html = '<div>Partial <span class="partial">○</span></div>'; break;
 
1590
                                        case !! (status & PREFIX):                      html = '<div>Prefixed <span class="check">✔</span></div>'; break;
 
1591
                                        case !! (status & EXPERIMENTAL):        html = '<div>Prefixed <span class="check">✔</span></div>'; break;
 
1592
                                        default:                                                        html = '<div>Yes <span class="check">✔</span></div>'; break;
 
1593
                                }
 
1594
                        }
 
1595
                        else {
 
1596
                                switch(true) {
 
1597
                                        case !! (status & UNKNOWN):                     html = '<div>Unknown <span class="partial">?</span></div>'; break;
 
1598
                                        case !! (status & BLOCKED):                     html = '<div>Not functional <span class="buggy">!</span></div>'; break;
 
1599
                                        case !! (status & DISABLED):            html = '<div>Disabled <span class="ballot">✘</span></div>'; break;
 
1600
                                        default:                                                        html = '<div>No <span class="ballot">✘</span></div>'; break;
 
1601
                                }
 
1602
                        }
 
1603
 
 
1604
                        return html;
 
1605
                }
 
1606
        }
 
1607
 
 
1608
 
 
1609
 
 
1610
        var BrowserPopup = function() { this.initialize.apply(this, arguments) };
 
1611
        BrowserPopup.current = null;
 
1612
        BrowserPopup.prototype = {
 
1613
                initialize: function(parent, type, data) {
 
1614
                        if (BrowserPopup.current) {
 
1615
                                BrowserPopup.current.close();
 
1616
                        }
 
1617
 
 
1618
                        var browser = data.platform + (data.version ? "-" + data.version : "");
 
1619
 
 
1620
                        var content = "";
 
1621
                        content += "<div class='info'>";
 
1622
                        content += "<div class='column left score'><h2>" + data.score + "</h2><span>Points</span></div>";
 
1623
                        content += "<div class='column middle'><a href='/results/" + type + "/timeline/" + data.id +".html' class='timeline'><span>Timeline</span></a></div>";
 
1624
                        content += "<div class='column right'><a href='/compare/browser/" + browser +".html' class='compare'><span>Compare</span></a></div>";
 
1625
                        content += "</div>";
 
1626
 
 
1627
                        if (typeof data.urls != 'undefined') {
 
1628
                                content += "<div class='links'>";
 
1629
 
 
1630
                                for (var i = 0; i < data.urls.length; i++) {
 
1631
                                }
 
1632
 
 
1633
                                content += "</div>";
 
1634
                        }
 
1635
 
 
1636
                        this.panel = document.createElement('div');
 
1637
                        this.panel.className = 'linksPanel popupPanel pointsLeft';
 
1638
                        this.panel.innerHTML = content;
 
1639
                        parent.appendChild(this.panel);
 
1640
 
 
1641
                        BrowserPopup.current = this;
 
1642
                },
 
1643
 
 
1644
                close: function() {
 
1645
                        this.panel.parentNode.removeChild(this.panel);
 
1646
                        BrowserPopup.current = null;
 
1647
                }
 
1648
        }
 
1649
 
 
1650
        document.addEventListener('click', function() { if (BrowserPopup.current) BrowserPopup.current.close() }, true)
 
1651
        document.addEventListener('touchstart', function() { if (BrowserPopup.current) BrowserPopup.current.close() }, true)