├── README
├── feature_tests.js
├── getStyleProperty.js
├── index.html
├── master.css
├── master.js
└── style.html
/README:
--------------------------------------------------------------------------------
1 | Common Feature Tests (CFT) aims to provide a convenient set of feature tests (duh!) for Javascript (and JScript). It eliminates a need for browser sniffing, providing a much more robust way to work around environment quirks. None of the tests require presence of `document.body` and so can be run at any time.
2 |
3 | A "live" version is at http://kangax.github.io/cft/
4 |
--------------------------------------------------------------------------------
/feature_tests.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Copyright (c) 2008 Juriy Zaytsev (kangax@gmail.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 | SOFTWARE.
19 |
20 | */
21 |
22 | (function(__global){
23 |
24 | var t = new Date();
25 |
26 | // make sure `window` resolves to a global object
27 | var window = this;
28 |
29 | var features = { };
30 | var bugs = { };
31 |
32 | features.IS_CSS_TRANSFORMATION_SUPPORTED = (features.__IS_CSS_TRANSFORMATION_SUPPORTED = function(){
33 | var docEl = document.documentElement, s;
34 | if (docEl && (s = docEl.style)) {
35 | return (
36 | typeof s.WebkitTransform == 'string' ||
37 | typeof s.MozTransform == 'string' ||
38 | typeof s.OTransform == 'string' ||
39 | typeof s.MsTransform == 'string' ||
40 | typeof s.transform == 'string'
41 | );
42 | }
43 | return null;
44 | })();
45 |
46 | features.IS_ELEMENT_TAGNAME_UPPERCASED = (features.__IS_ELEMENT_TAGNAME_UPPERCASED = function(){
47 | var docEl = document.documentElement;
48 | if (docEl) {
49 | return 'HTML' === docEl.nodeName;
50 | }
51 | return null;
52 | })();
53 |
54 | bugs.QUERY_SELECTOR_IGNORES_CAPITALIZED_VALUES = (bugs.__QUERY_SELECTOR_IGNORES_CAPITALIZED_VALUES = function(){
55 | if (document.createElement && (document.compatMode === 'BackCompat')) {
56 | var el = document.createElement('div'),
57 | el2 = document.createElement('span');
58 | if (el && el2 && el.appendChild && el.querySelector) {
59 | el2.className = 'Test';
60 | el.appendChild(el2);
61 | var isBuggy = (el.querySelector('.Test') !== null);
62 | el = el2 = null;
63 | return isBuggy;
64 | }
65 | }
66 | return null;
67 | })();
68 |
69 | features.ARRAY_PROTOTYPE_SLICE_CAN_CONVERT_NODELIST_TO_ARRAY = (features.__ARRAY_PROTOTYPE_SLICE_CAN_CONVERT_NODELIST_TO_ARRAY = function(){
70 | try {
71 | return (Array.prototype.slice.call(document.forms, 0) instanceof Array);
72 | }
73 | catch(e) {
74 | return false;
75 | }
76 | })();
77 |
78 | features.WINDOW_EVAL_EVALUATES_IN_GLOBAL_SCOPE = (features.__WINDOW_EVAL_EVALUATES_IN_GLOBAL_SCOPE = function(){
79 | var fnId = '__eval' + Number(new Date()),
80 | passed = false;
81 |
82 | try {
83 | // catch indirect eval call errors (i.e. in such clients as Blackberry 9530)
84 | window.eval('var ' + fnId + '=true');
85 | } catch(e) { }
86 | passed = (window[fnId] === true);
87 | if (passed) {
88 | try {
89 | delete window[fnId];
90 | }
91 | catch(e) {
92 | window[fnId] = void 0;
93 | }
94 | }
95 | return passed;
96 | })();
97 |
98 | ( features.__IS_EVENT_METAKEY_PRESENT =
99 | features.__IS_EVENT_PREVENTDEFAULT_PRESENT =
100 | features.__IS_EVENT_SRCELEMENT_PRESENT =
101 | features.__IS_EVENT_RELATEDTARGET_PRESENT = function(){
102 |
103 | features.IS_EVENT_METAKEY_PRESENT = null;
104 | features.IS_EVENT_PREVENTDEFAULT_PRESENT = null;
105 | features.IS_EVENT_SRCELEMENT_PRESENT = null;
106 | features.IS_EVENT_RELATEDTARGET_PRESENT = null;
107 |
108 | if (document.createElement) {
109 | var i = document.createElement('input'),
110 | root = document.documentElement;
111 | if (i && i.style && i.click && root && root.appendChild && root.removeChild) {
112 | i.type = 'checkbox';
113 | i.style.display = 'none';
114 | i.onclick = function(e) {
115 | e = e || window.event;
116 | features.IS_EVENT_METAKEY_PRESENT = ('metaKey' in e);
117 | features.IS_EVENT_PREVENTDEFAULT_PRESENT = ('preventDefault' in e);
118 | features.IS_EVENT_SRCELEMENT_PRESENT = ('srcElement' in e);
119 | features.IS_EVENT_RELATEDTARGET_PRESENT = ('relatedTarget' in e);
120 | };
121 | root.appendChild(i);
122 | i.click();
123 | root.removeChild(i);
124 | i.onclick = null;
125 | i = null;
126 | }
127 | }
128 | })();
129 |
130 | features.IS_NATIVE_HAS_ATTRIBUTE_PRESENT = (features.__IS_NATIVE_HAS_ATTRIBUTE_PRESENT = function(){
131 | if (document.createElement) {
132 | var i = document.createElement('iframe'),
133 | root = document.documentElement,
134 | frames = window.frames;
135 | if (root && root.appendChild && root.removeChild) {
136 | i.style.display = 'none';
137 | root.appendChild(i);
138 | // some clients (e.g. Blackberry 9000 (Bold)) throw error when accesing frame's document
139 | try {
140 | var frame = frames[frames.length-1];
141 | if (frame) {
142 | var doc = frame.document;
143 | if (doc && doc.write) {
144 | doc.write('
a<\/p><\/div>b';
276 | // Safari 2.x returns ALL elements in `children`
277 | // We check that first element is a DIV and that it's the only one element returned
278 | isSupported = (el.children &&
279 | el.children.length === 1 &&
280 | el.children[0] &&
281 | el.children[0].tagName &&
282 | el.children[0].tagName.toUpperCase() === 'DIV');
283 | }
284 | return isSupported;
285 | })();
286 |
287 | features.IS_CSS_ENABLED = (features.__IS_CSS_ENABLED = function(){
288 | var body = document.body,
289 | isSupported = null;
290 | if (document.createElement &&
291 | body &&
292 | body.appendChild &&
293 | body.removeChild) {
294 | var el = document.createElement('div');
295 | if (el && el.style) {
296 | el.style.display = 'none';
297 | body.appendChild(el);
298 | isSupported = (el.offsetWidth === 0);
299 | body.removeChild(el);
300 | }
301 | }
302 | return isSupported;
303 | })();
304 |
305 | // features.IS_DOMFOCUSIN_SUPPORTED = (features.__IS_DOMFOCUSIN_SUPPORTED = function(){
306 | // var body = document.body,
307 | // isSupported = null;
308 | // if (document.createElement &&
309 | // body &&
310 | // body.insertBefore &&
311 | // body.removeChild) {
312 | // var el = document.createElement('DIV');
313 | // if (el && el.addEventListener && el.focus) {
314 | // isSupported = false;
315 | // el.addEventListener('DOMFocusIn', function(){ isSupported = true; }, false);
316 | // el.tabIndex = -1;
317 | // body.insertBefore(el, body.firstChild);
318 | // el.focus();
319 | // body.removeChild(el);
320 | // }
321 | // }
322 | // return isSupported;
323 | // })();
324 |
325 | features.IS_QUIRKS_MODE = (features.__IS_QUIRKS_MODE = function(){
326 | if (document.createElement) {
327 | var el = document.createElement('div');
328 | if (el && el.style) {
329 | el.style.width = '1';
330 | }
331 | return el.style.width === '1px';
332 | }
333 | })();
334 |
335 | features.IS_CONTAINS_BUGGY = (features.__IS_CONTAINS_BUGGY = function(){
336 | if (document.createElement) {
337 | var el1 = document.createElement('div'),
338 | el2 = document.createElement('div');
339 | if (el1 && el2 && el1.contains) {
340 | return el1.contains(el2);
341 | }
342 | }
343 | return null;
344 | })();
345 |
346 | // as per M. Miller advice
347 | features.IS_STRICT_MODE_SUPPORTED = (features.__IS_STRICT_MODE_SUPPORTED = function(){
348 | "use strict";
349 | return !this;
350 | })();
351 |
352 | features.IS_ACTIVEX_ENABLED = (features.__IS_ACTIVEX_ENABLED = function(){
353 | if (typeof ActiveXObject == 'undefined') return null;
354 | var xmlVersions = [
355 | 'Microsoft.XMLHTTP',
356 | 'Msxml2.XMLHTTP.3.0',
357 | 'Msxml2.XMLHTTP.4.0',
358 | 'Msxml2.XMLHTTP.5.0',
359 | 'Msxml2.XMLHTTP.6.0'
360 | ];
361 | for (var i = xmlVersions.length; i--; ) {
362 | try {
363 | if (new ActiveXObject(xmlVersions[i])) {
364 | return true;
365 | }
366 | }
367 | catch(ex) { }
368 | }
369 | return false;
370 | })();
371 |
372 | features.IS_MATCHESSELECTOR_SUPPORTED = (features.__IS_MATCHESSELECTOR_SUPPORTED = function(){
373 | var docEl = document.documentElement, prefixes = 'Khtml O Ms Webkit Moz'.split(' '), method = 'MatchesSelector';
374 | if (docEl) {
375 | for (var i = prefixes.length; i--; ) {
376 | if (docEl[prefixes[i] + method]) {
377 | return true;
378 | }
379 | if (docEl[prefixes[i].toLowerCase() + method]) {
380 | return true;
381 | }
382 | }
383 | return docEl[method.charAt(0).toLowerCase() + method.slice(1)];
384 | }
385 | return null;
386 | })();
387 |
388 | // BUGGIES
389 |
390 | // Safari returns "function" as typeof HTMLCollection
391 | // test for typeof DOM1 `document.forms` (if exists)
392 | bugs.TYPEOF_NODELIST_IS_FUNCTION = (bugs.__TYPEOF_NODELIST_IS_FUNCTION = function(){
393 | if (document.forms) {
394 | return (typeof document.forms == 'function');
395 | }
396 | return null;
397 | })();
398 |
399 | // IE returns comment nodes as part of `getElementsByTagName` results
400 | bugs.GETELEMENTSBYTAGNAME_RETURNS_COMMENT_NODES = (bugs.__GETELEMENTSBYTAGNAME_RETURNS_COMMENT_NODES = function(){
401 | if (document.createElement) {
402 | var el = document.createElement('div');
403 | if (el && el.getElementsByTagName) {
404 | el.innerHTML = 'a';
405 | var all = el.getElementsByTagName('*');
406 | // IE5.5 returns a 0-length collection when calling getElementsByTagName with wildcard
407 | if (all.length) {
408 | var lastNode = el.getElementsByTagName('*')[1];
409 | var buggy = !!(lastNode && lastNode.nodeType === 8);
410 | return buggy;
411 | }
412 | }
413 | }
414 | return null;
415 | })();
416 |
417 | // name attribute can not be set at run time in IE
418 | // http://msdn.microsoft.com/en-us/library/ms536389.aspx
419 | bugs.SETATTRIBUTE_IGNORES_NAME_ATTRIBUTE = (bugs.__SETATTRIBUTE_IGNORES_NAME_ATTRIBUTE = function(){
420 | if (document.createElement) {
421 | var elForm = document.createElement('form'),
422 | elInput = document.createElement('input'),
423 | root = document.documentElement;
424 | if (elForm &&
425 | elInput &&
426 | elInput.setAttribute &&
427 | elForm.appendChild &&
428 | root &&
429 | root.appendChild &&
430 | root.removeChild) {
431 | elInput.setAttribute('name', 'test');
432 | elForm.appendChild(elInput);
433 | // Older Safari (e.g. 2.0.2) populates "elements" collection only when form is within a document
434 | root.appendChild(elForm);
435 | var isBuggy = elForm.elements ? (typeof elForm.elements['test'] == 'undefined') : null;
436 | root.removeChild(elForm);
437 | return isBuggy;
438 | }
439 | }
440 | return null;
441 | })();
442 |
443 | bugs.ELEMENT_PROPERTIES_ARE_ATTRIBUTES = (bugs.__ELEMENT_PROPERTIES_ARE_ATTRIBUTES = function(){
444 | if (document.createElement) {
445 | var el = document.createElement('div');
446 | if (el && el.getAttribute) {
447 | el.__foo = 'bar';
448 | var buggy = (el.getAttribute('__foo') === 'bar');
449 | el = null;
450 | return buggy;
451 | }
452 | }
453 | return null;
454 | })();
455 |
456 | bugs.STRING_PROTOTYPE_REPLACE_IGNORES_FUNCTIONS = (bugs.__STRING_PROTOTYPE_REPLACE_IGNORES_FUNCTIONS = function(){
457 | var s = 'a';
458 | if (typeof s.replace == 'function') {
459 | return (s.replace(s, function(){ return '' }).length !== 0);
460 | }
461 | return null;
462 | })();
463 |
464 | bugs.ARRAY_PROTOTYPE_CONCAT_ARGUMENTS_BUGGY = (bugs.__ARRAY_PROTOTYPE_CONCAT_ARGUMENTS_BUGGY = function(){
465 | return (function(){
466 | if (arguments instanceof Array) {
467 | return [].concat(arguments)[0] !== 1;
468 | }
469 | return null;
470 | })(1,2);
471 | })();
472 |
473 | bugs.PROPERTIES_SHADOWING_DONTENUM_ARE_ENUMERABLE = (bugs.__PROPERTIES_SHADOWING_DONTENUM_ARE_ENUMERABLE = function(){
474 | for (var prop in { toString: true }) {
475 | return false;
476 | }
477 | return true;
478 | })();
479 |
480 | bugs.IS_REGEXP_WHITESPACE_CHARACTER_CLASS_BUGGY = (bugs.__IS_REGEXP_WHITESPACE_CHARACTER_CLASS_BUGGY = function(){
481 | var str = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002"+
482 | "\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029";
483 | return !/^\s+$/.test(str);
484 | })();
485 |
486 | bugs.IS_STRING_PROTOTYPE_SPLIT_REGEXP_BUGGY = (bugs.__IS_STRING_PROTOTYPE_SPLIT_REGEXP_BUGGY = function(){
487 | var s = 'a_b';
488 | if (typeof s.split == 'function') {
489 | return s.split(/(_)/).length !== 3;
490 | }
491 | return null;
492 | })();
493 |
494 | bugs.PRE_ELEMENTS_IGNORE_NEWLINES = (bugs.__PRE_ELEMENTS_IGNORE_NEWLINES = function(){
495 | if (document.createElement && document.createTextNode) {
496 | var el = document.createElement('pre');
497 | var txt = document.createTextNode('xx');
498 | var root = document.documentElement;
499 | if (el &&
500 | el.appendChild &&
501 | txt &&
502 | root &&
503 | root.appendChild &&
504 | root.removeChild) {
505 | el.appendChild(txt);
506 | root.appendChild(el);
507 | var initialHeight = el.offsetHeight;
508 | el.firstChild.nodeValue = 'x\nx';
509 | // check if `offsetHeight` changed after adding '\n' to the value
510 | var isIgnored = (el.offsetHeight === initialHeight);
511 | root.removeChild(el);
512 | el = txt = null;
513 | return isIgnored;
514 | }
515 | }
516 | return null;
517 | })();
518 |
519 | bugs.SELECT_ELEMENT_INNERHTML_BUGGY = (bugs.__SELECT_ELEMENT_INNERHTML_BUGGY = function(){
520 | if (document.createElement) {
521 | var el = document.createElement('select'),
522 | isBuggy = true;
523 | if (el) {
524 | el.innerHTML = '