~chromium-team/chromium-browser/artful-beta

« back to all changes in this revision

Viewing changes to debian/tests/data/HTML5test/scripts/8/engine.js

  • Committer: Olivier Tilloy
  • Date: 2017-05-23 04:54:32 UTC
  • Revision ID: olivier.tilloy@canonical.com-20170523045432-iwy8f0mwora5q8bw
Remove stale autopkgtests and add a couple of new ones based on chromium's new headless mode.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
Test =
 
3
Test8 = (function () {
 
4
 
 
5
    var release = 8;
 
6
 
 
7
    var NO = 0,
 
8
        YES = 1,
 
9
        OLD = 2,
 
10
        BUGGY = 4,
 
11
        PREFIX = 8,
 
12
        BLOCKED = 16,
 
13
        DISABLED = 32,
 
14
        UNCONFIRMED = 64,
 
15
        UNKNOWN = 128,
 
16
        EXPERIMENTAL = 256;
 
17
 
 
18
    var blacklists = [];
 
19
 
 
20
 
 
21
    var testsuite = [
 
22
 
 
23
        /* doctype */
 
24
 
 
25
        function (results) {
 
26
            results.addItem({
 
27
                key: 'parsing.doctype',
 
28
                passed: document.compatMode == 'CSS1Compat'
 
29
            });
 
30
        },
 
31
 
 
32
 
 
33
        /* tokenizer */
 
34
 
 
35
        function (results) {
 
36
            var result = true;
 
37
            var e = document.createElement('div');
 
38
 
 
39
            try {
 
40
                e.innerHTML = "<div<div>";
 
41
                result &= e.firstChild && e.firstChild.nodeName == "DIV<DIV";
 
42
 
 
43
                e.innerHTML = "<div foo<bar=''>";
 
44
                result &= e.firstChild.attributes[0].nodeName == "foo<bar" || e.firstChild.attributes[0].name == "foo<bar";
 
45
 
 
46
                e.innerHTML = "<div foo=`bar`>";
 
47
                result &= e.firstChild.getAttribute("foo") == "`bar`";
 
48
 
 
49
                e.innerHTML = "<div \"foo=''>";
 
50
                result &= e.firstChild && (e.firstChild.attributes[0].nodeName == "\"foo" || e.firstChild.attributes[0].name == "\"foo");
 
51
 
 
52
                e.innerHTML = "<a href='\nbar'></a>";
 
53
                result &= e.firstChild && e.firstChild.getAttribute("href") == "\nbar";
 
54
 
 
55
                e.innerHTML = "<!DOCTYPE html>";
 
56
                result &= e.firstChild == null;
 
57
 
 
58
                e.innerHTML = "\u000D";
 
59
                result &= e.firstChild && e.firstChild.nodeValue == "\u000A";
 
60
 
 
61
                e.innerHTML = "&lang;&rang;";
 
62
                result &= e.firstChild.nodeValue == "\u27E8\u27E9";
 
63
 
 
64
                e.innerHTML = "&apos;";
 
65
                result &= e.firstChild.nodeValue == "'";
 
66
 
 
67
                e.innerHTML = "&ImaginaryI;";
 
68
                result &= e.firstChild.nodeValue == "\u2148";
 
69
 
 
70
                e.innerHTML = "&Kopf;";
 
71
                result &= e.firstChild.nodeValue == "\uD835\uDD42";
 
72
 
 
73
                e.innerHTML = "&notinva;";
 
74
                result &= e.firstChild.nodeValue == "\u2209";
 
75
 
 
76
                e.innerHTML = '<?import namespace="foo" implementation="#bar">';
 
77
                result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == '?import namespace="foo" implementation="#bar"';
 
78
 
 
79
                e.innerHTML = '<!--foo--bar-->';
 
80
                result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == 'foo--bar';
 
81
 
 
82
                e.innerHTML = '<![CDATA[x]]>';
 
83
                result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == '[CDATA[x]]';
 
84
 
 
85
                e.innerHTML = "<textarea><!--</textarea>--></textarea>";
 
86
                result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == "<!--";
 
87
 
 
88
                e.innerHTML = "<textarea><!--</textarea>-->";
 
89
                result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == "<!--";
 
90
 
 
91
                e.innerHTML = "<style><!--</style>--></style>";
 
92
                result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == "<!--";
 
93
 
 
94
                e.innerHTML = "<style><!--</style>-->";
 
95
                result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == "<!--";
 
96
            } catch (e) {
 
97
                result = false;
 
98
            }
 
99
 
 
100
            results.addItem({
 
101
                key: 'parsing.tokenizer',
 
102
                passed: result
 
103
            });
 
104
        },
 
105
 
 
106
 
 
107
        /* tree builder */
 
108
 
 
109
        function (results) {
 
110
            var result = true;
 
111
            var e = document.createElement('div');
 
112
 
 
113
            try {
 
114
                var h = document.createElement("html");
 
115
                h.innerHTML = "";
 
116
                result &= h.firstChild && h.firstChild.nodeName == "HEAD" && h.lastChild.nodeName == "BODY" && h.firstChild.nextSibling == h.lastChild;
 
117
            } catch (e) {
 
118
                result = false;
 
119
            }
 
120
 
 
121
            try {
 
122
                var t = document.createElement("table");
 
123
                t.innerHTML = "<col>";
 
124
                result &= t.firstChild && t.firstChild.nodeName == "COLGROUP";
 
125
            } catch (e) {
 
126
                result = false;
 
127
            }
 
128
 
 
129
            e.innerHTML = "<ul><li>A </li> <li>B</li></ul>";
 
130
            result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.firstChild && e.firstChild.firstChild.firstChild.nodeValue == "A ";
 
131
 
 
132
            e.innerHTML = "<table><form><input type=hidden><input></form><div></div></table>";
 
133
            result &= e.firstChild &&
 
134
                e.firstChild.nodeName == "INPUT" &&
 
135
                e.firstChild.nextSibling &&
 
136
                e.firstChild.nextSibling.nodeName == "DIV" &&
 
137
                e.lastChild.nodeName == "TABLE" &&
 
138
                e.firstChild.nextSibling.nextSibling == e.lastChild &&
 
139
                e.lastChild.firstChild &&
 
140
                e.lastChild.firstChild.nodeName == "FORM" &&
 
141
                e.lastChild.firstChild.firstChild == null &&
 
142
                e.lastChild.lastChild.nodeName == "INPUT" &&
 
143
                e.lastChild.firstChild.nextSibling == e.lastChild.lastChild;
 
144
 
 
145
            e.innerHTML = "<i>A<b>B<p></i>C</b>D";
 
146
            result &= e.firstChild &&
 
147
                e.childNodes.length == 3 &&
 
148
                e.childNodes[0].nodeName == "I" &&
 
149
                e.childNodes[0].childNodes.length == 2 &&
 
150
                e.childNodes[0].childNodes[0].nodeValue == "A" &&
 
151
                e.childNodes[0].childNodes[1].nodeName == "B" &&
 
152
                e.childNodes[0].childNodes[1].childNodes.length == 1 &&
 
153
                e.childNodes[0].childNodes[1].childNodes[0].nodeValue == "B" &&
 
154
                e.childNodes[1].nodeName == "B" &&
 
155
                e.childNodes[1].firstChild == null &&
 
156
                e.childNodes[2].nodeName == "P" &&
 
157
                e.childNodes[2].childNodes.length == 2 &&
 
158
                e.childNodes[2].childNodes[0].nodeName == "B" &&
 
159
                e.childNodes[2].childNodes[0].childNodes.length == 2 &&
 
160
                e.childNodes[2].childNodes[0].childNodes[0].nodeName == "I" &&
 
161
                e.childNodes[2].childNodes[0].childNodes[0].firstChild == null &&
 
162
                e.childNodes[2].childNodes[0].childNodes[1].nodeValue == "C" &&
 
163
                e.childNodes[2].childNodes[1].nodeValue == "D";
 
164
 
 
165
            e.innerHTML = "<div></div>";
 
166
            result &= e.firstChild && "namespaceURI" in e.firstChild && e.firstChild.namespaceURI == "http://www.w3.org/1999/xhtml";
 
167
 
 
168
            results.addItem({
 
169
                key: 'parsing.tree',
 
170
                passed: result
 
171
            });
 
172
        },
 
173
 
 
174
 
 
175
        /* svg in html */
 
176
 
 
177
        function (results) {
 
178
            var e = document.createElement('div');
 
179
            e.innerHTML = '<svg></svg>';
 
180
            var passed = e.firstChild && "namespaceURI" in e.firstChild && e.firstChild.namespaceURI == 'http://www.w3.org/2000/svg';
 
181
 
 
182
            results.addItem({
 
183
                key: 'parsing.svg',
 
184
                passed: passed
 
185
            });
 
186
        },
 
187
 
 
188
 
 
189
        /* svg in html */
 
190
 
 
191
        function (results) {
 
192
            var e = document.createElement('div');
 
193
            e.innerHTML = '<math></math>';
 
194
            var passed = e.firstChild && "namespaceURI" in e.firstChild && e.firstChild.namespaceURI == 'http://www.w3.org/1998/Math/MathML';
 
195
 
 
196
            results.addItem({
 
197
                key: 'parsing.mathml',
 
198
                passed: passed
 
199
            });
 
200
        },
 
201
 
 
202
        /* dataset */
 
203
 
 
204
        function (results) {
 
205
            var element = document.createElement('div');
 
206
            element.setAttribute('data-test', 'test');
 
207
 
 
208
            results.addItem({
 
209
                key: 'elements.dataset',
 
210
                passed: 'dataset' in element
 
211
            });
 
212
        },
 
213
 
 
214
 
 
215
        /* section, nav, article, header and footer */
 
216
 
 
217
        function (results) {
 
218
            var elements = 'section nav article aside header footer'.split(' ');
 
219
 
 
220
            for (var e = 0; e < elements.length; e++) {
 
221
                var passed = false;
 
222
 
 
223
                try {
 
224
                    var element = document.createElement(elements[e]);
 
225
                    document.body.appendChild(element);
 
226
 
 
227
                    try {
 
228
                        passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement) && isBlock(element) && closesImplicitly(elements[e]);
 
229
                    } catch (error) {
 
230
                    }
 
231
 
 
232
                    document.body.removeChild(element);
 
233
                } catch (error) {
 
234
                }
 
235
 
 
236
                results.addItem({
 
237
                    key: 'elements.section.' + elements[e],
 
238
                    passed: passed,
 
239
                    value: 1
 
240
                });
 
241
            }
 
242
 
 
243
        },
 
244
 
 
245
 
 
246
        /* main, figure and figcaption */
 
247
 
 
248
        function (results) {
 
249
            var elements = 'main figure figcaption'.split(' ');
 
250
 
 
251
            for (var e = 0; e < elements.length; e++) {
 
252
                var passed = false;
 
253
 
 
254
                try {
 
255
                    var element = document.createElement(elements[e]);
 
256
                    document.body.appendChild(element);
 
257
 
 
258
                    try {
 
259
                        passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement) && isBlock(element) && (elements[e] != 'figure' || closesImplicitly(elements[e]));
 
260
                    } catch (error) {
 
261
                    }
 
262
 
 
263
                    document.body.removeChild(element);
 
264
                } catch (error) {
 
265
                }
 
266
 
 
267
                results.addItem({
 
268
                    key: 'elements.grouping.' + elements[e],
 
269
                    passed: passed
 
270
                });
 
271
            }
 
272
 
 
273
        },
 
274
 
 
275
 
 
276
        /* ol grouping */
 
277
 
 
278
        function (results) {
 
279
            results.addItem({
 
280
                key: 'elements.grouping.ol',
 
281
                passed: 'reversed' in document.createElement('ol')
 
282
            });
 
283
 
 
284
        },
 
285
 
 
286
 
 
287
        /* a download */
 
288
 
 
289
        function (results) {
 
290
            results.addItem({
 
291
                key: 'elements.semantic.download',
 
292
                passed: 'download' in document.createElement('a')
 
293
            });
 
294
        },
 
295
 
 
296
 
 
297
        /* a ping */
 
298
 
 
299
        function (results) {
 
300
            results.addItem({
 
301
                key: 'elements.semantic.ping',
 
302
                passed: 'ping' in document.createElement('a')
 
303
            });
 
304
        },
 
305
 
 
306
 
 
307
        /* mark element */
 
308
 
 
309
        function (results) {
 
310
            var passed = false;
 
311
 
 
312
            try {
 
313
                var element = document.createElement('mark');
 
314
                document.body.appendChild(element);
 
315
 
 
316
                try {
 
317
                    passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement) && (color = getStyle(element, 'background-color')) && (color != 'transparent');
 
318
                } catch (error) {
 
319
                }
 
320
 
 
321
                document.body.removeChild(element);
 
322
            } catch (error) {
 
323
            }
 
324
 
 
325
            results.addItem({
 
326
                key: 'elements.semantic.mark',
 
327
                passed: passed
 
328
            });
 
329
        },
 
330
 
 
331
 
 
332
        /* ruby, rt, rp element */
 
333
 
 
334
        function (results) {
 
335
            var container = document.createElement('div');
 
336
            document.body.appendChild(container);
 
337
            container.innerHTML = "<ruby id='ruby'><rp id='rp'></rp><rt id='rt'></rt></ruby>";
 
338
            var rubyElement = document.getElementById('ruby');
 
339
            var rtElement = document.getElementById('rt');
 
340
            var rpElement = document.getElementById('rp');
 
341
 
 
342
            var rubySupport = false;
 
343
            var rtSupport = false;
 
344
            var rpSupport = false;
 
345
 
 
346
            try {
 
347
                rubySupport = rubyElement && rubyElement instanceof HTMLElement && !(rubyElement instanceof HTMLUnknownElement);
 
348
                rtSupport = rtElement && rtElement instanceof HTMLElement && !(rtElement instanceof HTMLUnknownElement);
 
349
                rpSupport = rpElement && rpElement instanceof HTMLElement && !(rpElement instanceof HTMLUnknownElement) && isHidden(rpElement);
 
350
            } catch (error) {
 
351
            }
 
352
 
 
353
            document.body.removeChild(container);
 
354
 
 
355
            results.addItem({
 
356
                key: 'elements.semantic.ruby',
 
357
                passed: rubySupport && rtSupport && rpSupport
 
358
            });
 
359
        },
 
360
 
 
361
 
 
362
        /* time element */
 
363
 
 
364
        function (results) {
 
365
            var passed = false;
 
366
 
 
367
            try {
 
368
                var element = document.createElement('time');
 
369
 
 
370
                try {
 
371
                    passed = typeof HTMLTimeElement != 'undefined' && element instanceof HTMLTimeElement;
 
372
                } catch (error) {
 
373
                }
 
374
            } catch (error) {
 
375
            }
 
376
 
 
377
            results.addItem({
 
378
                key: 'elements.semantic.time',
 
379
                passed: passed
 
380
            });
 
381
        },
 
382
 
 
383
 
 
384
        /* data element */
 
385
 
 
386
        function (results) {
 
387
            var passed = false;
 
388
 
 
389
            try {
 
390
                var element = document.createElement('data');
 
391
 
 
392
                try {
 
393
                    passed = typeof HTMLDataElement != 'undefined' && element instanceof HTMLDataElement;
 
394
                } catch (error) {
 
395
                }
 
396
            } catch (error) {
 
397
            }
 
398
 
 
399
            results.addItem({
 
400
                key: 'elements.semantic.data',
 
401
                passed: passed
 
402
            });
 
403
        },
 
404
 
 
405
 
 
406
        /* wbr element */
 
407
 
 
408
        function (results) {
 
409
            var passed = false;
 
410
 
 
411
            try {
 
412
                var element = document.createElement('wbr');
 
413
 
 
414
                try {
 
415
                    passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement);
 
416
                } catch (error) {
 
417
                }
 
418
            } catch (error) {
 
419
            }
 
420
 
 
421
            results.addItem({
 
422
                key: 'elements.semantic.wbr',
 
423
                passed: passed
 
424
            });
 
425
        },
 
426
 
 
427
 
 
428
        /* details element */
 
429
 
 
430
        function (results) {
 
431
            var passed = false;
 
432
 
 
433
            try {
 
434
                var element = document.createElement('details');
 
435
                element.innerHTML = '<summary>a</summary>b';
 
436
                document.body.appendChild(element);
 
437
 
 
438
                var height = element.offsetHeight;
 
439
                element.open = true;
 
440
 
 
441
                passed = height != element.offsetHeight;
 
442
 
 
443
                document.body.removeChild(element);
 
444
            } catch (error) {
 
445
            }
 
446
 
 
447
            results.addItem({
 
448
                key: 'elements.interactive.details',
 
449
                passed: passed
 
450
            });
 
451
        },
 
452
 
 
453
 
 
454
        /* summary element */
 
455
 
 
456
        function (results) {
 
457
            var passed = false;
 
458
 
 
459
            try {
 
460
                var element = document.createElement('summary');
 
461
                document.body.appendChild(element);
 
462
 
 
463
                try {
 
464
                    passed = element instanceof HTMLElement && !(element instanceof HTMLUnknownElement);
 
465
                } catch (error) {
 
466
                }
 
467
 
 
468
                document.body.removeChild(element);
 
469
            } catch (error) {
 
470
            }
 
471
 
 
472
            results.addItem({
 
473
                key: 'elements.interactive.summary',
 
474
                passed: passed
 
475
            });
 
476
        },
 
477
 
 
478
 
 
479
        /* menu toolbar */
 
480
 
 
481
        function (results) {
 
482
            var passed = legacy = false;
 
483
 
 
484
            try {
 
485
                var element = document.createElement('menu');
 
486
                document.body.appendChild(element);
 
487
 
 
488
                try {
 
489
                    legacy = typeof HTMLMenuElement != 'undefined' && element instanceof HTMLMenuElement && 'type' in element;
 
490
                } catch (error) {
 
491
                }
 
492
 
 
493
                // Check default type
 
494
                if (legacy && element.type != 'list') legacy = false;
 
495
 
 
496
                // Check type sanitization
 
497
                try {
 
498
                    element.type = 'foobar';
 
499
                } catch (error) {
 
500
                }
 
501
 
 
502
                if (legacy && element.type == 'foobar') legacy = false;
 
503
 
 
504
                // Check if correct type sticks
 
505
                try {
 
506
                    element.type = 'list';
 
507
                } catch (error) {
 
508
                    legacy = false;
 
509
                }
 
510
 
 
511
                if (legacy && element.type != 'list') legacy = false;
 
512
 
 
513
                document.body.removeChild(element);
 
514
            } catch (error) {
 
515
            }
 
516
 
 
517
            try {
 
518
                var element = document.createElement('menu');
 
519
                document.body.appendChild(element);
 
520
 
 
521
                try {
 
522
                    passed = typeof HTMLMenuElement != 'undefined' && element instanceof HTMLMenuElement && 'type' in element;
 
523
                } catch (error) {
 
524
                }
 
525
 
 
526
                // Check default type
 
527
                if (passed && element.type != 'toolbar') passed = false;
 
528
 
 
529
                // Check type sanitization
 
530
                try {
 
531
                    element.type = 'foobar';
 
532
                } catch (error) {
 
533
                }
 
534
 
 
535
                if (passed && element.type == 'foobar') passed = false;
 
536
 
 
537
                // Check if correct type sticks
 
538
                try {
 
539
                    element.type = 'toolbar';
 
540
                } catch (error) {
 
541
                    passed = false;
 
542
                }
 
543
 
 
544
                if (passed && element.type != 'toolbar') passed = false;
 
545
 
 
546
                document.body.removeChild(element);
 
547
            } catch (error) {
 
548
            }
 
549
 
 
550
            results.addItem({
 
551
                key: 'elements.interactive.menutoolbar',
 
552
                passed: passed ? YES : legacy ? YES | OLD : NO
 
553
            });
 
554
        },
 
555
 
 
556
 
 
557
        /* menu context */
 
558
 
 
559
        function (results) {
 
560
            var passed = legacy = false;
 
561
 
 
562
            try {
 
563
                var element = document.createElement('menu');
 
564
                document.body.appendChild(element);
 
565
 
 
566
                try {
 
567
                    legacy = typeof HTMLMenuElement != 'undefined' && element instanceof HTMLMenuElement && 'type' in element;
 
568
                } catch (error) {
 
569
                }
 
570
 
 
571
                // Check if correct type sticks
 
572
                try {
 
573
                    element.type = 'popup';
 
574
                } catch (error) {
 
575
                    legacy = false;
 
576
                }
 
577
 
 
578
                if (legacy && element.type != 'popup') legacy = false;
 
579
 
 
580
 
 
581
                if (legacy) {
 
582
                    var item = document.createElement('menuitem');
 
583
                    element.appendChild(item);
 
584
 
 
585
                    if (typeof HTMLMenuItemElement == 'undefined' || !item instanceof HTMLMenuItemElement) legacy = false;
 
586
                }
 
587
 
 
588
                document.body.removeChild(element);
 
589
            } catch (error) {
 
590
            }
 
591
 
 
592
            try {
 
593
                var element = document.createElement('menu');
 
594
                document.body.appendChild(element);
 
595
 
 
596
                try {
 
597
                    passed = typeof HTMLMenuElement != 'undefined' && element instanceof HTMLMenuElement && 'type' in element;
 
598
                } catch (error) {
 
599
                }
 
600
 
 
601
                try {
 
602
                    element.type = 'context';
 
603
                } catch (error) {
 
604
                }
 
605
 
 
606
                // Check default type
 
607
                var second = document.createElement('menu');
 
608
                element.appendChild(second);
 
609
                if (passed && second.type == 'list') legacy = true;
 
610
                if (passed && second.type != 'context') passed = false;
 
611
                element.removeChild(second);
 
612
 
 
613
                // Check type sanitization
 
614
                try {
 
615
                    element.type = 'foobar';
 
616
                } catch (error) {
 
617
                }
 
618
 
 
619
                if (passed && element.type == 'foobar') passed = false;
 
620
 
 
621
                // Check if correct type sticks
 
622
                try {
 
623
                    element.type = 'context';
 
624
                } catch (error) {
 
625
                    passed = false;
 
626
                }
 
627
 
 
628
                if (passed && element.type != 'context') passed = false;
 
629
 
 
630
 
 
631
                if (passed) {
 
632
                    var item = document.createElement('menuitem');
 
633
                    element.appendChild(item);
 
634
 
 
635
                    if (typeof HTMLMenuItemElement == 'undefined' || !item instanceof HTMLMenuItemElement) passed = false;
 
636
                }
 
637
 
 
638
                document.body.removeChild(element);
 
639
            } catch (error) {
 
640
            }
 
641
 
 
642
            results.addItem({
 
643
                key: 'elements.interactive.menucontext',
 
644
                passed: passed ? YES : legacy ? YES | OLD : NO
 
645
            });
 
646
        },
 
647
 
 
648
 
 
649
        /* dialog element */
 
650
 
 
651
        function (results) {
 
652
            var passed = false;
 
653
 
 
654
            try {
 
655
                var element = document.createElement('dialog');
 
656
 
 
657
                try {
 
658
                    passed = typeof HTMLDialogElement != 'undefined' && element instanceof HTMLDialogElement;
 
659
                } catch (error) {
 
660
                }
 
661
            } catch (error) {
 
662
            }
 
663
 
 
664
            results.addItem({
 
665
                key: 'elements.interactive.dialog',
 
666
                passed: passed
 
667
            });
 
668
        },
 
669
 
 
670
 
 
671
        /* hidden attribute */
 
672
 
 
673
        function (results) {
 
674
            results.addItem({
 
675
                key: 'elements.hidden',
 
676
                passed: 'hidden' in document.createElement('div')
 
677
            });
 
678
        },
 
679
 
 
680
 
 
681
        /* outerHTML property */
 
682
 
 
683
        function (results) {
 
684
            results.addItem({
 
685
                key: 'elements.dynamic.outerHTML',
 
686
                passed: 'outerHTML' in document.createElement('div')
 
687
            });
 
688
        },
 
689
 
 
690
 
 
691
        /* insertAdjacentHTML property */
 
692
 
 
693
        function (results) {
 
694
            results.addItem({
 
695
                key: 'elements.dynamic.insertAdjacentHTML',
 
696
                passed: 'insertAdjacentHTML' in document.createElement('div')
 
697
            });
 
698
        },
 
699
 
 
700
 
 
701
        /* input type=text */
 
702
 
 
703
        function (results) {
 
704
            var element = createInput('text');
 
705
 
 
706
            results.addItem({
 
707
                key: 'form.text.element',
 
708
                passed: element.type == 'text'
 
709
            });
 
710
 
 
711
            results.addItem({
 
712
                key: 'form.text.selection',
 
713
                passed: 'selectionDirection' in element
 
714
            });
 
715
        },
 
716
 
 
717
 
 
718
        /* input type=search */
 
719
 
 
720
        function (results) {
 
721
            var element = createInput('search');
 
722
 
 
723
            results.addItem({
 
724
                key: 'form.search.element',
 
725
                passed: element.type == 'search'
 
726
            });
 
727
        },
 
728
 
 
729
 
 
730
        /* input type=tel */
 
731
 
 
732
        function (results) {
 
733
            var element = createInput('tel');
 
734
 
 
735
            results.addItem({
 
736
                key: 'form.tel.element',
 
737
                passed: element.type == 'tel'
 
738
            });
 
739
        },
 
740
 
 
741
 
 
742
        /* input type=url */
 
743
 
 
744
        function (results) {
 
745
            var element = createInput('url');
 
746
 
 
747
            var validation = false;
 
748
            if ('validity' in element) {
 
749
                validation = true;
 
750
 
 
751
                element.value = "foo";
 
752
                validation &= !element.validity.valid
 
753
 
 
754
                element.value = "http://foo.org";
 
755
                validation &= element.validity.valid
 
756
            }
 
757
 
 
758
            results.addItem({
 
759
                key: 'form.url.element',
 
760
                passed: element.type == 'url'
 
761
            });
 
762
 
 
763
            results.addItem({
 
764
                key: 'form.url.validation',
 
765
                passed: validation
 
766
            });
 
767
        },
 
768
 
 
769
 
 
770
        /* input type=email */
 
771
 
 
772
        function (results) {
 
773
            var element = createInput('email');
 
774
 
 
775
            var validation = false;
 
776
            if ('validity' in element) {
 
777
                validation = true;
 
778
 
 
779
                element.value = "foo";
 
780
                validation &= !element.validity.valid
 
781
 
 
782
                element.value = "foo@bar.org";
 
783
                validation &= element.validity.valid
 
784
            }
 
785
 
 
786
            results.addItem({
 
787
                key: 'form.email.element',
 
788
                passed: element.type == 'email'
 
789
            });
 
790
 
 
791
            results.addItem({
 
792
                key: 'form.email.validation',
 
793
                passed: validation
 
794
            });
 
795
        },
 
796
 
 
797
 
 
798
        /* input type=date, month, week, time, datetime and datetime-local */
 
799
 
 
800
        function (results) {
 
801
            var types = ['date', 'month', 'week', 'time', 'datetime', 'datetime-local'];
 
802
            for (var t = 0; t < types.length; t++) {
 
803
                var element = createInput(types[t]);
 
804
 
 
805
                element.value = "foobar";
 
806
                var sanitization = element.value == '';
 
807
 
 
808
                var minimal = element.type == types[t];
 
809
 
 
810
                results.addItem({
 
811
                    key: 'form.' + types[t] + '.element',
 
812
                    passed: minimal
 
813
                });
 
814
 
 
815
                results.addItem({
 
816
                    key: 'form.' + types[t] + '.ui',
 
817
                    passed: minimal && sanitization     // Testing UI reliably is not possible, so we assume if sanitization is support we also have a UI and use the blacklist to make corrections
 
818
                });
 
819
 
 
820
                results.addItem({
 
821
                    key: 'form.' + types[t] + '.sanitization',
 
822
                    passed: minimal && sanitization
 
823
                });
 
824
 
 
825
                results.addItem({
 
826
                    key: 'form.' + types[t] + '.min',
 
827
                    passed: minimal && 'min' in element
 
828
                });
 
829
 
 
830
                results.addItem({
 
831
                    key: 'form.' + types[t] + '.max',
 
832
                    passed: minimal && 'max' in element
 
833
                });
 
834
 
 
835
                results.addItem({
 
836
                    key: 'form.' + types[t] + '.step',
 
837
                    passed: minimal && 'step' in element
 
838
                });
 
839
 
 
840
                results.addItem({
 
841
                    key: 'form.' + types[t] + '.stepDown',
 
842
                    passed: minimal && 'stepDown' in element
 
843
                });
 
844
 
 
845
                results.addItem({
 
846
                    key: 'form.' + types[t] + '.stepUp',
 
847
                    passed: minimal && 'stepUp' in element
 
848
                });
 
849
 
 
850
                if (types[t] != 'datetime-local' && types[t] != 'datetime') {
 
851
                    results.addItem({
 
852
                        key: 'form.' + types[t] + '.valueAsDate',
 
853
                        passed: minimal && 'valueAsDate' in element
 
854
                    });
 
855
                }
 
856
 
 
857
                results.addItem({
 
858
                    key: 'form.' + types[t] + '.valueAsNumber',
 
859
                    passed: minimal && 'valueAsNumber' in element
 
860
                });
 
861
            }
 
862
        },
 
863
 
 
864
 
 
865
        /* input type=number, range */
 
866
 
 
867
        function (results) {
 
868
            var types = ['number', 'range'];
 
869
            for (var t = 0; t < types.length; t++) {
 
870
                var element = createInput(types[t]);
 
871
 
 
872
                element.value = "foobar";
 
873
                var sanitization = element.value != 'foobar';
 
874
 
 
875
                var validation = false;
 
876
                if ('validity' in element) {
 
877
                    validation = true;
 
878
 
 
879
                    element.min = 40;
 
880
                    element.max = 50;
 
881
                    element.value = 100;
 
882
                    validation &= !element.validity.valid
 
883
 
 
884
                    element.value = 42;
 
885
                    validation &= element.validity.valid
 
886
                }
 
887
 
 
888
                var minimal = element.type == types[t];
 
889
 
 
890
                results.addItem({
 
891
                    key: 'form.' + types[t] + '.element',
 
892
                    passed: minimal
 
893
                });
 
894
 
 
895
                results.addItem({
 
896
                    key: 'form.' + types[t] + '.ui',
 
897
                    passed: minimal && sanitization             // Testing UI reliably is not possible, so we assume if sanitization is support we also have a UI and use the blacklist to make corrections
 
898
                });
 
899
 
 
900
                results.addItem({
 
901
                    key: 'form.' + types[t] + '.sanitization',
 
902
                    passed: minimal && sanitization
 
903
                });
 
904
 
 
905
                if (types[t] != 'range') {
 
906
                    results.addItem({
 
907
                        key: 'form.' + types[t] + '.validation',
 
908
                        passed: minimal && validation
 
909
                    });
 
910
                }
 
911
 
 
912
                results.addItem({
 
913
                    key: 'form.' + types[t] + '.min',
 
914
                    passed: minimal && 'min' in element
 
915
                });
 
916
 
 
917
                results.addItem({
 
918
                    key: 'form.' + types[t] + '.max',
 
919
                    passed: minimal && 'max' in element
 
920
                });
 
921
 
 
922
                results.addItem({
 
923
                    key: 'form.' + types[t] + '.step',
 
924
                    passed: minimal && 'step' in element
 
925
                });
 
926
 
 
927
                results.addItem({
 
928
                    key: 'form.' + types[t] + '.stepDown',
 
929
                    passed: minimal && 'stepDown' in element
 
930
                });
 
931
 
 
932
                results.addItem({
 
933
                    key: 'form.' + types[t] + '.stepUp',
 
934
                    passed: minimal && 'stepUp' in element
 
935
                });
 
936
 
 
937
                results.addItem({
 
938
                    key: 'form.' + types[t] + '.valueAsNumber',
 
939
                    passed: minimal && 'valueAsNumber' in element
 
940
                });
 
941
            }
 
942
        },
 
943
 
 
944
 
 
945
        /* input type=color */
 
946
 
 
947
        function (results) {
 
948
            var element = createInput('color');
 
949
 
 
950
            element.value = "foobar";
 
951
            var sanitization = element.value != 'foobar';
 
952
 
 
953
            results.addItem({
 
954
                key: 'form.color.element',
 
955
                passed: element.type == 'color'
 
956
            });
 
957
 
 
958
            results.addItem({
 
959
                key: 'form.color.ui',
 
960
                passed: sanitization            // Testing UI reliably is not possible, so we assume if sanitization is support we also have a UI and use the blacklist to make corrections
 
961
            });
 
962
 
 
963
            results.addItem({
 
964
                key: 'form.color.sanitization',
 
965
                passed: sanitization
 
966
            });
 
967
        },
 
968
 
 
969
 
 
970
        /* input type=checkbox */
 
971
 
 
972
        function (results) {
 
973
            var element = createInput('checkbox');
 
974
 
 
975
            results.addItem({
 
976
                key: 'form.checkbox.element',
 
977
                passed: element.type == 'checkbox'
 
978
            });
 
979
 
 
980
            results.addItem({
 
981
                key: 'form.checkbox.indeterminate',
 
982
                passed: 'indeterminate' in element
 
983
            });
 
984
        },
 
985
 
 
986
 
 
987
        /* input type=image */
 
988
 
 
989
        function (results) {
 
990
            var element = createInput('image');
 
991
            element.style.display = 'inline-block';
 
992
            document.body.appendChild(element);
 
993
 
 
994
            var supportsWidth = 'width' in element;
 
995
            var supportsHeight = 'height' in element;
 
996
 
 
997
            element.setAttribute('width', '100');
 
998
            element.setAttribute('height', '100');
 
999
 
 
1000
            results.addItem({
 
1001
                key: 'form.image.element',
 
1002
                passed: element.type == 'image'
 
1003
            });
 
1004
 
 
1005
            results.addItem({
 
1006
                key: 'form.image.width',
 
1007
                passed: supportsWidth && element.offsetWidth == 100
 
1008
            });
 
1009
 
 
1010
            results.addItem({
 
1011
                key: 'form.image.height',
 
1012
                passed: supportsHeight && element.offsetHeight == 100
 
1013
            });
 
1014
 
 
1015
            document.body.removeChild(element);
 
1016
        },
 
1017
 
 
1018
 
 
1019
        /* input type=file */
 
1020
 
 
1021
        function (results) {
 
1022
            var element = createInput('file');
 
1023
 
 
1024
            results.addItem({
 
1025
                key: 'form.file.element',
 
1026
                passed: element.type == 'file'
 
1027
            });
 
1028
 
 
1029
            results.addItem({
 
1030
                key: 'form.file.files',
 
1031
                passed: element.files && element.files instanceof FileList
 
1032
            });
 
1033
 
 
1034
            results.addItem({
 
1035
                key: 'form.file.directory',
 
1036
                passed: 'directory' in element && window.Directory
 
1037
            });
 
1038
        },
 
1039
 
 
1040
 
 
1041
        /* textarea */
 
1042
 
 
1043
        function (results) {
 
1044
            var element = document.createElement('textarea');
 
1045
 
 
1046
            var passed = false;
 
1047
            try {
 
1048
                passed = typeof HTMLTextAreaElement != 'undefined' && element instanceof HTMLTextAreaElement;
 
1049
            } catch (error) {
 
1050
            }
 
1051
 
 
1052
            results.addItem({
 
1053
                key: 'form.textarea.element',
 
1054
                passed: passed
 
1055
            });
 
1056
 
 
1057
            results.addItem({
 
1058
                key: 'form.textarea.maxlength',
 
1059
                passed: 'maxLength' in element
 
1060
            });
 
1061
 
 
1062
            results.addItem({
 
1063
                key: 'form.textarea.wrap',
 
1064
                passed: 'wrap' in element
 
1065
            });
 
1066
        },
 
1067
 
 
1068
 
 
1069
        /* select */
 
1070
 
 
1071
        function (results) {
 
1072
            var element = document.createElement('select');
 
1073
 
 
1074
            var passed = false;
 
1075
            try {
 
1076
                passed = typeof HTMLSelectElement != 'undefined' && element instanceof HTMLSelectElement;
 
1077
            } catch (error) {
 
1078
            }
 
1079
 
 
1080
            results.addItem({
 
1081
                key: 'form.select.element',
 
1082
                passed: passed
 
1083
            });
 
1084
 
 
1085
            results.addItem({
 
1086
                key: 'form.select.required',
 
1087
                passed: 'required' in element
 
1088
            });
 
1089
        },
 
1090
 
 
1091
 
 
1092
        /* fieldset */
 
1093
 
 
1094
        function (results) {
 
1095
            var element = document.createElement('fieldset');
 
1096
 
 
1097
            var passed = false;
 
1098
            try {
 
1099
                passed = typeof HTMLFieldSetElement != 'undefined' && element instanceof HTMLFieldSetElement;
 
1100
            } catch (error) {
 
1101
            }
 
1102
 
 
1103
            results.addItem({
 
1104
                key: 'form.fieldset.element',
 
1105
                passed: passed
 
1106
            });
 
1107
 
 
1108
            results.addItem({
 
1109
                key: 'form.fieldset.elements',
 
1110
                passed: 'elements' in element
 
1111
            });
 
1112
 
 
1113
            results.addItem({
 
1114
                key: 'form.fieldset.disabled',
 
1115
                passed: 'disabled' in element
 
1116
            });
 
1117
        },
 
1118
 
 
1119
 
 
1120
        /* datalist */
 
1121
 
 
1122
        function (results) {
 
1123
            var passed = false;
 
1124
 
 
1125
            try {
 
1126
                var element = document.createElement('datalist');
 
1127
 
 
1128
                try {
 
1129
                    passed = (typeof HTMLDataListElement != 'undefined' && element instanceof HTMLDataListElement) || element.childNodes.length;
 
1130
                } catch (error) {
 
1131
                }
 
1132
            } catch (error) {
 
1133
            }
 
1134
 
 
1135
            results.addItem({
 
1136
                key: 'form.datalist.element',
 
1137
                passed: passed
 
1138
            });
 
1139
 
 
1140
            var element = document.createElement('input');
 
1141
 
 
1142
            results.addItem({
 
1143
                key: 'form.datalist.list',
 
1144
                passed: !!("list" in element)
 
1145
            });
 
1146
        },
 
1147
 
 
1148
 
 
1149
        /* keygen */
 
1150
 
 
1151
        function (results) {
 
1152
            var element = document.createElement('div');
 
1153
            element.innerHTML = '<keygen>';
 
1154
 
 
1155
            var passed = false;
 
1156
            try {
 
1157
                passed = typeof HTMLKeygenElement != 'undefined' && element.firstChild instanceof HTMLKeygenElement && 'challenge' in element.firstChild && 'keytype' in element.firstChild;
 
1158
            } catch (error) {
 
1159
            }
 
1160
 
 
1161
            results.addItem({
 
1162
                key: 'form.keygen.element',
 
1163
                passed: passed
 
1164
            });
 
1165
 
 
1166
            results.addItem({
 
1167
                key: 'form.keygen.challenge',
 
1168
                passed: element.firstChild && 'challenge' in element.firstChild
 
1169
            });
 
1170
 
 
1171
            results.addItem({
 
1172
                key: 'form.keygen.keytype',
 
1173
                passed: element.firstChild && 'keytype' in element.firstChild
 
1174
            });
 
1175
        },
 
1176
 
 
1177
 
 
1178
        /* output */
 
1179
 
 
1180
        function (results) {
 
1181
            var passed = false;
 
1182
 
 
1183
            try {
 
1184
                var element = document.createElement('output');
 
1185
 
 
1186
                try {
 
1187
                    passed = typeof HTMLOutputElement != 'undefined' && element instanceof HTMLOutputElement;
 
1188
                } catch (error) {
 
1189
                }
 
1190
            } catch (error) {
 
1191
            }
 
1192
 
 
1193
            results.addItem({
 
1194
                key: 'form.output.element',
 
1195
                passed: passed
 
1196
            });
 
1197
        },
 
1198
 
 
1199
 
 
1200
        /* progress */
 
1201
 
 
1202
        function (results) {
 
1203
            var passed = false;
 
1204
 
 
1205
            try {
 
1206
                var element = document.createElement('progress');
 
1207
 
 
1208
                try {
 
1209
                    passed = typeof HTMLProgressElement != 'undefined' && element instanceof HTMLProgressElement;
 
1210
                } catch (error) {
 
1211
                }
 
1212
            } catch (error) {
 
1213
            }
 
1214
 
 
1215
            results.addItem({
 
1216
                key: 'form.progress.element',
 
1217
                passed: passed
 
1218
            });
 
1219
        },
 
1220
 
 
1221
 
 
1222
        /* meter */
 
1223
 
 
1224
        function (results) {
 
1225
            var passed = false;
 
1226
 
 
1227
            try {
 
1228
                var element = document.createElement('meter');
 
1229
 
 
1230
                try {
 
1231
                    passed = typeof HTMLMeterElement != 'undefined' && element instanceof HTMLMeterElement;
 
1232
                } catch (error) {
 
1233
                }
 
1234
            } catch (error) {
 
1235
            }
 
1236
 
 
1237
            results.addItem({
 
1238
                key: 'form.meter.element',
 
1239
                passed: passed
 
1240
            });
 
1241
        },
 
1242
 
 
1243
 
 
1244
        /* pattern and required properties */
 
1245
 
 
1246
        function (results) {
 
1247
            var element = document.createElement('input');
 
1248
 
 
1249
            var props = 'pattern required'.split(' ');
 
1250
 
 
1251
            for (var p = 0; p < props.length; p++) {
 
1252
                results.addItem({
 
1253
                    key: 'form.validation.' + props[p],
 
1254
                    passed: !!(props[p] in element)
 
1255
                });
 
1256
            }
 
1257
        },
 
1258
 
 
1259
 
 
1260
        /* control property on labels */
 
1261
 
 
1262
        function (results) {
 
1263
            var field = document.createElement('input');
 
1264
            field.id = "a";
 
1265
            document.body.appendChild(field);
 
1266
 
 
1267
            var label = document.createElement("label");
 
1268
            label.setAttribute('for', 'a');
 
1269
            document.body.appendChild(label);
 
1270
 
 
1271
            results.addItem({
 
1272
                key: 'form.association.control',
 
1273
                passed: label.control == field
 
1274
            });
 
1275
 
 
1276
            document.body.removeChild(field);
 
1277
            document.body.removeChild(label);
 
1278
        },
 
1279
 
 
1280
 
 
1281
        /* form attribute on input */
 
1282
 
 
1283
        function (results) {
 
1284
            var element = document.createElement('div');
 
1285
            document.body.appendChild(element);
 
1286
            element.innerHTML = '<form id="form"></form><input form="form">';
 
1287
 
 
1288
            results.addItem({
 
1289
                key: 'form.association.form',
 
1290
                passed: element.lastChild.form == element.firstChild
 
1291
            });
 
1292
 
 
1293
            document.body.removeChild(element);
 
1294
        },
 
1295
 
 
1296
 
 
1297
        /* formAction, formEnctype, formMethod, formNoValidate and formTarget properties */
 
1298
 
 
1299
        function (results) {
 
1300
            var props = 'formAction formEnctype formMethod formNoValidate formTarget'.split(' ');
 
1301
 
 
1302
            var element = document.createElement('input');
 
1303
 
 
1304
            for (var p = 0; p < props.length; p++) {
 
1305
                results.addItem({
 
1306
                    key: 'form.association.' + props[p],
 
1307
                    passed: !!(props[p] in element)
 
1308
                });
 
1309
            }
 
1310
        },
 
1311
 
 
1312
 
 
1313
        /* labels property on input */
 
1314
 
 
1315
        function (results) {
 
1316
            var element = document.createElement('input');
 
1317
            document.body.appendChild(element);
 
1318
            element.id = "testFormInput";
 
1319
 
 
1320
            var label = document.createElement("label");
 
1321
            label.setAttribute('for', 'testFormInput');
 
1322
            document.body.appendChild(label);
 
1323
 
 
1324
            results.addItem({
 
1325
                key: 'form.association.labels',
 
1326
                passed: (!!element.labels && element.labels.length == 1 && element.labels[0] == label)
 
1327
            });
 
1328
 
 
1329
            document.body.removeChild(label);
 
1330
            document.body.removeChild(element);
 
1331
        },
 
1332
 
 
1333
 
 
1334
        /* autofocus */
 
1335
 
 
1336
        function (results) {
 
1337
            var element = document.createElement('input');
 
1338
 
 
1339
            results.addItem({
 
1340
                key: 'form.other.autofocus',
 
1341
                passed: !!('autofocus' in element)
 
1342
            });
 
1343
        },
 
1344
 
 
1345
 
 
1346
        /* autocomplete, placeholder, multiple and dirName properties */
 
1347
 
 
1348
        function (results) {
 
1349
            var element = document.createElement('input');
 
1350
 
 
1351
            var props = 'autocomplete placeholder multiple dirName'.split(' ');
 
1352
 
 
1353
            for (var p = 0; p < props.length; p++) {
 
1354
                var prop = props[p].toLowerCase();
 
1355
                results.addItem({
 
1356
                    key: 'form.other.' + prop,
 
1357
                    passed: !!(props[p] in element)
 
1358
                });
 
1359
            }
 
1360
        },
 
1361
 
 
1362
 
 
1363
        /* valid, invalid, optional, required, in-range, out-of-range, read-write and read-only css selectors */
 
1364
 
 
1365
        function (results) {
 
1366
            var selectors = "valid invalid optional required in-range out-of-range read-write read-only".split(" ");
 
1367
            var passed = [NO | UNKNOWN, NO | UNKNOWN, NO | UNKNOWN, NO | UNKNOWN, NO | UNKNOWN, NO | UNKNOWN, NO | UNKNOWN, NO | UNKNOWN];
 
1368
 
 
1369
                        /*  At this time we are not testing enabled, disabled, checked and indeterminate,
 
1370
                                because these selectors are part of the CSS 3 Selector specification and
 
1371
                                universally implemented, see http://www.css3.info/selectors-test/
 
1372
                        */
 
1373
 
 
1374
            if ('querySelector' in document) {
 
1375
                var element = document.createElement('input');
 
1376
                element.id = 'testFormInput';
 
1377
                element.setAttribute("type", "text");
 
1378
                document.body.appendChild(element);
 
1379
 
 
1380
                try {
 
1381
                    passed[0] = !!document.querySelector("#testFormInput:valid");
 
1382
                } catch (e) {
 
1383
                    passed[0] = NO;
 
1384
                }
 
1385
 
 
1386
                try {
 
1387
                    passed[6] = !!document.querySelector("#testFormInput:read-write");
 
1388
                } catch (e) {
 
1389
                    passed[6] = NO;
 
1390
 
 
1391
                    try {
 
1392
                        passed[6] = document.querySelector("#testFormInput:-moz-read-write") ? YES | PREFIX : NO;
 
1393
                    } catch (e) {
 
1394
                    }
 
1395
                }
 
1396
 
 
1397
                if ("validity" in element && "setCustomValidity" in element) {
 
1398
                    element.setCustomValidity("foo");
 
1399
 
 
1400
                    try {
 
1401
                        passed[1] = !!document.querySelector("#testFormInput:invalid");
 
1402
                    } catch (e) {
 
1403
                        passed[1] = NO;
 
1404
                    }
 
1405
                } else {
 
1406
                    passed[1] = NO;
 
1407
                }
 
1408
 
 
1409
                try {
 
1410
                    passed[2] = !!document.querySelector("#testFormInput:optional");
 
1411
                } catch (e) {
 
1412
                    passed[2] = NO;
 
1413
                }
 
1414
 
 
1415
                element.setAttribute("required", "true");
 
1416
 
 
1417
                try {
 
1418
                    passed[3] = !!document.querySelector("#testFormInput:required");
 
1419
                } catch (e) {
 
1420
                    passed[3] = NO;
 
1421
                }
 
1422
 
 
1423
                try {
 
1424
                    element.setAttribute("type", "number");
 
1425
                    element.setAttribute("min", "10");
 
1426
                    element.setAttribute("max", "20");
 
1427
                    element.setAttribute("value", "15");
 
1428
                    passed[4] = !!document.querySelector("#testFormInput:in-range");
 
1429
                } catch (e) {
 
1430
                    passed[4] = NO;
 
1431
                }
 
1432
 
 
1433
 
 
1434
                try {
 
1435
                    element.setAttribute("type", "number");
 
1436
                    element.setAttribute("min", "10");
 
1437
                    element.setAttribute("max", "20");
 
1438
                    element.setAttribute("value", "25");
 
1439
                    passed[5] = !!document.querySelector("#testFormInput:out-of-range");
 
1440
                } catch (e) {
 
1441
                    passed[5] = NO;
 
1442
                }
 
1443
 
 
1444
                document.body.removeChild(element);
 
1445
 
 
1446
                var element = document.createElement('input');
 
1447
                element.id = 'testFormInput';
 
1448
                element.setAttribute("type", "text");
 
1449
                element.setAttribute("readonly", "readonly");
 
1450
                document.body.appendChild(element);
 
1451
 
 
1452
                try {
 
1453
                    passed[7] = !!document.querySelector("#testFormInput:read-only");
 
1454
                } catch (e) {
 
1455
                    passed[7] = NO;
 
1456
 
 
1457
                    try {
 
1458
                        passed[7] = document.querySelector("#testFormInput:-moz-read-only") ? YES | PREFIX : NO;
 
1459
                    } catch (e) {
 
1460
                    }
 
1461
                }
 
1462
 
 
1463
                document.body.removeChild(element);
 
1464
            }
 
1465
 
 
1466
            for (var i = 0; i < selectors.length; i++) {
 
1467
                results.addItem({
 
1468
                    key: 'form.selectors.' + selectors[i],
 
1469
                    passed: passed[i]
 
1470
                });
 
1471
            }
 
1472
        },
 
1473
 
 
1474
 
 
1475
        /* oninput, onchange and oninvalid events */
 
1476
 
 
1477
        function (results) {
 
1478
            var inputItem = results.addItem({
 
1479
                key: 'form.events.oninput',
 
1480
                passed: isEventSupported('input')
 
1481
            });
 
1482
 
 
1483
            var changeItem = results.addItem({
 
1484
                key: 'form.events.onchange',
 
1485
                passed: isEventSupported('change')
 
1486
            });
 
1487
 
 
1488
            var invalidItem = results.addItem({
 
1489
                key: 'form.events.oninvalid',
 
1490
                passed: isEventSupported('invalid')
 
1491
            });
 
1492
 
 
1493
            try {
 
1494
                inputItem.startBackground();
 
1495
                changeItem.startBackground();
 
1496
 
 
1497
                var event = document.createEvent("KeyboardEvent");
 
1498
                if (event.initKeyEvent) {
 
1499
                    event.initKeyEvent("keypress", false, true, null, false, false, false, false, null, 65);
 
1500
 
 
1501
                    var input = document.createElement('input');
 
1502
                    input.style.position = 'fixed';
 
1503
                    input.style.left = '-500px';
 
1504
                    input.style.top = '0px';
 
1505
 
 
1506
                    document.body.appendChild(input);
 
1507
                    input.addEventListener('input', function () {
 
1508
                        inputItem.update({
 
1509
                            'passed': true
 
1510
                        });
 
1511
 
 
1512
                        inputItem.stopBackground();
 
1513
                    }, true);
 
1514
 
 
1515
                    input.addEventListener('change', function () {
 
1516
                        changeItem.update({
 
1517
                            'passed': true
 
1518
                        });
 
1519
 
 
1520
                        changeItem.stopBackground();
 
1521
                    }, true);
 
1522
 
 
1523
                    input.focus();
 
1524
                    input.dispatchEvent(event);
 
1525
                    input.blur();
 
1526
 
 
1527
                    window.setTimeout(function () {
 
1528
                        document.body.removeChild(input);
 
1529
 
 
1530
                        inputItem.stopBackground();
 
1531
                        changeItem.stopBackground();
 
1532
                    }, 1000);
 
1533
                } else {
 
1534
                    inputItem.stopBackground();
 
1535
                    changeItem.stopBackground();
 
1536
                }
 
1537
            } catch (e) {
 
1538
                inputItem.stopBackground();
 
1539
                changeItem.stopBackground();
 
1540
            }
 
1541
        },
 
1542
 
 
1543
 
 
1544
        /* checkValidity property */
 
1545
 
 
1546
        function (results) {
 
1547
            results.addItem({
 
1548
                key: 'form.formvalidation.checkValidity',
 
1549
                passed: 'checkValidity' in document.createElement('form')
 
1550
            });
 
1551
        },
 
1552
 
 
1553
 
 
1554
        /* noValidate property */
 
1555
 
 
1556
        function (results) {
 
1557
            results.addItem({
 
1558
                key: 'form.formvalidation.noValidate',
 
1559
                passed: 'noValidate' in document.createElement('form')
 
1560
            });
 
1561
        },
 
1562
 
 
1563
 
 
1564
        /* microdata */
 
1565
 
 
1566
        function (results) {
 
1567
            var container = document.createElement('div');
 
1568
            container.innerHTML = '<div id="microdataItem" itemscope itemtype="http://example.net/user"><p>My name is <span id="microdataProperty" itemprop="name">Elizabeth</span>.</p></div>';
 
1569
            document.body.appendChild(container);
 
1570
 
 
1571
            var item = document.getElementById('microdataItem');
 
1572
            var property = document.getElementById('microdataProperty');
 
1573
            var passed = true;
 
1574
 
 
1575
            // Check the element that contains the property
 
1576
            passed = passed && !!('itemValue' in property) && property.itemValue == 'Elizabeth';
 
1577
 
 
1578
            // Check the element that is the item
 
1579
            passed = passed && !!('properties' in item) && item.properties['name'][0].itemValue == 'Elizabeth';
 
1580
 
 
1581
            // Check the getItems method
 
1582
            if (!!document.getItems) {
 
1583
                var user = document.getItems('http://example.net/user')[0];
 
1584
                passed = passed && user.properties['name'][0].itemValue == 'Elizabeth';
 
1585
            }
 
1586
 
 
1587
            document.body.removeChild(container);
 
1588
 
 
1589
            results.addItem({
 
1590
                key: 'microdata',
 
1591
                passed: passed
 
1592
            });
 
1593
        },
 
1594
 
 
1595
 
 
1596
        /* geolocation */
 
1597
 
 
1598
        function (results) {
 
1599
            results.addItem({
 
1600
                key: 'location.geolocation',
 
1601
                passed: !!navigator.geolocation
 
1602
            });
 
1603
        },
 
1604
 
 
1605
 
 
1606
        /* device orientation */
 
1607
 
 
1608
        function (results) {
 
1609
            results.addItem({
 
1610
                key: 'location.orientation',
 
1611
                passed: !!window.DeviceOrientationEvent
 
1612
            });
 
1613
        },
 
1614
 
 
1615
 
 
1616
        /* device motion */
 
1617
 
 
1618
        function (results) {
 
1619
            results.addItem({
 
1620
                key: 'location.motion',
 
1621
                passed: !!window.DeviceMotionEvent
 
1622
            });
 
1623
        },
 
1624
 
 
1625
 
 
1626
        /* fullscreen */
 
1627
 
 
1628
        function (results) {
 
1629
            results.addItem({
 
1630
                key: 'output.requestFullScreen',
 
1631
                passed: !!document.documentElement.requestFullscreen ? YES : !!document.documentElement.webkitRequestFullScreen || !!document.documentElement.mozRequestFullScreen || !!document.documentElement.msRequestFullscreen ? YES | PREFIX : NO
 
1632
            });
 
1633
        },
 
1634
 
 
1635
 
 
1636
        /* notifications */
 
1637
 
 
1638
        function (results) {
 
1639
            results.addItem({
 
1640
                key: 'output.notifications',
 
1641
                passed: 'Notification' in window ? YES : 'webkitNotifications' in window || 'mozNotification' in window.navigator || 'oNotification' in window || 'msNotification' in window ? YES | PREFIX : NO
 
1642
            });
 
1643
        },
 
1644
 
 
1645
 
 
1646
        /* getUserMedia */
 
1647
 
 
1648
        function (results) {
 
1649
            results.addItem({
 
1650
                key: 'media.getUserMedia',
 
1651
                passed: !!navigator.mediaDevices && !!navigator.mediaDevices.getUserMedia ? YES : !!navigator.getUserMedia ? YES | OLD : !!navigator.webkitGetUserMedia || !!navigator.mozGetUserMedia || !!navigator.msGetUserMedia || !!navigator.oGetUserMedia ? YES | PREFIX : NO
 
1652
            });
 
1653
        },
 
1654
 
 
1655
 
 
1656
        /* getDisplayMedia */
 
1657
 
 
1658
        function (results) {
 
1659
            results.addItem({
 
1660
                key: 'media.getDisplayMedia',
 
1661
                passed: !!navigator.mediaDevices && !!navigator.mediaDevices.getDisplayMedia ? YES : NO
 
1662
            });
 
1663
        },
 
1664
 
 
1665
 
 
1666
        /* enumerateDevices */
 
1667
 
 
1668
        function (results) {
 
1669
            results.addItem({
 
1670
                key: 'media.enumerateDevices',
 
1671
                passed: !!navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices ? YES : NO
 
1672
            });
 
1673
        },
 
1674
 
 
1675
 
 
1676
        /* getGamepads */
 
1677
 
 
1678
        function (results) {
 
1679
            results.addItem({
 
1680
                key: 'input.getGamepads',
 
1681
                passed: !!navigator.getGamepads ? YES : !!navigator.webkitGetGamepads || !!navigator.mozGetGamepads || !!navigator.msGetGamepads || !!navigator.oGetGamepads ? YES | PREFIX : NO
 
1682
            });
 
1683
        },
 
1684
 
 
1685
 
 
1686
        /* pointerLock */
 
1687
 
 
1688
        function (results) {
 
1689
            results.addItem({
 
1690
                key: 'input.pointerLock',
 
1691
                passed: 'pointerLockElement' in document ? YES : 'oPointerLockElement' in document || 'msPointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document ? YES | PREFIX : NO
 
1692
            });
 
1693
        },
 
1694
 
 
1695
 
 
1696
        /* pointerevents */
 
1697
 
 
1698
        function (results) {
 
1699
            results.addItem({
 
1700
                key: 'input.pointerevents',
 
1701
                passed: !!window.PointerEvent ? YES : !!window.webkitPointerEvent || !!window.mozPointerEvent || !!window.msPointerEvent || !!window.oPointerEvent ? YES | PREFIX : NO
 
1702
            });
 
1703
        },
 
1704
 
 
1705
 
 
1706
        /* beacon */
 
1707
 
 
1708
        function (results) {
 
1709
            results.addItem({
 
1710
                key: 'communication.beacon',
 
1711
                passed: 'sendBeacon' in navigator
 
1712
            });
 
1713
        },
 
1714
 
 
1715
 
 
1716
        /* eventSource */
 
1717
 
 
1718
        function (results) {
 
1719
            results.addItem({
 
1720
                key: 'communication.eventSource',
 
1721
                passed: 'EventSource' in window
 
1722
            });
 
1723
        },
 
1724
 
 
1725
 
 
1726
        /* fetch */
 
1727
 
 
1728
        function (results) {
 
1729
            results.addItem({
 
1730
                key: 'communication.fetch',
 
1731
                passed: 'Promise' in window && typeof window.fetch === 'function' && window.fetch('') instanceof Promise
 
1732
            });
 
1733
        },
 
1734
 
 
1735
 
 
1736
        /* xmlhttprequest upload */
 
1737
 
 
1738
        function (results) {
 
1739
            results.addItem({
 
1740
                key: 'communication.xmlhttprequest2.upload',
 
1741
                passed: window.XMLHttpRequest && 'upload' in new XMLHttpRequest()
 
1742
            });
 
1743
        },
 
1744
 
 
1745
 
 
1746
        /* xmlhttprequest response text */
 
1747
 
 
1748
        function (results) {
 
1749
            var item = results.addItem({
 
1750
                key: 'communication.xmlhttprequest2.response.text',
 
1751
                passed: false
 
1752
            });
 
1753
 
 
1754
            if (!window.XMLHttpRequest) return;
 
1755
 
 
1756
            var xhr = new window.XMLHttpRequest();
 
1757
 
 
1758
            if (typeof xhr.responseType == 'undefined') return;
 
1759
 
 
1760
            var done = false;
 
1761
 
 
1762
            xhr.onreadystatechange = function () {
 
1763
                if (this.readyState == 4 && !done) {
 
1764
                    done = true;
 
1765
                    passed = false;
 
1766
 
 
1767
                    try {
 
1768
                        passed = !!(this.responseText); // && this.responseText == '<title>&amp;&<</title>');
 
1769
                    } catch (e) {
 
1770
                    }
 
1771
 
 
1772
                    item.stopBackground();
 
1773
                    item.update({
 
1774
                        'passed': passed
 
1775
                    });
 
1776
                }
 
1777
            }
 
1778
 
 
1779
            try {
 
1780
                item.startBackground();
 
1781
                xhr.open("GET", "/assets/detect.html?" + Math.random().toString(36).substr(2, 5));
 
1782
                xhr.responseType = "text";
 
1783
                xhr.send();
 
1784
            } catch (e) {
 
1785
                item.stopBackground();
 
1786
            }
 
1787
        },
 
1788
 
 
1789
 
 
1790
        /* xmlhttprequest response document */
 
1791
 
 
1792
        function (results) {
 
1793
            var item = results.addItem({
 
1794
                key: 'communication.xmlhttprequest2.response.document',
 
1795
                passed: false
 
1796
            });
 
1797
 
 
1798
            if (!window.XMLHttpRequest) return;
 
1799
 
 
1800
            var xhr = new window.XMLHttpRequest();
 
1801
 
 
1802
            if (typeof xhr.responseType == 'undefined') return;
 
1803
 
 
1804
            var done = false;
 
1805
 
 
1806
            xhr.onreadystatechange = function () {
 
1807
                if (this.readyState == 4 && !done) {
 
1808
                    done = true;
 
1809
                    passed = false;
 
1810
 
 
1811
                    try {
 
1812
                        passed = !!(this.responseXML && this.responseXML.title && this.responseXML.title == "&&<");
 
1813
                    } catch (e) {
 
1814
                    }
 
1815
 
 
1816
                    item.stopBackground();
 
1817
                    item.update({
 
1818
                        'passed': passed
 
1819
                    });
 
1820
                }
 
1821
            }
 
1822
 
 
1823
            try {
 
1824
                item.startBackground();
 
1825
                xhr.open("GET", "/assets/detect.html?" + Math.random().toString(36).substr(2, 5));
 
1826
                xhr.responseType = "document";
 
1827
                xhr.send();
 
1828
            } catch (e) {
 
1829
                item.stopBackground();
 
1830
            }
 
1831
        },
 
1832
 
 
1833
 
 
1834
        /* xmlhttprequest response array */
 
1835
 
 
1836
        function (results) {
 
1837
            var item = results.addItem({
 
1838
                key: 'communication.xmlhttprequest2.response.array',
 
1839
                passed: false
 
1840
            });
 
1841
 
 
1842
            if (!window.XMLHttpRequest || !window.ArrayBuffer) return;
 
1843
 
 
1844
            var xhr = new window.XMLHttpRequest();
 
1845
 
 
1846
            if (typeof xhr.responseType == 'undefined') return;
 
1847
 
 
1848
            var done = false;
 
1849
 
 
1850
            xhr.onreadystatechange = function () {
 
1851
                if (this.readyState == 4 && !done) {
 
1852
                    done = true;
 
1853
                    passed = false;
 
1854
 
 
1855
                    try {
 
1856
                        passed = !!(this.response && this.response instanceof ArrayBuffer);
 
1857
                    } catch (e) {
 
1858
                    }
 
1859
 
 
1860
                    item.stopBackground();
 
1861
                    item.update({
 
1862
                        'passed': passed
 
1863
                    });
 
1864
                }
 
1865
            }
 
1866
 
 
1867
            try {
 
1868
                item.startBackground();
 
1869
                xhr.open("GET", "/assets/detect.html?" + Math.random().toString(36).substr(2, 5));
 
1870
                xhr.responseType = "arraybuffer";
 
1871
                xhr.send();
 
1872
            } catch (e) {
 
1873
                item.stopBackground();
 
1874
            }
 
1875
        },
 
1876
 
 
1877
 
 
1878
        /* xmlhttprequest response blob */
 
1879
 
 
1880
        function (results) {
 
1881
            var item = results.addItem({
 
1882
                key: 'communication.xmlhttprequest2.response.blob',
 
1883
                passed: false
 
1884
            });
 
1885
 
 
1886
            if (!window.XMLHttpRequest || !window.Blob) return;
 
1887
 
 
1888
            var xhr = new window.XMLHttpRequest();
 
1889
 
 
1890
            if (typeof xhr.responseType == 'undefined') return;
 
1891
 
 
1892
            var done = false;
 
1893
 
 
1894
            xhr.onreadystatechange = function () {
 
1895
                if (this.readyState == 4 && !done) {
 
1896
                    done = true;
 
1897
                    passed = false;
 
1898
 
 
1899
                    try {
 
1900
                        passed = !!(this.response && this.response instanceof Blob);
 
1901
                    } catch (e) {
 
1902
                    }
 
1903
 
 
1904
                    item.stopBackground();
 
1905
                    item.update({
 
1906
                        'passed': passed
 
1907
                    });
 
1908
                }
 
1909
            }
 
1910
 
 
1911
            try {
 
1912
                item.startBackground();
 
1913
                xhr.open("GET", "/assets/detect.html?" + Math.random().toString(36).substr(2, 5));
 
1914
                xhr.responseType = "blob";
 
1915
                xhr.send();
 
1916
            } catch (e) {
 
1917
                item.stopBackground();
 
1918
            }
 
1919
        },
 
1920
 
 
1921
 
 
1922
        /* websockets */
 
1923
 
 
1924
        function (results) {
 
1925
            var websocket = window.WebSocket || window.MozWebSocket;
 
1926
            var passed = 'WebSocket' in window ? YES : 'MozWebSocket' in window ? YES | PREFIX : NO;
 
1927
            if (websocket && websocket.CLOSING !== 2) passed |= OLD;
 
1928
 
 
1929
            results.addItem({
 
1930
                key: 'communication.websocket.basic',
 
1931
                passed: passed
 
1932
            });
 
1933
        },
 
1934
 
 
1935
 
 
1936
        /* binary websockets */
 
1937
 
 
1938
        function (results) {
 
1939
            var passed = false;
 
1940
            var protocol = 'https:' == location.protocol ? 'wss' : 'ws';
 
1941
 
 
1942
            if ("WebSocket" in window) {
 
1943
                if ("binaryType" in WebSocket.prototype) {
 
1944
                    passed = true;
 
1945
                }
 
1946
                else {
 
1947
                    try {
 
1948
                        passed = !!(new WebSocket(protocol + '://.').binaryType);
 
1949
                    } catch (e) {
 
1950
                    }
 
1951
                }
 
1952
            }
 
1953
 
 
1954
            results.addItem({
 
1955
                key: 'communication.websocket.binary',
 
1956
                passed: passed
 
1957
            });
 
1958
        },
 
1959
 
 
1960
 
 
1961
        /* WebRTC */
 
1962
 
 
1963
        function (results) {
 
1964
            results.addItem({
 
1965
                key: 'rtc.webrtc',
 
1966
                passed: !!window.RTCPeerConnection ? YES : !!window.webkitRTCPeerConnection || !!window.mozRTCPeerConnection || !!window.msRTCPeerConnection || !!window.oRTCPeerConnection ? YES | PREFIX : NO
 
1967
            });
 
1968
        },
 
1969
 
 
1970
 
 
1971
        /* ObjectRTC */
 
1972
 
 
1973
        function (results) {
 
1974
            results.addItem({
 
1975
                key: 'rtc.objectrtc',
 
1976
                passed: !!window.RTCIceTransport ? YES : !!window.webkitRTCIceTransport || !!window.mozRTCIceTransport || !!window.msRTCIceTransport || !!window.oRTCIceTransport ? YES | PREFIX : NO
 
1977
            });
 
1978
        },
 
1979
 
 
1980
 
 
1981
        /* Datachannel */
 
1982
 
 
1983
        function (results) {
 
1984
            var passed = false;
 
1985
            try {
 
1986
                o = new (window.RTCPeerConnection || window.msRTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection)(null);
 
1987
                passed = 'createDataChannel' in o;
 
1988
            }
 
1989
            catch (e) {
 
1990
            }
 
1991
 
 
1992
            results.addItem({
 
1993
                key: 'rtc.datachannel',
 
1994
                passed: passed ? (window.RTCPeerConnection ? YES : YES | PREFIX) : NO
 
1995
            });
 
1996
        },
 
1997
 
 
1998
 
 
1999
        /* MediaRecorder */
 
2000
 
 
2001
        function (results) {
 
2002
            results.addItem({
 
2003
                key: 'rtc.recorder',
 
2004
                passed: 'MediaRecorder' in window
 
2005
            });
 
2006
        },
 
2007
 
 
2008
 
 
2009
 
 
2010
        /* Draggable */
 
2011
 
 
2012
        function (results) {
 
2013
            results.addItem({
 
2014
                key: 'interaction.dragdrop.attributes.draggable',
 
2015
                passed: 'draggable' in document.createElement('div')
 
2016
            });
 
2017
        },
 
2018
 
 
2019
 
 
2020
        /* Dropzone */
 
2021
 
 
2022
        function (results) {
 
2023
            var element = document.createElement('div');
 
2024
 
 
2025
            results.addItem({
 
2026
                key: 'interaction.dragdrop.attributes.dropzone',
 
2027
                passed: 'dropzone' in element ? YES : 'webkitdropzone' in element || 'mozdropzone' in element || 'msdropzone' in element || 'odropzone' in element ? YES | PREFIX : NO
 
2028
            });
 
2029
        },
 
2030
 
 
2031
 
 
2032
        /* Drag and drop events */
 
2033
 
 
2034
        function (results) {
 
2035
            var passed = 'draggable' in document.createElement('div')
 
2036
 
 
2037
                        /* We need to check if the draggable attribute is supported, because older versions of IE do
 
2038
                           support the incompatible versions of the events below. IE 9 and up do support the HTML5
 
2039
                           events in combination with the draggable attribute */
 
2040
 
 
2041
 
 
2042
            results.addItem({
 
2043
                key: 'interaction.dragdrop.events.ondrag',
 
2044
                passed: isEventSupported('drag') && passed
 
2045
            });
 
2046
 
 
2047
            results.addItem({
 
2048
                key: 'interaction.dragdrop.events.ondragstart',
 
2049
                passed: isEventSupported('dragstart') && passed
 
2050
            });
 
2051
 
 
2052
            results.addItem({
 
2053
                key: 'interaction.dragdrop.events.ondragenter',
 
2054
                passed: isEventSupported('dragenter') && passed
 
2055
            });
 
2056
 
 
2057
            results.addItem({
 
2058
                key: 'interaction.dragdrop.events.ondragover',
 
2059
                passed: isEventSupported('dragover') && passed
 
2060
            });
 
2061
 
 
2062
            results.addItem({
 
2063
                key: 'interaction.dragdrop.events.ondragleave',
 
2064
                passed: isEventSupported('dragleave') && passed
 
2065
            });
 
2066
 
 
2067
            results.addItem({
 
2068
                key: 'interaction.dragdrop.events.ondragend',
 
2069
                passed: isEventSupported('dragend') && passed
 
2070
            });
 
2071
 
 
2072
            results.addItem({
 
2073
                key: 'interaction.dragdrop.events.ondrop',
 
2074
                passed: isEventSupported('drop') && passed
 
2075
            });
 
2076
        },
 
2077
 
 
2078
 
 
2079
        /* contentEditable */
 
2080
 
 
2081
        function (results) {
 
2082
            results.addItem({
 
2083
                key: 'interaction.editing.elements.contentEditable',
 
2084
                passed: 'contentEditable' in document.createElement('div')
 
2085
            });
 
2086
        },
 
2087
 
 
2088
 
 
2089
        /* isContentEditable */
 
2090
 
 
2091
        function (results) {
 
2092
            results.addItem({
 
2093
                key: 'interaction.editing.elements.isContentEditable',
 
2094
                passed: 'isContentEditable' in document.createElement('div')
 
2095
            });
 
2096
        },
 
2097
 
 
2098
 
 
2099
        /* designMode */
 
2100
 
 
2101
        function (results) {
 
2102
            results.addItem({
 
2103
                key: 'interaction.editing.documents.designMode',
 
2104
                passed: 'designMode' in document
 
2105
            });
 
2106
        },
 
2107
 
 
2108
 
 
2109
        /* execCommand and queryCommand API */
 
2110
 
 
2111
        function (results) {
 
2112
            results.addItem({
 
2113
                key: 'interaction.editing.apis.execCommand',
 
2114
                passed: 'execCommand' in document
 
2115
            });
 
2116
 
 
2117
            results.addItem({
 
2118
                key: 'interaction.editing.apis.queryCommandEnabled',
 
2119
                passed: 'queryCommandEnabled' in document
 
2120
            });
 
2121
 
 
2122
            results.addItem({
 
2123
                key: 'interaction.editing.apis.queryCommandIndeterm',
 
2124
                passed: 'queryCommandIndeterm' in document
 
2125
            });
 
2126
 
 
2127
            results.addItem({
 
2128
                key: 'interaction.editing.apis.queryCommandState',
 
2129
                passed: 'queryCommandState' in document
 
2130
            });
 
2131
 
 
2132
            results.addItem({
 
2133
                key: 'interaction.editing.apis.queryCommandSupported',
 
2134
                passed: 'queryCommandSupported' in document
 
2135
            });
 
2136
 
 
2137
            results.addItem({
 
2138
                key: 'interaction.editing.apis.queryCommandValue',
 
2139
                passed: 'queryCommandValue' in document
 
2140
            });
 
2141
        },
 
2142
 
 
2143
 
 
2144
        /* read-write and read-only selectors */
 
2145
 
 
2146
        function (results) {
 
2147
            var selectors = "read-write read-only".split(" ");
 
2148
            var passed = [NO | UNKNOWN, NO | UNKNOWN];
 
2149
 
 
2150
            if ('querySelector' in document) {
 
2151
                var element = document.createElement('div');
 
2152
                element.id = 'testDivElement';
 
2153
                element.contentEditable = true;
 
2154
                document.body.appendChild(element);
 
2155
 
 
2156
                var nested = document.createElement('div');
 
2157
                nested.id = 'testDivNested';
 
2158
                nested.contentEditable = false;
 
2159
                element.appendChild(nested);
 
2160
 
 
2161
                try {
 
2162
                    passed[0] = document.querySelector("#testDivElement:read-write") == element;
 
2163
                } catch (e) {
 
2164
                    passed[0] = NO;
 
2165
 
 
2166
                    try {
 
2167
                        passed[0] = document.querySelector("#testDivElement:-moz-read-write") == element ? YES | PREFIX : NO;
 
2168
                    } catch (e) {
 
2169
                    }
 
2170
                }
 
2171
 
 
2172
                try {
 
2173
                    passed[1] = document.querySelector("#testDivNested:read-only") == nested;
 
2174
                } catch (e) {
 
2175
                    passed[1] = NO;
 
2176
 
 
2177
                    try {
 
2178
                        passed[1] = document.querySelector("#testDivNested:-moz-read-only") == nested ? YES | PREFIX : NO;
 
2179
                    } catch (e) {
 
2180
                    }
 
2181
                }
 
2182
 
 
2183
                document.body.removeChild(element);
 
2184
            }
 
2185
 
 
2186
            for (var i = 0; i < selectors.length; i++) {
 
2187
                results.addItem({
 
2188
                    key: 'interaction.editing.selectors.' + selectors[i],
 
2189
                    passed: passed[i]
 
2190
                });
 
2191
            }
 
2192
        },
 
2193
 
 
2194
 
 
2195
        /* ClipboardEvent */
 
2196
 
 
2197
        function (results) {
 
2198
            results.addItem({
 
2199
                key: 'interaction.clipboard',
 
2200
                passed: 'ClipboardEvent' in window
 
2201
            });
 
2202
        },
 
2203
 
 
2204
 
 
2205
        /* spellcheck */
 
2206
 
 
2207
        function (results) {
 
2208
            results.addItem({
 
2209
                key: 'interaction.spellcheck',
 
2210
                passed: 'spellcheck' in document.createElement('div')
 
2211
            });
 
2212
        },
 
2213
 
 
2214
 
 
2215
        /* webworker */
 
2216
 
 
2217
        function (results) {
 
2218
            results.addItem({
 
2219
                key: 'performance.worker',
 
2220
                passed: !!window.Worker
 
2221
            });
 
2222
        },
 
2223
 
 
2224
 
 
2225
        /* sharedworker */
 
2226
 
 
2227
        function (results) {
 
2228
            results.addItem({
 
2229
                key: 'performance.sharedWorker',
 
2230
                passed: !!window.SharedWorker
 
2231
            });
 
2232
        },
 
2233
 
 
2234
 
 
2235
        /* requestIdleCallback */
 
2236
 
 
2237
        function (results) {
 
2238
            results.addItem({
 
2239
                key: 'performance.requestIdleCallback',
 
2240
                passed: 'requestIdleCallback' in window
 
2241
            });
 
2242
        },
 
2243
 
 
2244
 
 
2245
        /* crypto */
 
2246
 
 
2247
        function (results) {
 
2248
            var passed = NO;
 
2249
            try {
 
2250
                var crypto = window.crypto || window.webkitCrypto || window.mozCrypto || window.msCrypto || window.oCrypto;
 
2251
                var available = window.crypto ? YES : window.mozCrypto || window.msCrypto || window.oCrypto ? YES | PREFIX : NO;
 
2252
                passed = !!crypto && 'subtle' in crypto ? available : !!crypto && 'webkitSubtle' in crypto ? YES | PREFIX : NO;
 
2253
            } catch (e) {
 
2254
            }
 
2255
 
 
2256
            results.addItem({
 
2257
                key: 'security.crypto',
 
2258
                passed: passed
 
2259
            });
 
2260
        },
 
2261
 
 
2262
 
 
2263
        /* csp 1.0 */
 
2264
 
 
2265
        function (results) {
 
2266
            var passed = false;
 
2267
 
 
2268
            if (navigator.webdriver && Browsers.isBrowser('Firefox', '>', 22)) {
 
2269
                passed = YES | DISABLED;
 
2270
            }
 
2271
 
 
2272
            var item = results.addItem({
 
2273
                key: 'security.csp10',
 
2274
                passed: passed
 
2275
            });
 
2276
 
 
2277
            window.addEventListener('message', function(e) {
 
2278
                if (e.data === 'csp10:passed') {
 
2279
                    item.update({
 
2280
                        passed: true
 
2281
                    });
 
2282
 
 
2283
                    item.stopBackground();
 
2284
                }
 
2285
 
 
2286
                if (e.data === 'csp10:failed') {
 
2287
                    item.stopBackground();
 
2288
                }
 
2289
            }, false);
 
2290
 
 
2291
            item.startBackground();
 
2292
 
 
2293
            var iframe = document.createElement('iframe');
 
2294
            iframe.src = '/assets/csp.html';
 
2295
            iframe.style.visibility = 'hidden';
 
2296
            document.body.appendChild(iframe);
 
2297
 
 
2298
            window.setTimeout(function () {
 
2299
                item.stopBackground();
 
2300
                document.body.removeChild(iframe);
 
2301
            }, 1000);
 
2302
        },
 
2303
 
 
2304
 
 
2305
        /* csp 1.1 */
 
2306
 
 
2307
        function (results) {
 
2308
            results.addItem({
 
2309
                key: 'security.csp11',
 
2310
                passed: 'SecurityPolicyViolationEvent' in window
 
2311
            });
 
2312
        },
 
2313
 
 
2314
 
 
2315
        /* cors */
 
2316
 
 
2317
        function (results) {
 
2318
            results.addItem({
 
2319
                key: 'security.cors',
 
2320
                passed: window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()
 
2321
            });
 
2322
        },
 
2323
 
 
2324
 
 
2325
        /* subresource integrity */
 
2326
 
 
2327
        function (results) {
 
2328
            results.addItem({
 
2329
                key: 'security.integrity',
 
2330
                passed: 'integrity' in document.createElement('link')
 
2331
            });
 
2332
        },
 
2333
 
 
2334
 
 
2335
        /* postMessage */
 
2336
 
 
2337
        function (results) {
 
2338
            results.addItem({
 
2339
                key: 'security.postMessage',
 
2340
                passed: !!window.postMessage
 
2341
            });
 
2342
        },
 
2343
 
 
2344
 
 
2345
        /* web authentication */
 
2346
 
 
2347
        function (results) {
 
2348
            results.addItem({
 
2349
                key: 'security.authentication',
 
2350
                passed: 'webauthn' in window ? YES : 'msCredentials' in window ? YES | OLD : NO
 
2351
            });
 
2352
        },
 
2353
 
 
2354
 
 
2355
        /* credential management */
 
2356
 
 
2357
        function (results) {
 
2358
            results.addItem({
 
2359
                key: 'security.credential',
 
2360
                passed: 'credentials' in navigator
 
2361
            });
 
2362
        },
 
2363
 
 
2364
 
 
2365
        /* sandboxed iframe */
 
2366
 
 
2367
        function (results) {
 
2368
            results.addItem({
 
2369
                key: 'security.sandbox',
 
2370
                passed: 'sandbox' in document.createElement('iframe')
 
2371
            });
 
2372
        },
 
2373
 
 
2374
 
 
2375
        /* srcdoc iframe */
 
2376
 
 
2377
        function (results) {
 
2378
            results.addItem({
 
2379
                key: 'security.srcdoc',
 
2380
                passed: 'srcdoc' in document.createElement('iframe')
 
2381
            });
 
2382
        },
 
2383
 
 
2384
 
 
2385
        /* web payments */
 
2386
 
 
2387
        function (results) {
 
2388
            results.addItem({
 
2389
                key: 'payments.payments',
 
2390
                passed: 'PaymentRequest' in window
 
2391
            });
 
2392
        },
 
2393
 
 
2394
 
 
2395
        /* history */
 
2396
 
 
2397
        function (results) {
 
2398
            results.addItem({
 
2399
                key: 'other.history',
 
2400
                passed: !!(window.history && history.pushState)
 
2401
            });
 
2402
        },
 
2403
 
 
2404
 
 
2405
        /* video element */
 
2406
 
 
2407
        function (results) {
 
2408
            var element = document.createElement('video');
 
2409
 
 
2410
            results.addItem({
 
2411
                key: 'video.element',
 
2412
                passed: !!element.canPlayType
 
2413
            });
 
2414
 
 
2415
 
 
2416
            /* audioTracks property */
 
2417
 
 
2418
            results.addItem({
 
2419
                key: 'video.audiotracks',
 
2420
                passed: 'audioTracks' in element
 
2421
            });
 
2422
 
 
2423
 
 
2424
            /* videoTracks property */
 
2425
 
 
2426
            results.addItem({
 
2427
                key: 'video.videotracks',
 
2428
                passed: 'videoTracks' in element
 
2429
            });
 
2430
 
 
2431
 
 
2432
            /* subtitles */
 
2433
 
 
2434
            results.addItem({
 
2435
                key: 'video.subtitle',
 
2436
                passed: 'track' in document.createElement('track')
 
2437
            });
 
2438
 
 
2439
 
 
2440
            /* poster */
 
2441
 
 
2442
            results.addItem({
 
2443
                key: 'video.poster',
 
2444
                passed: 'poster' in element
 
2445
            });
 
2446
        },
 
2447
 
 
2448
 
 
2449
        /* video codecs */
 
2450
 
 
2451
        function (results) {
 
2452
            var element = document.createElement('video');
 
2453
 
 
2454
            /* mpeg-4 codec */
 
2455
 
 
2456
            results.addItem({
 
2457
                key: 'video.codecs.mp4.mpeg4',
 
2458
                passed: !!element.canPlayType && canPlayType(element, 'video/mp4; codecs="mp4v.20.8"')
 
2459
            });
 
2460
 
 
2461
            /* h.264 codec */
 
2462
 
 
2463
            /* I added a workaround for IE9, which only detects H.264 if you also provide an audio codec. Bug filed @ connect.microsoft.com */
 
2464
 
 
2465
            results.addItem({
 
2466
                key: 'video.codecs.mp4.h264',
 
2467
                passed: !!element.canPlayType && (canPlayType(element, 'video/mp4; codecs="avc1.42E01E"') || canPlayType(element, 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'))
 
2468
            });
 
2469
 
 
2470
            /* h.265 codec */
 
2471
 
 
2472
            results.addItem({
 
2473
                key: 'video.codecs.mp4.h265',
 
2474
                passed: !!element.canPlayType && (canPlayType(element, 'video/mp4; codecs="hvc1.1.L0.0"') || canPlayType(element, 'video/mp4; codecs="hev1.1.L0.0"'))
 
2475
            });
 
2476
 
 
2477
            /* theora codec */
 
2478
 
 
2479
            results.addItem({
 
2480
                key: 'video.codecs.ogg.theora',
 
2481
                passed: !!element.canPlayType && canPlayType(element, 'video/ogg; codecs="theora"')
 
2482
            });
 
2483
 
 
2484
            /* vp8 in webm codec */
 
2485
 
 
2486
            results.addItem({
 
2487
                key: 'video.codecs.webm.vp8',
 
2488
                passed: !!element.canPlayType && canPlayType(element, 'video/webm; codecs="vp8"')
 
2489
            });
 
2490
 
 
2491
            /* vp9 in webm codec */
 
2492
 
 
2493
            results.addItem({
 
2494
                key: 'video.codecs.webm.vp9',
 
2495
                passed: !!element.canPlayType && canPlayType(element, 'video/webm; codecs="vp9"')
 
2496
            });
 
2497
 
 
2498
            /* does codec detection work properly? */
 
2499
 
 
2500
            var passed = true;
 
2501
 
 
2502
            if (!!element.canPlayType) {
 
2503
                if (element.canPlayType('video/nonsense') == 'no') {
 
2504
                    passed = false;
 
2505
                    log('BUGGY: Codec detection bug in Firefox 3.5.0 - 3.5.1 and Safari 4.0.0 - 4.0.4 that answer "no" to unknown codecs instead of an empty string')
 
2506
                }
 
2507
 
 
2508
                if (element.canPlayType('video/webm') == 'probably') {
 
2509
                    passed = false;
 
2510
                    log('BUGGY: Codec detection bug that Firefox 27 and earlier always says "probably" when asked about WebM, even when the codecs string is not present')
 
2511
                }
 
2512
 
 
2513
                if (element.canPlayType('video/mp4; codecs="avc1.42E01E"') == 'maybe' && element.canPlayType('video/mp4') == 'probably') {
 
2514
                    passed = false;
 
2515
                    log('BUGGY: Codec detection bug in iOS 4.1 and earlier that switches "maybe" and "probably" around')
 
2516
                }
 
2517
 
 
2518
                if (element.canPlayType('video/mp4; codecs="avc1.42E01E"') == 'maybe' && element.canPlayType('video/mp4') == 'maybe') {
 
2519
                    passed = false;
 
2520
                    log('BUGGY: Codec detection bug in Android where no better answer than "maybe" is given')
 
2521
                }
 
2522
 
 
2523
                if (element.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') == 'probably' && element.canPlayType('video/mp4; codecs="avc1.42E01E"') != 'probably') {
 
2524
                    passed = false;
 
2525
                    log('BUGGY: Codec detection bug in Internet Explorer 9 that requires both audio and video codec on test')
 
2526
                }
 
2527
            }
 
2528
 
 
2529
            results.addItem({
 
2530
                key: 'video.canplaytype',
 
2531
                passed: element.canPlayType ? (passed ? YES : YES | BUGGY) : NO
 
2532
            });
 
2533
        },
 
2534
 
 
2535
 
 
2536
        /* audio element */
 
2537
 
 
2538
        function (results) {
 
2539
            var element = document.createElement('audio');
 
2540
 
 
2541
            results.addItem({
 
2542
                key: 'audio.element',
 
2543
                passed: !!element.canPlayType
 
2544
            });
 
2545
 
 
2546
            /* loop property */
 
2547
 
 
2548
            results.addItem({
 
2549
                key: 'audio.loop',
 
2550
                passed: 'loop' in element
 
2551
            });
 
2552
 
 
2553
            /* preload property */
 
2554
 
 
2555
            results.addItem({
 
2556
                key: 'audio.preload',
 
2557
                passed: 'preload' in element
 
2558
            });
 
2559
        },
 
2560
 
 
2561
 
 
2562
        /* audio codecs */
 
2563
 
 
2564
        function (results) {
 
2565
            var element = document.createElement('audio');
 
2566
 
 
2567
            /* pcm codec */
 
2568
 
 
2569
            results.addItem({
 
2570
                key: 'audio.codecs.pcm',
 
2571
                passed: !!element.canPlayType && canPlayType(element, 'audio/wav; codecs="1"')
 
2572
            });
 
2573
 
 
2574
            /* mp3 codec */
 
2575
 
 
2576
            var r = false;
 
2577
            if (element.canPlayType) {
 
2578
                var t = element.canPlayType('audio/mpeg');
 
2579
                if (t == 'maybe') {
 
2580
                    // We need to check if the browser really supports playing MP3s by loading one and seeing if the
 
2581
                    // loadedmetadata event is triggered... but for now assume it does support it...
 
2582
                    r = true;
 
2583
                } else if (t == 'probably') {
 
2584
                    r = true;
 
2585
                }
 
2586
            }
 
2587
 
 
2588
            results.addItem({
 
2589
                key: 'audio.codecs.mp3',
 
2590
                passed: r
 
2591
            });
 
2592
 
 
2593
            /* aac codec */
 
2594
 
 
2595
            results.addItem({
 
2596
                key: 'audio.codecs.mp4.aac',
 
2597
                passed: !!element.canPlayType && canPlayType(element, 'audio/mp4; codecs="mp4a.40.2"')
 
2598
            });
 
2599
 
 
2600
            /* ac3 codec */
 
2601
 
 
2602
            results.addItem({
 
2603
                key: 'audio.codecs.mp4.ac3',
 
2604
                passed: !!element.canPlayType && canPlayType(element, 'audio/mp4; codecs="ac-3"')
 
2605
            });
 
2606
 
 
2607
            /* enhanced ac3 codec */
 
2608
 
 
2609
            results.addItem({
 
2610
                key: 'audio.codecs.mp4.ec3',
 
2611
                passed: !!element.canPlayType && canPlayType(element, 'audio/mp4; codecs="ec-3"')
 
2612
            });
 
2613
 
 
2614
            /* ogg vorbis codec */
 
2615
 
 
2616
            results.addItem({
 
2617
                key: 'audio.codecs.ogg.vorbis',
 
2618
                passed: !!element.canPlayType && canPlayType(element, 'audio/ogg; codecs="vorbis"')
 
2619
            });
 
2620
 
 
2621
            /* ogg opus codec */
 
2622
 
 
2623
            results.addItem({
 
2624
                key: 'audio.codecs.ogg.opus',
 
2625
                passed: !!element.canPlayType && canPlayType(element, 'audio/ogg; codecs="opus"')
 
2626
            });
 
2627
 
 
2628
            /* webm vorbis codec */
 
2629
 
 
2630
            results.addItem({
 
2631
                key: 'audio.codecs.webm.vorbis',
 
2632
                passed: !!element.canPlayType && canPlayType(element, 'audio/webm; codecs="vorbis"')
 
2633
            });
 
2634
 
 
2635
            /* webm opus codec */
 
2636
 
 
2637
            results.addItem({
 
2638
                key: 'audio.codecs.webm.opus',
 
2639
                passed: !!element.canPlayType && canPlayType(element, 'audio/webm; codecs="opus"')
 
2640
            });
 
2641
        },
 
2642
 
 
2643
 
 
2644
 
 
2645
        /* webaudio */
 
2646
 
 
2647
        function (results) {
 
2648
            results.addItem({
 
2649
                key: 'audio.webaudio',
 
2650
                passed: 'AudioContext' in window ? YES : 'webkitAudioContext' in window || 'mozAudioContext' in window || 'oAudioContext' in window || 'msAudioContext' in window ? YES | PREFIX : NO
 
2651
            });
 
2652
        },
 
2653
 
 
2654
 
 
2655
        /* speech recognition */
 
2656
 
 
2657
        function (results) {
 
2658
            results.addItem({
 
2659
                key: 'audio.speechrecognition',
 
2660
                passed: 'SpeechRecognition' in window ? YES : 'webkitSpeechRecognition' in window || 'mozSpeechRecognition' in window || 'oSpeechRecognition' in window || 'msSpeechRecognition' in window ? YES | PREFIX : NO
 
2661
            });
 
2662
        },
 
2663
 
 
2664
 
 
2665
        /* speech synthesis */
 
2666
 
 
2667
        function (results) {
 
2668
            var speechSynthesis = window.speechSynthesis || window.webkitSpeechSynthesis || window.mozSpeechSynthesis || window.oSpeechSynthesis || window.msSpeechSynthesis;
 
2669
            var available = 'speechSynthesis' in window ? YES : 'webkitSpeechSynthesis' in window || 'mozSpeechSynthesis' in window || 'oSpeechSynthesis' in window || 'msSpeechSynthesis' in window ? YES | PREFIX : NO;
 
2670
            var voices = speechSynthesis ? speechSynthesis.getVoices().length : 0;
 
2671
 
 
2672
            var speechItem = results.addItem({
 
2673
                key: 'audio.speechsynthesis',
 
2674
                passed: speechSynthesis && voices ? available : NO
 
2675
            });
 
2676
 
 
2677
            if (speechSynthesis && !voices) {
 
2678
                if (speechSynthesis.addEventListener) {
 
2679
                    speechItem.startBackground();
 
2680
 
 
2681
                    speechSynthesis.addEventListener("voiceschanged", function () {
 
2682
                        voices = speechSynthesis.getVoices().length;
 
2683
 
 
2684
                        speechItem.update({
 
2685
                            passed: voices ? available : NO
 
2686
                        });
 
2687
 
 
2688
                        speechItem.stopBackground();
 
2689
                    });
 
2690
 
 
2691
                    window.setTimeout(function () {
 
2692
                        speechItem.stopBackground();
 
2693
                    }, 1000);
 
2694
                }
 
2695
            }
 
2696
        },
 
2697
 
 
2698
 
 
2699
        /* streaming */
 
2700
 
 
2701
        function (results) {
 
2702
            var element = document.createElement('video');
 
2703
 
 
2704
            /* mediasource */
 
2705
 
 
2706
            results.addItem({
 
2707
                key: 'streaming.mediasource',
 
2708
                passed: 'MediaSource' in window ? YES : 'WebKitMediaSource' in window || 'mozMediaSource' in window || 'msMediaSource' in window ? YES | PREFIX : NO
 
2709
            });
 
2710
 
 
2711
            /* drm */
 
2712
 
 
2713
            results.addItem({
 
2714
                key: 'streaming.drm',
 
2715
                passed: 'setMediaKeys' in element ? YES : 'webkitAddKey' in element || 'webkitSetMediaKeys' in element || 'mozSetMediaKeys' in element || 'msSetMediaKeys' in element ? YES | PREFIX : NO
 
2716
            });
 
2717
 
 
2718
            /* dash streaming */
 
2719
 
 
2720
            results.addItem({
 
2721
                key: 'streaming.type.dash',
 
2722
                passed: !!element.canPlayType && element.canPlayType('application/dash+xml') != ''
 
2723
            });
 
2724
 
 
2725
            /* hls streaming */
 
2726
 
 
2727
            results.addItem({
 
2728
                key: 'streaming.type.hls',
 
2729
                passed: !!element.canPlayType && (element.canPlayType('application/vnd.apple.mpegURL') != '' || element.canPlayType('audio/mpegurl') != '')
 
2730
            });
 
2731
        },
 
2732
 
 
2733
 
 
2734
        /* streaming video codecs */
 
2735
 
 
2736
        function (results) {
 
2737
 
 
2738
            /* mpeg-4 in mp4 codec */
 
2739
 
 
2740
            results.addItem({
 
2741
                key: 'streaming.video.codecs.mp4.mpeg4',
 
2742
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('video/mp4; codecs="mp4v.20.8"')
 
2743
            });
 
2744
 
 
2745
            /* h.264 in mp4 codec */
 
2746
 
 
2747
            results.addItem({
 
2748
                key: 'streaming.video.codecs.mp4.h264',
 
2749
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E"')
 
2750
            });
 
2751
 
 
2752
            /* h.265 in mp4 codec */
 
2753
 
 
2754
            results.addItem({
 
2755
                key: 'streaming.video.codecs.mp4.h265',
 
2756
                passed: 'MediaSource' in window && (MediaSource.isTypeSupported('video/mp4; codecs="hvc1.1.L0.0"') || MediaSource.isTypeSupported('video/mp4; codecs="hev1.1.L0.0"'))
 
2757
            });
 
2758
 
 
2759
            /* h.264 in ts codec */
 
2760
 
 
2761
            results.addItem({
 
2762
                key: 'streaming.video.codecs.ts.h264',
 
2763
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('video/mp2t; codecs="avc1.42E01E"')
 
2764
            });
 
2765
 
 
2766
            /* h.265 in ts codec */
 
2767
 
 
2768
            results.addItem({
 
2769
                key: 'streaming.video.codecs.ts.h265',
 
2770
                passed: 'MediaSource' in window && (MediaSource.isTypeSupported('video/mp2t; codecs="hvc1.1.L0.0"') || MediaSource.isTypeSupported('video/mp2t; codecs="hev1.1.L0.0"'))
 
2771
            });
 
2772
 
 
2773
            /* theora codec */
 
2774
 
 
2775
            results.addItem({
 
2776
                key: 'streaming.video.codecs.ogg.theora',
 
2777
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('video/ogg; codecs="theora"')
 
2778
            });
 
2779
 
 
2780
            /* vp8 in webm codec */
 
2781
 
 
2782
            results.addItem({
 
2783
                key: 'streaming.video.codecs.webm.vp8',
 
2784
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('video/webm; codecs="vp8"')
 
2785
            });
 
2786
 
 
2787
            /* vp9 in webm codec */
 
2788
 
 
2789
            results.addItem({
 
2790
                key: 'streaming.video.codecs.webm.vp9',
 
2791
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('video/webm; codecs="vp9"')
 
2792
            });
 
2793
        },
 
2794
 
 
2795
 
 
2796
        /* streaming audio codecs */
 
2797
 
 
2798
        function (results) {
 
2799
 
 
2800
            /* aac codec in mp4 */
 
2801
 
 
2802
            results.addItem({
 
2803
                key: 'streaming.audio.codecs.mp4.aac',
 
2804
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"')
 
2805
            });
 
2806
 
 
2807
            /* ac3 codec in mp4 */
 
2808
 
 
2809
            results.addItem({
 
2810
                key: 'streaming.audio.codecs.mp4.ac3',
 
2811
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
 
2812
            });
 
2813
 
 
2814
            /* enhanced ac3 codec in mp4 */
 
2815
 
 
2816
            results.addItem({
 
2817
                key: 'streaming.audio.codecs.mp4.ec3',
 
2818
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/mp4; codecs="ec-3"')
 
2819
            });
 
2820
 
 
2821
            /* aac codec in mp4 */
 
2822
 
 
2823
            results.addItem({
 
2824
                key: 'streaming.audio.codecs.ts.aac',
 
2825
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/mp2t; codecs="mp4a.40.2"')
 
2826
            });
 
2827
 
 
2828
            /* ac3 codec in mp4 */
 
2829
 
 
2830
            results.addItem({
 
2831
                key: 'streaming.audio.codecs.ts.ac3',
 
2832
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/mp2t; codecs="ac-3"')
 
2833
            });
 
2834
 
 
2835
            /* enhanced ac3 codec in mp4 */
 
2836
 
 
2837
            results.addItem({
 
2838
                key: 'streaming.audio.codecs.ts.ec3',
 
2839
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/mp2t; codecs="ec-3"')
 
2840
            });
 
2841
 
 
2842
            /* vorbis in ogg codec */
 
2843
 
 
2844
            results.addItem({
 
2845
                key: 'streaming.audio.codecs.ogg.vorbis',
 
2846
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/ogg; codecs="vorbis"')
 
2847
            });
 
2848
 
 
2849
            /* opus in ogg codec */
 
2850
 
 
2851
            results.addItem({
 
2852
                key: 'streaming.audio.codecs.ogg.opus',
 
2853
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/ogg; codecs="opus"')
 
2854
            });
 
2855
 
 
2856
            /* vorbis in webm codec */
 
2857
 
 
2858
            results.addItem({
 
2859
                key: 'streaming.audio.codecs.webm.vorbis',
 
2860
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/webm; codecs="vorbis"')
 
2861
            });
 
2862
 
 
2863
            /* opus in webm codec */
 
2864
 
 
2865
            results.addItem({
 
2866
                key: 'streaming.audio.codecs.webm.opus',
 
2867
                passed: 'MediaSource' in window && MediaSource.isTypeSupported('audio/webm; codecs="opus"')
 
2868
            });
 
2869
        },
 
2870
 
 
2871
 
 
2872
        /* picture element */
 
2873
 
 
2874
        function (results) {
 
2875
            results.addItem({
 
2876
                key: 'responsive.picture',
 
2877
                passed: 'HTMLPictureElement' in window
 
2878
            });
 
2879
        },
 
2880
 
 
2881
 
 
2882
        /* srcset attribute */
 
2883
 
 
2884
        function (results) {
 
2885
            results.addItem({
 
2886
                key: 'responsive.srcset',
 
2887
                passed: 'srcset' in document.createElement('img')
 
2888
            });
 
2889
        },
 
2890
 
 
2891
 
 
2892
        /* sizes attribute */
 
2893
 
 
2894
        function (results) {
 
2895
            results.addItem({
 
2896
                key: 'responsive.sizes',
 
2897
                passed: 'sizes' in document.createElement('img')
 
2898
            });
 
2899
        },
 
2900
 
 
2901
 
 
2902
        /* canvas element and 2d context */
 
2903
 
 
2904
        function (results) {
 
2905
            var canvas = document.createElement('canvas');
 
2906
 
 
2907
            results.addItem({
 
2908
                key: 'canvas.context',
 
2909
                passed: !!(canvas.getContext && typeof CanvasRenderingContext2D != 'undefined' && canvas.getContext('2d') instanceof CanvasRenderingContext2D)
 
2910
            });
 
2911
        },
 
2912
 
 
2913
 
 
2914
        /* canvas drawing functions */
 
2915
 
 
2916
        function (results) {
 
2917
            var canvas = document.createElement('canvas');
 
2918
 
 
2919
            /* text support */
 
2920
 
 
2921
            var passed = false;
 
2922
            if (canvas.getContext) {
 
2923
                try {
 
2924
                    passed = typeof canvas.getContext('2d').fillText == 'function';
 
2925
                }
 
2926
                catch (e) {
 
2927
                }
 
2928
            }
 
2929
 
 
2930
            results.addItem({
 
2931
                key: 'canvas.text',
 
2932
                passed: passed
 
2933
            });
 
2934
 
 
2935
            /* ellipse support */
 
2936
 
 
2937
            var passed = false;
 
2938
            if (canvas.getContext) {
 
2939
                try {
 
2940
                    passed = typeof canvas.getContext('2d').ellipse != 'undefined';
 
2941
                }
 
2942
                catch (e) {
 
2943
                }
 
2944
            }
 
2945
 
 
2946
            results.addItem({
 
2947
                key: 'canvas.ellipse',
 
2948
                passed: passed
 
2949
            });
 
2950
 
 
2951
            /* dashed support */
 
2952
 
 
2953
            var passed = false;
 
2954
            if (canvas.getContext) {
 
2955
                try {
 
2956
                    passed = typeof canvas.getContext('2d').setLineDash != 'undefined';
 
2957
                }
 
2958
                catch (e) {
 
2959
                }
 
2960
            }
 
2961
 
 
2962
            results.addItem({
 
2963
                key: 'canvas.dashed',
 
2964
                passed: passed
 
2965
            });
 
2966
 
 
2967
            /* focusring support */
 
2968
 
 
2969
            var passed = false;
 
2970
            if (canvas.getContext) {
 
2971
                try {
 
2972
                    passed = typeof canvas.getContext('2d').drawFocusIfNeeded != 'undefined';
 
2973
                }
 
2974
                catch (e) {
 
2975
                }
 
2976
            }
 
2977
 
 
2978
            results.addItem({
 
2979
                key: 'canvas.focusring',
 
2980
                passed: passed
 
2981
            });
 
2982
 
 
2983
            /* hittest support */
 
2984
 
 
2985
            var passed = false;
 
2986
            if (canvas.getContext) {
 
2987
                try {
 
2988
                    passed = typeof canvas.getContext('2d').addHitRegion != 'undefined';
 
2989
                }
 
2990
                catch (e) {
 
2991
                }
 
2992
            }
 
2993
 
 
2994
            results.addItem({
 
2995
                key: 'canvas.hittest',
 
2996
                passed: passed
 
2997
            });
 
2998
        },
 
2999
 
 
3000
 
 
3001
        /* path support */
 
3002
 
 
3003
        function (results) {
 
3004
            results.addItem({
 
3005
                key: 'canvas.path',
 
3006
                passed: typeof Path2D != "undefined" ? YES : typeof Path != "undefined" ? YES | OLD : NO
 
3007
            });
 
3008
        },
 
3009
 
 
3010
 
 
3011
        /* blending support */
 
3012
 
 
3013
        function (results) {
 
3014
            var canvas = document.createElement('canvas');
 
3015
 
 
3016
            var passed = false;
 
3017
 
 
3018
            if (canvas.getContext) {
 
3019
                canvas.width = 1;
 
3020
                canvas.height = 1;
 
3021
 
 
3022
                try {
 
3023
                    var ctx = canvas.getContext('2d');
 
3024
                    ctx.fillStyle = '#fff';
 
3025
                    ctx.fillRect(0, 0, 1, 1);
 
3026
                    ctx.globalCompositeOperation = 'screen';
 
3027
                    ctx.fillStyle = '#000';
 
3028
                    ctx.fillRect(0, 0, 1, 1);
 
3029
 
 
3030
                    var data = ctx.getImageData(0, 0, 1, 1);
 
3031
 
 
3032
                    passed = ctx.globalCompositeOperation == 'screen' && data.data[0] == 255;
 
3033
                }
 
3034
                catch (e) {
 
3035
                }
 
3036
            }
 
3037
 
 
3038
            results.addItem({
 
3039
                key: 'canvas.blending',
 
3040
                passed: passed
 
3041
            });
 
3042
        },
 
3043
 
 
3044
 
 
3045
        /* export to image format */
 
3046
 
 
3047
        function (results) {
 
3048
            var canvas = document.createElement('canvas');
 
3049
 
 
3050
            /* export to png */
 
3051
 
 
3052
            var passed = false;
 
3053
            if (canvas.getContext) {
 
3054
                try {
 
3055
                    passed = canvas.toDataURL('image/png').substring(5, 14) == 'image/png';
 
3056
                }
 
3057
                catch (e) {
 
3058
                }
 
3059
            }
 
3060
 
 
3061
            results.addItem({
 
3062
                key: 'canvas.png',
 
3063
                passed: passed
 
3064
            });
 
3065
 
 
3066
            /* export to jpeg */
 
3067
 
 
3068
            var passed = false;
 
3069
            if (canvas.getContext) {
 
3070
                try {
 
3071
                    passed = canvas.toDataURL('image/jpeg').substring(5, 15) == 'image/jpeg';
 
3072
                }
 
3073
                catch (e) {
 
3074
                }
 
3075
            }
 
3076
 
 
3077
            results.addItem({
 
3078
                key: 'canvas.jpeg',
 
3079
                passed: passed
 
3080
            });
 
3081
 
 
3082
            /* export to jpeg xr */
 
3083
 
 
3084
            var passed = false;
 
3085
            if (canvas.getContext) {
 
3086
                try {
 
3087
                    passed = canvas.toDataURL('image/vnd.ms-photo').substring(5, 23) == 'image/vnd.ms-photo';
 
3088
                }
 
3089
                catch (e) {
 
3090
                }
 
3091
            }
 
3092
 
 
3093
            results.addItem({
 
3094
                key: 'canvas.jpegxr',
 
3095
                passed: passed
 
3096
            });
 
3097
 
 
3098
            /* export to webp */
 
3099
 
 
3100
            var passed = false;
 
3101
            if (canvas.getContext) {
 
3102
                try {
 
3103
                    passed = canvas.toDataURL('image/webp').substring(5, 15) == 'image/webp';
 
3104
                }
 
3105
                catch (e) {
 
3106
                }
 
3107
            }
 
3108
 
 
3109
            results.addItem({
 
3110
                key: 'canvas.webp',
 
3111
                passed: passed
 
3112
            });
 
3113
        },
 
3114
 
 
3115
 
 
3116
        /* webgl */
 
3117
 
 
3118
        function (results) {
 
3119
            var element = document.createElement('canvas');
 
3120
 
 
3121
            var passed = 'WebGLRenderingContext' in window;
 
3122
 
 
3123
            var contexts = ['webgl', 'experimental-webgl', 'ms-webgl', 'moz-webgl', 'opera-3d', 'webkit-3d', 'ms-3d', '3d'];
 
3124
            var context = '';
 
3125
            var enabled = false;
 
3126
 
 
3127
            for (var b = -1, len = contexts.length; ++b < len;) {
 
3128
                try {
 
3129
                    if (element.getContext(contexts[b])) {
 
3130
                        context = contexts[b];
 
3131
                        enabled = true;
 
3132
                        break;
 
3133
                    };
 
3134
                } catch (e) { }
 
3135
            }
 
3136
 
 
3137
            results.addItem({
 
3138
                key: '3d.webgl',
 
3139
                passed: enabled ? (context == 'webgl' ? YES : (context == 'experimental-webgl' ? YES | EXPERIMENTAL : YES | PREFIX)) : (passed ? YES | DISABLED : NO)
 
3140
            });
 
3141
        },
 
3142
 
 
3143
 
 
3144
        /* webgl2 */
 
3145
 
 
3146
        function (results) {
 
3147
            var element = document.createElement('canvas');
 
3148
            var contexts = ['webgl2', 'experimental-webgl2'];
 
3149
            var context = '';
 
3150
            var enabled = false;
 
3151
 
 
3152
            var passed = 'WebGL2RenderingContext' in window;
 
3153
 
 
3154
            for (var b = -1, len = contexts.length; ++b < len;) {
 
3155
                try {
 
3156
                    if (element.getContext(contexts[b])) {
 
3157
                        context = contexts[b];
 
3158
                        enabled = true;
 
3159
                        break;
 
3160
                    };
 
3161
                } catch (e) { }
 
3162
            }
 
3163
 
 
3164
            results.addItem({
 
3165
                key: '3d.webgl2',
 
3166
                passed: enabled ? (context == 'webgl2' ? YES : (context == 'experimental-webgl2' ? YES | EXPERIMENTAL : YES | PREFIX)) : (passed ? YES | DISABLED : NO)
 
3167
            });
 
3168
        },
 
3169
 
 
3170
 
 
3171
        /* webvr */
 
3172
 
 
3173
        function (results) {
 
3174
            results.addItem({
 
3175
                key: '3d.webvr',
 
3176
                passed: 'getVRDisplays' in navigator ? YES : 'mozGetVRDevices' in navigator ? YES | PREFIX : NO
 
3177
            });
 
3178
        },
 
3179
 
 
3180
 
 
3181
        /* animation api */
 
3182
 
 
3183
        function (results) {
 
3184
            results.addItem({
 
3185
                key: 'animation.webanimation',
 
3186
                passed: 'animate' in document.createElement('div')
 
3187
            });
 
3188
        },
 
3189
 
 
3190
 
 
3191
        /* requestAnimationFrame */
 
3192
 
 
3193
        function (results) {
 
3194
            results.addItem({
 
3195
                key: 'animation.requestAnimationFrame',
 
3196
                passed: !!window.requestAnimationFrame ? YES : !!window.webkitRequestAnimationFrame || !!window.mozRequestAnimationFrame || !!window.msRequestAnimationFrame || !!window.oRequestAnimationFrame ? YES | PREFIX : NO
 
3197
            });
 
3198
        },
 
3199
 
 
3200
 
 
3201
        /* applicationCache */
 
3202
 
 
3203
        function (results) {
 
3204
            results.addItem({
 
3205
                key: 'offline.applicationCache',
 
3206
                passed: !!window.applicationCache
 
3207
            });
 
3208
        },
 
3209
 
 
3210
 
 
3211
        /* serviceWorker */
 
3212
 
 
3213
        function (results) {
 
3214
            results.addItem({
 
3215
                key: 'offline.serviceWorkers',
 
3216
                passed: !!window.navigator.serviceWorker
 
3217
            });
 
3218
        },
 
3219
 
 
3220
 
 
3221
        /* serviceWorker */
 
3222
 
 
3223
        function (results) {
 
3224
            results.addItem({
 
3225
                key: 'offline.pushMessages',
 
3226
                passed: 'PushManager' in window && 'PushSubscription' in window
 
3227
            });
 
3228
        },
 
3229
 
 
3230
 
 
3231
        /* registerProtocolHandler */
 
3232
 
 
3233
        function (results) {
 
3234
            results.addItem({
 
3235
                key: 'offline.registerProtocolHandler',
 
3236
                passed: !!window.navigator.registerProtocolHandler
 
3237
            });
 
3238
        },
 
3239
 
 
3240
 
 
3241
        /* registerContentHandler */
 
3242
 
 
3243
        function (results) {
 
3244
            results.addItem({
 
3245
                key: 'offline.registerContentHandler',
 
3246
                passed: !!window.navigator.registerContentHandler
 
3247
            });
 
3248
        },
 
3249
 
 
3250
 
 
3251
        /* session storage */
 
3252
 
 
3253
        function (results) {
 
3254
            results.addItem({
 
3255
                key: 'storage.sessionStorage',
 
3256
                passed: 'sessionStorage' in window && window.sessionStorage != null
 
3257
            });
 
3258
 
 
3259
        },
 
3260
 
 
3261
 
 
3262
        /* local storage */
 
3263
 
 
3264
        function (results) {
 
3265
            var passed = false;
 
3266
            try {
 
3267
                passed = 'localStorage' in window && window.localStorage != null
 
3268
            } catch (e) {
 
3269
                /* If we get a security exception we know the feature exists, but cookies are disabled */
 
3270
                if (e.name == 'NS_ERROR_DOM_SECURITY_ERR' || e.name == 'SecurityError') {
 
3271
                    passed = YES | DISABLED;
 
3272
                }
 
3273
            }
 
3274
 
 
3275
            results.addItem({
 
3276
                key: 'storage.localStorage',
 
3277
                passed: passed
 
3278
            });
 
3279
        },
 
3280
 
 
3281
 
 
3282
        /* indexeddb */
 
3283
 
 
3284
        function (results) {
 
3285
            var indexedDB;
 
3286
            var passed = false;
 
3287
 
 
3288
            try {
 
3289
                indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.moz_indexedDB || window.oIndexedDB || window.msIndexedDB;
 
3290
                passed = !!window.indexedDB ? YES : !!window.webkitIndexedDB || !!window.mozIndexedDB || !!window.moz_indexedDB || !!window.oIndexedDB || !!window.msIndexedDB ? YES | PREFIX : NO;
 
3291
 
 
3292
                if (indexedDB && ! 'deleteDatabase' in indexedDB) {
 
3293
                    passed |= BUGGY;
 
3294
                    log('BUGGY: missing deleteDatabase function on indexedDB');
 
3295
                }
 
3296
            } catch (e) {
 
3297
                /* If we get a security exception we know the feature exists, but cookies are disabled */
 
3298
                if (e.name == 'NS_ERROR_DOM_SECURITY_ERR' || e.name == 'SecurityError') {
 
3299
                    passed = YES | DISABLED;
 
3300
                }
 
3301
            }
 
3302
 
 
3303
            results.addItem({
 
3304
                key: 'storage.indexedDB.basic',
 
3305
                passed: passed
 
3306
            });
 
3307
 
 
3308
            /* indexeddb blob and arraybuffer storage */
 
3309
 
 
3310
            var blobitem = results.addItem({
 
3311
                key: 'storage.indexedDB.blob',
 
3312
                passed: passed & DISABLED || passed & BUGGY ? NO | UNKNOWN : NO
 
3313
            });
 
3314
 
 
3315
            var arrayitem = results.addItem({
 
3316
                key: 'storage.indexedDB.arraybuffer',
 
3317
                passed: passed & DISABLED || passed & BUGGY ? NO | UNKNOWN : NO
 
3318
            });
 
3319
 
 
3320
            if (indexedDB && 'deleteDatabase' in indexedDB) {
 
3321
                try {
 
3322
                    blobitem.startBackground();
 
3323
                    arrayitem.startBackground();
 
3324
 
 
3325
                    var request = indexedDB.deleteDatabase('html5test');
 
3326
 
 
3327
                    request.onerror = function (e) {
 
3328
                        blobitem.stopBackground();
 
3329
                        arrayitem.stopBackground();
 
3330
                    };
 
3331
 
 
3332
                    request.onsuccess = function () {
 
3333
                        var request = indexedDB.open('html5test', 1);
 
3334
 
 
3335
                        request.onupgradeneeded = function () {
 
3336
                            request.result.createObjectStore("store");
 
3337
                        };
 
3338
 
 
3339
                        request.onerror = function (event) {
 
3340
                            blobitem.stopBackground();
 
3341
                            arrayitem.stopBackground();
 
3342
                        };
 
3343
 
 
3344
                        request.onsuccess = function () {
 
3345
                            var db = request.result;
 
3346
 
 
3347
                            try {
 
3348
                                db.transaction("store", "readwrite").objectStore("store").put(new Blob(), "key");
 
3349
 
 
3350
                                blobitem.update({
 
3351
                                    passed: true
 
3352
                                });
 
3353
                            } catch (e) {
 
3354
                            }
 
3355
 
 
3356
                            try {
 
3357
                                db.transaction("store", "readwrite").objectStore("store").put(new ArrayBuffer(), "key");
 
3358
 
 
3359
                                arrayitem.update({
 
3360
                                    passed: true
 
3361
                                });
 
3362
 
 
3363
                            } catch (e) {
 
3364
                            }
 
3365
 
 
3366
                            blobitem.stopBackground();
 
3367
                            arrayitem.stopBackground();
 
3368
 
 
3369
                            db.close();
 
3370
                            indexedDB.deleteDatabase('html5test');
 
3371
                        };
 
3372
                    };
 
3373
                } catch (e) {
 
3374
                    blobitem.stopBackground();
 
3375
                    arrayitem.stopBackground();
 
3376
                }
 
3377
            }
 
3378
        },
 
3379
 
 
3380
 
 
3381
        /* websql */
 
3382
 
 
3383
        function (results) {
 
3384
            results.addItem({
 
3385
                key: 'storage.sqlDatabase',
 
3386
                passed: !!window.openDatabase
 
3387
            });
 
3388
        },
 
3389
 
 
3390
 
 
3391
        /* file reader */
 
3392
 
 
3393
        function (results) {
 
3394
            results.addItem({
 
3395
                key: 'files.fileReader',
 
3396
                passed: 'FileReader' in window
 
3397
            });
 
3398
 
 
3399
            /* file reader as blob */
 
3400
 
 
3401
            results.addItem({
 
3402
                key: 'files.fileReader.blob',
 
3403
                passed: 'Blob' in window
 
3404
            });
 
3405
 
 
3406
            /* file reader as data url */
 
3407
 
 
3408
            results.addItem({
 
3409
                key: 'files.fileReader.dataURL',
 
3410
                passed: 'FileReader' in window && 'readAsDataURL' in (new FileReader())
 
3411
            });
 
3412
 
 
3413
            /* file reader as array buffer */
 
3414
 
 
3415
            results.addItem({
 
3416
                key: 'files.fileReader.arraybuffer',
 
3417
                passed: 'FileReader' in window && 'readAsArrayBuffer' in (new FileReader())
 
3418
            });
 
3419
 
 
3420
            /* file reader as object url */
 
3421
 
 
3422
            results.addItem({
 
3423
                key: 'files.fileReader.objectURL',
 
3424
                passed: 'URL' in window && 'createObjectURL' in URL
 
3425
            });
 
3426
        },
 
3427
 
 
3428
 
 
3429
        /* request file system */
 
3430
 
 
3431
        function (results) {
 
3432
            results.addItem({
 
3433
                key: 'files.fileSystem',
 
3434
                passed: !!window.requestFileSystem ? YES : !!window.webkitRequestFileSystem || !!window.mozRequestFileSystem || !!window.oRequestFileSystem || !!window.msRequestFileSystem ? YES | PREFIX : NO
 
3435
            });
 
3436
        },
 
3437
 
 
3438
 
 
3439
        /* get file system */
 
3440
 
 
3441
        function (results) {
 
3442
            results.addItem({
 
3443
                key: 'files.getFileSystem',
 
3444
                passed: !!navigator.getFileSystem ? YES : !!navigator.webkitGetFileSystem || !!navigator.mozGetFileSystem || !!window.msGetFileSystem ? YES | PREFIX : NO
 
3445
            });
 
3446
        },
 
3447
 
 
3448
 
 
3449
        /* readable streams */
 
3450
 
 
3451
        function (results) {
 
3452
            results.addItem({
 
3453
                key: 'streams.readable',
 
3454
                passed: 'ReadableStream' in window
 
3455
            });
 
3456
 
 
3457
        },
 
3458
 
 
3459
 
 
3460
        /* writeable streams */
 
3461
 
 
3462
        function (results) {
 
3463
            results.addItem({
 
3464
                key: 'streams.writeable',
 
3465
                passed: 'WriteableStream' in window
 
3466
            });
 
3467
        },
 
3468
 
 
3469
 
 
3470
        /* custom elements */
 
3471
 
 
3472
        function (results) {
 
3473
            results.addItem({
 
3474
                key: 'components.custom',
 
3475
                passed: 'registerElement' in document
 
3476
            });
 
3477
        },
 
3478
 
 
3479
 
 
3480
        /* shadow dom */
 
3481
 
 
3482
        function (results) {
 
3483
            results.addItem({
 
3484
                key: 'components.shadowdom',
 
3485
                passed: 'attachShadow' in document.createElement('div') ? YES : 'createShadowRoot' in document.createElement('div') || 'webkitCreateShadowRoot' in document.createElement('div') ? YES | OLD : NO
 
3486
            });
 
3487
        },
 
3488
 
 
3489
 
 
3490
        /* templates */
 
3491
 
 
3492
        function (results) {
 
3493
            var passed = false;
 
3494
 
 
3495
            try {
 
3496
                passed = 'content' in document.createElement('template');
 
3497
            } catch (error) {
 
3498
            }
 
3499
 
 
3500
            results.addItem({
 
3501
                key: 'components.template',
 
3502
                passed: passed
 
3503
            });
 
3504
        },
 
3505
 
 
3506
 
 
3507
        /* html imports */
 
3508
 
 
3509
        function (results) {
 
3510
            results.addItem({
 
3511
                key: 'components.imports',
 
3512
                passed: 'import' in document.createElement('link')
 
3513
            });
 
3514
        },
 
3515
 
 
3516
 
 
3517
        /* async scripts */
 
3518
 
 
3519
        function (results) {
 
3520
            results.addItem({
 
3521
                key: 'scripting.async',
 
3522
                passed: 'async' in document.createElement('script')
 
3523
            });
 
3524
        },
 
3525
 
 
3526
 
 
3527
        /* deferred scripts */
 
3528
 
 
3529
        function (results) {
 
3530
            results.addItem({
 
3531
                key: 'scripting.defer',
 
3532
                passed: 'defer' in document.createElement('script')
 
3533
            });
 
3534
        },
 
3535
 
 
3536
 
 
3537
        /* script error reporting */
 
3538
 
 
3539
        function (results) {
 
3540
            results.addItem({
 
3541
                key: 'scripting.onerror',
 
3542
                passed: isEventSupported('error')
 
3543
            });
 
3544
        },
 
3545
 
 
3546
 
 
3547
        /* script execution events */
 
3548
 
 
3549
        function (results) {
 
3550
            var executionevents = results.addItem({
 
3551
                key: 'scripting.executionevents',
 
3552
                passed: false
 
3553
            });
 
3554
 
 
3555
            executionevents.startBackground();
 
3556
 
 
3557
            var before = false;
 
3558
 
 
3559
            var s = document.createElement('script');
 
3560
            s.src = "data:text/javascript;charset=utf-8,window"
 
3561
 
 
3562
            s.addEventListener('beforescriptexecute', function () {
 
3563
                before = true;
 
3564
            }, true);
 
3565
 
 
3566
            s.addEventListener('afterscriptexecute', function () {
 
3567
                if (before) {
 
3568
                    executionevents.update({
 
3569
                        passed: true
 
3570
                    });
 
3571
                }
 
3572
 
 
3573
                executionevents.stopBackground();
 
3574
            }, true);
 
3575
 
 
3576
            document.body.appendChild(s);
 
3577
 
 
3578
            window.setTimeout(function () {
 
3579
                executionevents.stopBackground();
 
3580
            }, 500);
 
3581
        },
 
3582
 
 
3583
 
 
3584
        /* base64 encoding and decoding */
 
3585
 
 
3586
        function (results) {
 
3587
            results.addItem({
 
3588
                key: 'scripting.base64',
 
3589
                passed: 'btoa' in window && 'atob' in window
 
3590
            });
 
3591
        },
 
3592
 
 
3593
 
 
3594
        /* mutation observer */
 
3595
 
 
3596
        function (results) {
 
3597
            results.addItem({
 
3598
                key: 'scripting.mutationObserver',
 
3599
                passed: 'MutationObserver' in window ? YES : 'WebKitMutationObserver' in window || 'MozMutationObserver' in window || 'oMutationObserver' in window || 'msMutationObserver' in window ? YES | PREFIX : NO
 
3600
            });
 
3601
        },
 
3602
 
 
3603
 
 
3604
        /* url api */
 
3605
 
 
3606
        function (results) {
 
3607
            results.addItem({
 
3608
                key: 'scripting.url',
 
3609
                passed: 'URL' in window ? YES : 'WebKitURL' in window || 'MozURL' in window || 'oURL' in window || 'msURL' in window ? YES | PREFIX : NO
 
3610
            });
 
3611
        },
 
3612
 
 
3613
 
 
3614
        /* text encoding api */
 
3615
 
 
3616
        function (results) {
 
3617
            results.addItem({
 
3618
                key: 'scripting.encoding',
 
3619
                passed: 'TextEncoder' in window && 'TextDecoder' in window ? YES : NO
 
3620
            });
 
3621
        },
 
3622
 
 
3623
 
 
3624
        /* json encoding and decoding */
 
3625
 
 
3626
        function (results) {
 
3627
            results.addItem({
 
3628
                key: 'scripting.es5.json',
 
3629
                passed: 'JSON' in window && 'parse' in JSON
 
3630
            });
 
3631
        },
 
3632
 
 
3633
 
 
3634
        /* array functions */
 
3635
 
 
3636
        function (results) {
 
3637
            var passed = !!(Array.prototype &&
 
3638
                Array.prototype.every &&
 
3639
                Array.prototype.filter &&
 
3640
                Array.prototype.forEach &&
 
3641
                Array.prototype.indexOf &&
 
3642
                Array.prototype.lastIndexOf &&
 
3643
                Array.prototype.map &&
 
3644
                Array.prototype.some &&
 
3645
                Array.prototype.reduce &&
 
3646
                Array.prototype.reduceRight &&
 
3647
                Array.isArray)
 
3648
 
 
3649
            results.addItem({
 
3650
                key: 'scripting.es5.array',
 
3651
                passed: passed ? YES : NO
 
3652
            });
 
3653
        },
 
3654
 
 
3655
 
 
3656
        /* date functions */
 
3657
 
 
3658
        function (results) {
 
3659
            var canParseISODate = false;
 
3660
 
 
3661
            try {
 
3662
                canParseISODate = !!Date.parse('2013-04-12T06:06:37.307Z');
 
3663
            } catch (e) {
 
3664
            }
 
3665
 
 
3666
            var passed = !!(Date.now &&
 
3667
                Date.prototype &&
 
3668
                Date.prototype.toISOString &&
 
3669
                Date.prototype.toJSON &&
 
3670
                canParseISODate);
 
3671
 
 
3672
            results.addItem({
 
3673
                key: 'scripting.es5.date',
 
3674
                passed: passed ? YES : NO
 
3675
            });
 
3676
        },
 
3677
 
 
3678
 
 
3679
        /* function functions */
 
3680
 
 
3681
        function (results) {
 
3682
            var passed = !!(Function.prototype && Function.prototype.bind);
 
3683
 
 
3684
            results.addItem({
 
3685
                key: 'scripting.es5.function',
 
3686
                passed: passed ? YES : NO
 
3687
            });
 
3688
        },
 
3689
 
 
3690
 
 
3691
        /* object functions */
 
3692
 
 
3693
        function (results) {
 
3694
            var passed = !!(Object.keys &&
 
3695
                Object.create &&
 
3696
                Object.getPrototypeOf &&
 
3697
                Object.getOwnPropertyNames &&
 
3698
                Object.isSealed &&
 
3699
                Object.isFrozen &&
 
3700
                Object.isExtensible &&
 
3701
                Object.getOwnPropertyDescriptor &&
 
3702
                Object.defineProperty &&
 
3703
                Object.defineProperties &&
 
3704
                Object.seal &&
 
3705
                Object.freeze &&
 
3706
                Object.preventExtensions);
 
3707
 
 
3708
            results.addItem({
 
3709
                key: 'scripting.es5.object',
 
3710
                passed: passed ? YES : NO
 
3711
            });
 
3712
        },
 
3713
 
 
3714
 
 
3715
        /* strict */
 
3716
 
 
3717
        function (results) {
 
3718
            var passed = (function() {'use strict'; return !this; })()
 
3719
 
 
3720
            results.addItem({
 
3721
                key: 'scripting.es5.strict',
 
3722
                passed: passed ? YES : NO
 
3723
            });
 
3724
        },
 
3725
 
 
3726
 
 
3727
        /* string functions */
 
3728
 
 
3729
        function (results) {
 
3730
            var passed = !!(String.prototype && String.prototype.trim)
 
3731
 
 
3732
            results.addItem({
 
3733
                key: 'scripting.es5.string',
 
3734
                passed: passed ? YES : NO
 
3735
            });
 
3736
        },
 
3737
 
 
3738
 
 
3739
        /* internationalisation api */
 
3740
 
 
3741
        function (results) {
 
3742
            results.addItem({
 
3743
                key: 'scripting.es6.i18n',
 
3744
                passed: 'Intl' in window ? YES : NO
 
3745
            });
 
3746
        },
 
3747
 
 
3748
 
 
3749
        /* promises */
 
3750
 
 
3751
        function (results) {
 
3752
            var passed = 'Promise' in window ? YES | OLD : NO;
 
3753
 
 
3754
            if ('Promise' in window &&
 
3755
                'resolve' in window.Promise &&
 
3756
                'reject' in window.Promise &&
 
3757
                'all' in window.Promise &&
 
3758
                'race' in window.Promise &&
 
3759
                (function () {
 
3760
                    var resolve;
 
3761
                    new window.Promise(function (r) { resolve = r; });
 
3762
                    return typeof resolve === 'function';
 
3763
                } ())) {
 
3764
                passed = YES;
 
3765
            }
 
3766
 
 
3767
            results.addItem({
 
3768
                key: 'scripting.es6.promises',
 
3769
                passed: passed
 
3770
            });
 
3771
        },
 
3772
 
 
3773
 
 
3774
        /* const */
 
3775
 
 
3776
        function (results) {
 
3777
            var passed = YES;
 
3778
 
 
3779
            try {
 
3780
                eval('const a = 1');
 
3781
            } catch (e) {
 
3782
                passed = NO;
 
3783
            }
 
3784
 
 
3785
            results.addItem({
 
3786
                key: 'scripting.es6.const',
 
3787
                passed: passed
 
3788
            });
 
3789
        },
 
3790
 
 
3791
 
 
3792
        /* let */
 
3793
 
 
3794
        function (results) {
 
3795
            var passed = YES;
 
3796
 
 
3797
            try {
 
3798
                eval('let a = 1');
 
3799
            } catch (e) {
 
3800
                passed = NO;
 
3801
            }
 
3802
 
 
3803
            results.addItem({
 
3804
                key: 'scripting.es6.let',
 
3805
                passed: passed
 
3806
            });
 
3807
        },
 
3808
 
 
3809
 
 
3810
        /* arrow functions */
 
3811
 
 
3812
        function (results) {
 
3813
            var passed = YES;
 
3814
 
 
3815
            try {
 
3816
                eval('()=>{}');
 
3817
            } catch (e) {
 
3818
                passed = NO;
 
3819
            }
 
3820
 
 
3821
            results.addItem({
 
3822
                key: 'scripting.es6.arrow',
 
3823
                passed: passed
 
3824
            });
 
3825
        },
 
3826
 
 
3827
 
 
3828
        /* classes */
 
3829
 
 
3830
        function (results) {
 
3831
            var passed = YES;
 
3832
 
 
3833
            try {
 
3834
                eval('class Something {}');
 
3835
            } catch (e) {
 
3836
                passed = NO;
 
3837
            }
 
3838
 
 
3839
            results.addItem({
 
3840
                key: 'scripting.es6.class',
 
3841
                passed: passed
 
3842
            });
 
3843
        },
 
3844
 
 
3845
 
 
3846
        /* generators */
 
3847
 
 
3848
        function (results) {
 
3849
            var passed = YES;
 
3850
 
 
3851
            try {
 
3852
                eval('function* test() {}');
 
3853
            } catch (e) {
 
3854
                passed = NO;
 
3855
            }
 
3856
 
 
3857
            results.addItem({
 
3858
                key: 'scripting.es6.generators',
 
3859
                passed: passed
 
3860
            });
 
3861
        },
 
3862
 
 
3863
 
 
3864
        /* template strings */
 
3865
 
 
3866
        function (results) {
 
3867
            var passed = YES;
 
3868
 
 
3869
            try {
 
3870
                eval('var a = `a`');
 
3871
            } catch (e) {
 
3872
                passed = NO;
 
3873
            }
 
3874
 
 
3875
            results.addItem({
 
3876
                key: 'scripting.es6.template',
 
3877
                passed: passed
 
3878
            });
 
3879
        },
 
3880
 
 
3881
 
 
3882
        /* destructuring */
 
3883
 
 
3884
        function (results) {
 
3885
            var passed = YES;
 
3886
 
 
3887
            try {
 
3888
                eval('var { first: f, last: l } = { first: "Jane", last: "Doe" }');
 
3889
            } catch (e) {
 
3890
                passed = NO;
 
3891
            }
 
3892
 
 
3893
            results.addItem({
 
3894
                key: 'scripting.es6.destructuring',
 
3895
                passed: passed
 
3896
            });
 
3897
        },
 
3898
 
 
3899
 
 
3900
        /* spread */
 
3901
 
 
3902
        function (results) {
 
3903
            var passed = YES;
 
3904
 
 
3905
            try {
 
3906
                eval('Math.max(...[ 5, 10 ])');
 
3907
            } catch (e) {
 
3908
                passed = NO;
 
3909
            }
 
3910
 
 
3911
            results.addItem({
 
3912
                key: 'scripting.es6.spread',
 
3913
                passed: passed
 
3914
            });
 
3915
        },
 
3916
 
 
3917
 
 
3918
        /* default params */
 
3919
 
 
3920
        function (results) {
 
3921
            var passed = YES;
 
3922
 
 
3923
            try {
 
3924
                eval('function test (one = 1) {}');
 
3925
            } catch (e) {
 
3926
                passed = NO;
 
3927
            }
 
3928
 
 
3929
            results.addItem({
 
3930
                key: 'scripting.es6.defaultparams',
 
3931
                passed: passed
 
3932
            });
 
3933
        },
 
3934
 
 
3935
 
 
3936
        /* symbols */
 
3937
 
 
3938
        function (results) {
 
3939
            results.addItem({
 
3940
                key: 'scripting.es6.symbol',
 
3941
                passed: typeof Symbol !== 'undefined' ? YES : NO
 
3942
            });
 
3943
        },
 
3944
 
 
3945
 
 
3946
        /* collections */
 
3947
 
 
3948
        function (results) {
 
3949
            var passed = typeof Map !== 'undefined' &&
 
3950
                typeof WeakMap !== 'undefined' &&
 
3951
                typeof Set !== 'undefined' &&
 
3952
                typeof WeakSet !== 'undefined';
 
3953
 
 
3954
            results.addItem({
 
3955
                key: 'scripting.es6.collections',
 
3956
                passed: passed ? YES : NO
 
3957
            });
 
3958
        },
 
3959
 
 
3960
 
 
3961
        /* array functions */
 
3962
 
 
3963
        function (results) {
 
3964
            var passed = typeof Array.prototype.find !== 'undefined' &&
 
3965
                typeof Array.prototype.findIndex !== 'undefined' &&
 
3966
                typeof Array.from !== 'undefined' &&
 
3967
                typeof Array.of !== 'undefined' &&
 
3968
                typeof Array.prototype.entries !== 'undefined' &&
 
3969
                typeof Array.prototype.keys !== 'undefined' &&
 
3970
                typeof Array.prototype.copyWithin !== 'undefined' &&
 
3971
                typeof Array.prototype.fill !== 'undefined';
 
3972
 
 
3973
            results.addItem({
 
3974
                key: 'scripting.es6.array',
 
3975
                passed: passed ? YES : NO
 
3976
            });
 
3977
        },
 
3978
 
 
3979
 
 
3980
        /* string functions */
 
3981
 
 
3982
        function (results) {
 
3983
            var passed = typeof String.fromCodePoint !== 'undefined' &&
 
3984
                typeof String.raw !== 'undefined' &&
 
3985
                typeof String.prototype.codePointAt !== 'undefined' &&
 
3986
                typeof String.prototype.repeat !== 'undefined' &&
 
3987
                typeof String.prototype.startsWith !== 'undefined' &&
 
3988
                typeof String.prototype.endsWith !== 'undefined' &&
 
3989
                typeof String.prototype.includes !== 'undefined';
 
3990
 
 
3991
            results.addItem({
 
3992
                key: 'scripting.es6.string',
 
3993
                passed: passed ? YES : NO
 
3994
            });
 
3995
        },
 
3996
 
 
3997
 
 
3998
        /* number functions */
 
3999
 
 
4000
        function (results) {
 
4001
            var passed = !!(Number.isFinite &&
 
4002
                Number.isInteger &&
 
4003
                Number.isSafeInteger &&
 
4004
                Number.isNaN &&
 
4005
                Number.parseInt &&
 
4006
                Number.parseFloat &&
 
4007
                Number.isInteger(Number.MAX_SAFE_INTEGER) &&
 
4008
                Number.isInteger(Number.MIN_SAFE_INTEGER) &&
 
4009
                Number.isFinite(Number.EPSILON));
 
4010
 
 
4011
            results.addItem({
 
4012
                key: 'scripting.es6.number',
 
4013
                passed: passed ? YES : NO
 
4014
            });
 
4015
        },
 
4016
 
 
4017
 
 
4018
        /* object functions */
 
4019
 
 
4020
        function (results) {
 
4021
            var passed = !!(Object.assign &&
 
4022
                Object.is &&
 
4023
                Object.setPrototypeOf);
 
4024
 
 
4025
            results.addItem({
 
4026
                key: 'scripting.es6.object',
 
4027
                passed: passed ? YES : NO
 
4028
            });
 
4029
        },
 
4030
 
 
4031
 
 
4032
        /* math functions */
 
4033
 
 
4034
        function (results) {
 
4035
            var passed = !!(Math &&
 
4036
                Math.clz32 &&
 
4037
                Math.cbrt &&
 
4038
                Math.imul &&
 
4039
                Math.sign &&
 
4040
                Math.log10 &&
 
4041
                Math.log2 &&
 
4042
                Math.log1p &&
 
4043
                Math.expm1 &&
 
4044
                Math.cosh &&
 
4045
                Math.sinh &&
 
4046
                Math.tanh &&
 
4047
                Math.acosh &&
 
4048
                Math.asinh &&
 
4049
                Math.atanh &&
 
4050
                Math.hypot &&
 
4051
                Math.trunc &&
 
4052
                Math.fround);
 
4053
 
 
4054
            results.addItem({
 
4055
                key: 'scripting.es6.math',
 
4056
                passed: passed ? YES : NO
 
4057
            });
 
4058
        },
 
4059
 
 
4060
 
 
4061
        /* datatypes */
 
4062
 
 
4063
        function (results) {
 
4064
            results.addItem({
 
4065
                key: 'scripting.es6.datatypes.ArrayBuffer',
 
4066
                passed: typeof ArrayBuffer != 'undefined'
 
4067
            });
 
4068
 
 
4069
            results.addItem({
 
4070
                key: 'scripting.es6.datatypes.Int8Array',
 
4071
                passed: typeof Int8Array != 'undefined'
 
4072
            });
 
4073
 
 
4074
            results.addItem({
 
4075
                key: 'scripting.es6.datatypes.Uint8Array',
 
4076
                passed: typeof Uint8Array != 'undefined'
 
4077
            });
 
4078
 
 
4079
            results.addItem({
 
4080
                key: 'scripting.es6.datatypes.Uint8ClampedArray',
 
4081
                passed: typeof Uint8ClampedArray != 'undefined'
 
4082
            });
 
4083
 
 
4084
            results.addItem({
 
4085
                key: 'scripting.es6.datatypes.Int16Array',
 
4086
                passed: typeof Int16Array != 'undefined'
 
4087
            });
 
4088
 
 
4089
            results.addItem({
 
4090
                key: 'scripting.es6.datatypes.Uint16Array',
 
4091
                passed: typeof Uint16Array != 'undefined'
 
4092
            });
 
4093
 
 
4094
            results.addItem({
 
4095
                key: 'scripting.es6.datatypes.Int32Array',
 
4096
                passed: typeof Int32Array != 'undefined'
 
4097
            });
 
4098
 
 
4099
            results.addItem({
 
4100
                key: 'scripting.es6.datatypes.Uint32Array',
 
4101
                passed: typeof Uint32Array != 'undefined'
 
4102
            });
 
4103
 
 
4104
            results.addItem({
 
4105
                key: 'scripting.es6.datatypes.Float32Array',
 
4106
                passed: typeof Float32Array != 'undefined'
 
4107
            });
 
4108
 
 
4109
            results.addItem({
 
4110
                key: 'scripting.es6.datatypes.Float64Array',
 
4111
                passed: typeof Float64Array != 'undefined'
 
4112
            });
 
4113
 
 
4114
            results.addItem({
 
4115
                key: 'scripting.es6.datatypes.DataView',
 
4116
                passed: typeof DataView != 'undefined'
 
4117
            });
 
4118
 
 
4119
 
 
4120
            var passed = typeof ArrayBuffer != 'undefined' &&
 
4121
                typeof Int8Array != 'undefined' &&
 
4122
                typeof Uint8Array != 'undefined' &&
 
4123
                typeof Uint8ClampedArray != 'undefined' &&
 
4124
                typeof Int16Array != 'undefined' &&
 
4125
                typeof Uint16Array != 'undefined' &&
 
4126
                typeof Int32Array != 'undefined' &&
 
4127
                typeof Uint32Array != 'undefined' &&
 
4128
                typeof Float32Array != 'undefined' &&
 
4129
                typeof Float64Array != 'undefined' &&
 
4130
                typeof DataView != 'undefined';
 
4131
 
 
4132
            results.addItem({
 
4133
                key: 'scripting.es6.datatypes',
 
4134
                passed: passed ? YES : NO
 
4135
            });
 
4136
        },
 
4137
 
 
4138
 
 
4139
        /* modules */
 
4140
 
 
4141
        function (results) {
 
4142
            var item = results.addItem({
 
4143
                key: 'scripting.es6.modules',
 
4144
                passed: false
 
4145
            });
 
4146
 
 
4147
            item.startBackground();
 
4148
 
 
4149
            var callback = item.getGlobalCallback(function(scoped) {
 
4150
                item.update({
 
4151
                    passed: scoped ? YES : YES | BUGGY
 
4152
                });
 
4153
 
 
4154
                if (!scoped) {
 
4155
                    log('BUGGY: Non-exported variables are not scoped to the ES6 module');
 
4156
                }
 
4157
 
 
4158
                item.stopBackground();
 
4159
            });
 
4160
 
 
4161
            var s = document.createElement('script');
 
4162
            s.type = 'module';
 
4163
            s.src = "data:text/javascript;charset=utf-8,var test_module_scope = true; window." + callback + "(typeof window.test_module_scope === 'undefined')";
 
4164
            document.body.appendChild(s);
 
4165
 
 
4166
            window.setTimeout(function () {
 
4167
                item.stopBackground();
 
4168
            }, 500);
 
4169
        },
 
4170
 
 
4171
 
 
4172
        /* async await api */
 
4173
 
 
4174
        function (results) {
 
4175
            var passed = YES;
 
4176
 
 
4177
            try {
 
4178
                eval('async function a() { return await Promise.resolve() }');
 
4179
            } catch (e) {
 
4180
                passed = NO;
 
4181
            }
 
4182
 
 
4183
            results.addItem({
 
4184
                key: 'scripting.es7.async',
 
4185
                passed: passed
 
4186
            });
 
4187
        },
 
4188
 
 
4189
 
 
4190
        /* page visiblity */
 
4191
 
 
4192
        function (results) {
 
4193
            results.addItem({
 
4194
                key: 'other.pagevisiblity',
 
4195
                passed: 'visibilityState' in document ? YES : 'webkitVisibilityState' in document || 'mozVisibilityState' in document || 'oVisibilityState' in document || 'msVisibilityState' in document ? YES | PREFIX : NO
 
4196
            });
 
4197
        },
 
4198
 
 
4199
 
 
4200
        /* selection */
 
4201
 
 
4202
        function (results) {
 
4203
            results.addItem({
 
4204
                key: 'other.getSelection',
 
4205
                passed: !!window.getSelection
 
4206
            });
 
4207
        },
 
4208
 
 
4209
 
 
4210
        /* scrollIntoView */
 
4211
 
 
4212
        function (results) {
 
4213
            results.addItem({
 
4214
                key: 'other.scrollIntoView',
 
4215
                passed: 'scrollIntoView' in document.createElement('div')
 
4216
            });
 
4217
        }
 
4218
    ];
 
4219
 
 
4220
 
 
4221
 
 
4222
 
 
4223
    /* Helper functions */
 
4224
 
 
4225
    var isEventSupported = (function () {
 
4226
        var TAGNAMES = {
 
4227
            'select': 'input', 'change': 'input', 'input': 'input',
 
4228
            'submit': 'form', 'reset': 'form', 'forminput': 'form', 'formchange': 'form',
 
4229
            'error': 'img', 'load': 'img', 'abort': 'img'
 
4230
        }
 
4231
 
 
4232
        function isEventSupported(eventName, element) {
 
4233
            element = element || document.createElement(TAGNAMES[eventName] || 'div');
 
4234
            eventName = 'on' + eventName;
 
4235
 
 
4236
            var isSupported = (eventName in element);
 
4237
 
 
4238
            if (!isSupported) {
 
4239
                if (!element.setAttribute) {
 
4240
                    element = document.createElement('div');
 
4241
                }
 
4242
                if (element.setAttribute && element.removeAttribute) {
 
4243
                    element.setAttribute(eventName, '');
 
4244
                    isSupported = typeof element[eventName] == 'function';
 
4245
 
 
4246
                    if (typeof element[eventName] != 'undefined') {
 
4247
                        element[eventName] = void 0;
 
4248
                    }
 
4249
                    element.removeAttribute(eventName);
 
4250
                }
 
4251
            }
 
4252
 
 
4253
            element = null;
 
4254
            return isSupported;
 
4255
        }
 
4256
 
 
4257
        return isEventSupported;
 
4258
    })();
 
4259
 
 
4260
    var log = function () {
 
4261
        if (typeof console != 'undefined') {
 
4262
            console.log.apply(console, arguments);
 
4263
        }
 
4264
    };
 
4265
 
 
4266
    var createInput = function (type) {
 
4267
        var field = document.createElement('input');
 
4268
 
 
4269
        try {
 
4270
            field.setAttribute('type', type);
 
4271
        } catch (e) {
 
4272
        }
 
4273
 
 
4274
        return field;
 
4275
    };
 
4276
 
 
4277
    var canPlayType = function (element, type) {
 
4278
                /*
 
4279
                        There is a bug in iOS 4.1 or earlier where probably and maybe are switched around.
 
4280
                        This bug was reported and fixed in iOS 4.2
 
4281
                */
 
4282
 
 
4283
        if (Browsers.isOs('iOS', '<', '4.2'))
 
4284
            return element.canPlayType(type) == 'probably' || element.canPlayType(type) == 'maybe';
 
4285
        else
 
4286
            return element.canPlayType(type) == 'probably';
 
4287
    };
 
4288
 
 
4289
    var closesImplicitly = function (name) {
 
4290
        var foo = document.createElement('div');
 
4291
        foo.innerHTML = '<p><' + name + '></' + name + '>';
 
4292
        return foo.childNodes.length == 2;
 
4293
    };
 
4294
 
 
4295
    var getStyle = function (element, name) {
 
4296
        function camelCase(str) {
 
4297
            return str.replace(/-\D/g, function (match) {
 
4298
                return match.charAt(1).toUpperCase()
 
4299
            })
 
4300
        }
 
4301
 
 
4302
        if (element.style[name]) {
 
4303
            return element.style[name];
 
4304
        } else if (element.currentStyle) {
 
4305
            return element.currentStyle[camelCase(name)];
 
4306
        }
 
4307
        else if (document.defaultView && document.defaultView.getComputedStyle) {
 
4308
            s = document.defaultView.getComputedStyle(element, "");
 
4309
            return s && s.getPropertyValue(name);
 
4310
        } else {
 
4311
            return null;
 
4312
        }
 
4313
    };
 
4314
 
 
4315
    var isBlock = function (element) {
 
4316
        return getStyle(element, 'display') == 'block';
 
4317
    };
 
4318
 
 
4319
    var isHidden = function (element) {
 
4320
        return getStyle(element, 'display') == 'none';
 
4321
    };
 
4322
 
 
4323
 
 
4324
 
 
4325
 
 
4326
    /* Classes */
 
4327
 
 
4328
    function List(parent) { this.initialize(parent); }
 
4329
    List.prototype = {
 
4330
        initialize: function (parent) {
 
4331
            this.parent = parent;
 
4332
            this.items = [];
 
4333
        },
 
4334
 
 
4335
        addItem: function (data) {
 
4336
            var i = new Item(this, data);
 
4337
            this.items.push(i);
 
4338
            return i;
 
4339
        },
 
4340
 
 
4341
        toString: function () {
 
4342
            var value = [];
 
4343
 
 
4344
            for (var i = 0; i < this.items.length; i++) {
 
4345
                if (typeof this.items[i].data.passed != 'undefined') value.push(this.items[i].data.key + '=' + (+this.items[i].data.passed));
 
4346
            }
 
4347
 
 
4348
            return value.join(',');
 
4349
        }
 
4350
    };
 
4351
 
 
4352
    function Item(list, data) { this.initialize(list, data); }
 
4353
    Item.prototype = {
 
4354
        initialize: function (list, data) {
 
4355
            this.list = list;
 
4356
            this.data = data;
 
4357
 
 
4358
            if (typeof this.data.passed == 'undefined') this.data.passed = false;
 
4359
 
 
4360
            if (this.data.passed) {
 
4361
                var blacklist = this.isOnBlacklist();
 
4362
                if (blacklist) {
 
4363
                    this.data.passed = blacklist;
 
4364
                }
 
4365
            }
 
4366
        },
 
4367
 
 
4368
        update: function (data) {
 
4369
            for (var key in data) {
 
4370
                this.data[key] = data[key];
 
4371
            }
 
4372
 
 
4373
            if (typeof this.data.passed == 'undefined') this.data.passed = false;
 
4374
 
 
4375
            if (this.data.passed) {
 
4376
                var blacklist = this.isOnBlacklist();
 
4377
                if (blacklist) {
 
4378
                    this.data.passed = blacklist;
 
4379
                }
 
4380
            }
 
4381
        },
 
4382
 
 
4383
        isOnBlacklist: function () {
 
4384
            var part = '';
 
4385
            var parts = this.data.key.replace(/\-/g, '.').split('.');
 
4386
 
 
4387
            for (var i = 0; i < parts.length; i++) {
 
4388
                part += (i == 0 ? '' : '.') + parts[i];
 
4389
 
 
4390
                for (var k = 0; k < blacklists.length; k++) {
 
4391
                    if (typeof blacklists[k][1][part] != 'undefined') {
 
4392
                        if (blacklists[k][1][part]) {
 
4393
                            log('BLOCKED: ' + part + ' is on the blacklist for this browser!');
 
4394
                            return blacklists[k][0];
 
4395
                        }
 
4396
                    }
 
4397
                }
 
4398
            }
 
4399
 
 
4400
            return false;
 
4401
        },
 
4402
 
 
4403
        startBackground: function () {
 
4404
            this.list.parent.startBackground(this.data.key);
 
4405
        },
 
4406
 
 
4407
        stopBackground: function () {
 
4408
            this.list.parent.stopBackground(this.data.key);
 
4409
        },
 
4410
 
 
4411
        getGlobalCallback: function(callback) {
 
4412
            var uniqueid = (((1 + Math.random()) * 0x1000000) | 0).toString(16).substring(1);
 
4413
 
 
4414
            var that = this;
 
4415
            window['callback_' + uniqueid] = function() {
 
4416
                callback.apply(that, arguments);
 
4417
            };
 
4418
 
 
4419
            return 'callback_' + uniqueid;
 
4420
        }
 
4421
    };
 
4422
 
 
4423
    function Runner(callback, error) { this.initialize(callback, error); }
 
4424
    Runner.prototype = {
 
4425
        initialize: function (callback, error) {
 
4426
            blacklists = [
 
4427
                [
 
4428
                    BLOCKED,
 
4429
                    {
 
4430
                        'form.file': Browsers.isDevice('Xbox 360') || Browsers.isDevice('Xbox One') || Browsers.isDevice('Playstation 4') || Browsers.isOs('Windows Phone', '<', '8.1') || Browsers.isOs('iOS', '<', '6') || Browsers.isOs('Android', '<', '2.2'),
 
4431
                        'form.date.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || (Browsers.isBrowser('UC Browser', '<', '8.6') && Browsers.isType('mobile', 'tablet')),
 
4432
                        'form.month.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || (Browsers.isBrowser('UC Browser', '<', '8.6') && Browsers.isType('mobile', 'tablet')),
 
4433
                        'form.week.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || (Browsers.isBrowser('UC Browser', '<', '8.6') && Browsers.isType('mobile', 'tablet')),
 
4434
                        'form.time.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || (Browsers.isBrowser('UC Browser', '<', '8.6') && Browsers.isType('mobile', 'tablet')),
 
4435
                        'form.datetime-local.ui': Browsers.isBrowser('Sogou Explorer') || Browsers.isBrowser('Maxthon', '<', '4.0.5') || (Browsers.isBrowser('UC Browser', '<', '8.6') && Browsers.isType('mobile', 'tablet')),
 
4436
                        'form.color.ui': Browsers.isBrowser('Sogou Explorer') || (Browsers.isBrowser('UC Browser', '<', '9.8') && Browsers.isType('mobile', 'tablet')),
 
4437
                        'form.range.ui': (Browsers.isBrowser('UC Browser', '<', '9.8') && Browsers.isType('mobile', 'tablet')),
 
4438
                        'form.progress.element': Browsers.isBrowser('Baidu Browser'),
 
4439
                        'files.fileSystem': Browsers.isOs('BlackBerry Tablet OS'),
 
4440
                        'input.getUserMedia': Browsers.isDevice('webOS TV') || Browsers.isBrowser('Baidu Browser') || Browsers.isBrowser('Sogou Explorer') || (Browsers.isBrowser('UC Browser', '<', '9.8') && Browsers.isType('mobile', 'tablet')) || Browsers.isBrowser('Dolphin') || Browsers.isBrowser('Safari', '=', '9'),
 
4441
                        'input.getGamepads': Browsers.isDevice('webOS TV') || Browsers.isDevice('Playstation 4') || Browsers.isDevice('Wii U'),
 
4442
                        'location.geolocation': Browsers.isDevice('webOS TV') || Browsers.isDevice('Xbox One') || Browsers.isBrowser('Baidu Browser') || Browsers.isOs('Google TV'),
 
4443
                        'location.orientation': Browsers.isBrowser('Baidu Browser'),
 
4444
                        'output.notifications': Browsers.isBrowser('Opera', '=', '18') || Browsers.isBrowser('Baidu Browser') || Browsers.isBrowser('Sogou Explorer'),
 
4445
                        'output.requestFullScreen': Browsers.isBrowser('Sogou Explorer') || Browsers.isOs('BlackBerry Tablet OS') || Browsers.isOs('BlackBerry OS'),
 
4446
                        'video.subtitle': Browsers.isBrowser('Baidu Browser') || Browsers.isBrowser('Sogou Explorer'),
 
4447
                        '3d.webgl': Browsers.isBrowser('Baidu Browser')
 
4448
                    }
 
4449
                ],
 
4450
 
 
4451
                [
 
4452
                    DISABLED,
 
4453
                    {
 
4454
                        'elements.semantic.ping': Browsers.isBrowser('Firefox') || Browsers.isBrowser('Firefox Mobile')
 
4455
                    }
 
4456
                ],
 
4457
 
 
4458
                [
 
4459
                    UNCONFIRMED,
 
4460
                    {
 
4461
                        'interaction.dragdrop': !(Browsers.isType('desktop') ||
 
4462
                            Browsers.isType('mobile', 'tablet', 'media') && (
 
4463
                                Browsers.isBrowser('Opera') && Browsers.isEngine('Presto')
 
4464
                            ) ||
 
4465
                            Browsers.isType('television') && (
 
4466
                                Browsers.isDevice('webOS TV')
 
4467
                            )
 
4468
                        ),
 
4469
 
 
4470
                        'interaction.editing': !(Browsers.isType('desktop') ||
 
4471
                            Browsers.isType('mobile', 'tablet', 'media') && (
 
4472
                                Browsers.isOs('iOS', '>=', '5') ||
 
4473
                                Browsers.isOs('Android', '>=', '4') ||
 
4474
                                Browsers.isOs('Windows Phone', '>=', '7.5') ||
 
4475
                                Browsers.isOs('BlackBerry') ||
 
4476
                                Browsers.isOs('BlackBerry OS') ||
 
4477
                                Browsers.isOs('BlackBerry Tablet OS') ||
 
4478
                                Browsers.isOs('Meego') ||
 
4479
                                Browsers.isOs('Tizen') ||
 
4480
                                Browsers.isEngine('Gecko') ||
 
4481
                                Browsers.isEngine('Presto') ||
 
4482
                                Browsers.isBrowser('Chrome') ||
 
4483
                                Browsers.isBrowser('Polaris', '>=', '8')
 
4484
                            ) ||
 
4485
                            Browsers.isType('television') && (
 
4486
                                Browsers.isOs('Tizen') ||
 
4487
                                Browsers.isDevice('webOS TV') ||
 
4488
                                Browsers.isBrowser('Espial') ||
 
4489
                                Browsers.isBrowser('MachBlue XT') ||
 
4490
                                Browsers.isEngine('Presto', '>=', '2.9')
 
4491
                            ) ||
 
4492
                            Browsers.isType('gaming') && (
 
4493
                                Browsers.isDevice('Xbox 360') ||
 
4494
                                Browsers.isDevice('Xbox One') ||
 
4495
                                Browsers.isDevice('Playstation 4')
 
4496
                            )
 
4497
                        )
 
4498
                    }
 
4499
                ]
 
4500
            ];
 
4501
 
 
4502
            try {
 
4503
                this.backgroundTasks = [];
 
4504
                this.backgroundIds = {};
 
4505
                this.backgroundId = 0;
 
4506
 
 
4507
                this.callback = callback;
 
4508
 
 
4509
                this.list = new List(this);
 
4510
 
 
4511
                for (var s = 0; s < testsuite.length; s++) {
 
4512
                    testsuite[s](this.list);
 
4513
                }
 
4514
 
 
4515
                this.waitForBackground();
 
4516
            }
 
4517
            catch (e) {
 
4518
                error(e);
 
4519
            }
 
4520
        },
 
4521
 
 
4522
        waitForBackground: function () {
 
4523
            var that = this;
 
4524
 
 
4525
            window.setTimeout(function () {
 
4526
                that.checkForBackground.call(that);
 
4527
            }, 300);
 
4528
        },
 
4529
 
 
4530
        checkForBackground: function () {
 
4531
            var running = 0;
 
4532
            for (var task = 0; task < this.backgroundTasks.length; task++) { running += this.backgroundTasks[task] }
 
4533
 
 
4534
            if (running) {
 
4535
                this.waitForBackground();
 
4536
            } else {
 
4537
                this.finished();
 
4538
            }
 
4539
        },
 
4540
 
 
4541
        startBackground: function (id) {
 
4542
            var i = this.backgroundId++;
 
4543
            this.backgroundIds[id] = i;
 
4544
            this.backgroundTasks[i] = 1;
 
4545
        },
 
4546
 
 
4547
        stopBackground: function (id) {
 
4548
            this.backgroundTasks[this.backgroundIds[id]] = 0;
 
4549
        },
 
4550
 
 
4551
        finished: function () {
 
4552
            var uniqueid = (((1 + Math.random()) * 0x1000000) | 0).toString(16).substring(1) + ("0000000000" + (new Date().getTime() - new Date(2010, 0, 1).getTime()).toString(16)).slice(-10);
 
4553
 
 
4554
            this.callback({
 
4555
                release: release,
 
4556
                uniqueid: uniqueid,
 
4557
                results: this.list.toString()
 
4558
            });
 
4559
        }
 
4560
    };
 
4561
 
 
4562
    return Runner;
 
4563
})();