|
1
|
|
|
/*! |
|
2
|
|
|
* modernizr v3.3.1 |
|
3
|
|
|
* Build http://modernizr.com/download?-backgroundsize-boxshadow-cssanimations-csstransitions-rgba-textshadow-setclasses-dontmin |
|
4
|
|
|
* |
|
5
|
|
|
* Copyright (c) |
|
6
|
|
|
* Faruk Ates |
|
7
|
|
|
* Paul Irish |
|
8
|
|
|
* Alex Sexton |
|
9
|
|
|
* Ryan Seddon |
|
10
|
|
|
* Patrick Kettner |
|
11
|
|
|
* Stu Cox |
|
12
|
|
|
* Richard Herrera |
|
13
|
|
|
|
|
14
|
|
|
* MIT License |
|
15
|
|
|
*/ |
|
16
|
|
|
|
|
17
|
|
|
/* |
|
18
|
|
|
* Modernizr tests which native CSS3 and HTML5 features are available in the |
|
19
|
|
|
* current UA and makes the results available to you in two ways: as properties on |
|
20
|
|
|
* a global `Modernizr` object, and as classes on the `<html>` element. This |
|
21
|
|
|
* information allows you to progressively enhance your pages with a granular level |
|
22
|
|
|
* of control over the experience. |
|
23
|
|
|
*/ |
|
24
|
|
|
|
|
25
|
|
|
;(function(window, document, undefined){ |
|
26
|
|
|
var classes = []; |
|
27
|
|
|
|
|
28
|
|
|
|
|
29
|
|
|
var tests = []; |
|
30
|
|
|
|
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* |
|
34
|
|
|
* ModernizrProto is the constructor for Modernizr |
|
35
|
|
|
* |
|
36
|
|
|
* @class |
|
37
|
|
|
* @access public |
|
38
|
|
|
*/ |
|
39
|
|
|
|
|
40
|
|
|
var ModernizrProto = { |
|
41
|
|
|
// The current version, dummy |
|
42
|
|
|
_version: '3.3.1', |
|
43
|
|
|
|
|
44
|
|
|
// Any settings that don't work as separate modules |
|
45
|
|
|
// can go in here as configuration. |
|
46
|
|
|
_config: { |
|
47
|
|
|
'classPrefix': '', |
|
48
|
|
|
'enableClasses': true, |
|
49
|
|
|
'enableJSClass': true, |
|
50
|
|
|
'usePrefixes': true |
|
51
|
|
|
}, |
|
52
|
|
|
|
|
53
|
|
|
// Queue of tests |
|
54
|
|
|
_q: [], |
|
55
|
|
|
|
|
56
|
|
|
// Stub these for people who are listening |
|
57
|
|
|
on: function(test, cb) { |
|
58
|
|
|
// I don't really think people should do this, but we can |
|
59
|
|
|
// safe guard it a bit. |
|
60
|
|
|
// -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. |
|
61
|
|
|
// This is in case people listen to synchronous tests. I would leave it out, |
|
62
|
|
|
// but the code to *disallow* sync tests in the real version of this |
|
63
|
|
|
// function is actually larger than this. |
|
64
|
|
|
var self = this; |
|
65
|
|
|
setTimeout(function() { |
|
66
|
|
|
cb(self[test]); |
|
67
|
|
|
}, 0); |
|
68
|
|
|
}, |
|
69
|
|
|
|
|
70
|
|
|
addTest: function(name, fn, options) { |
|
71
|
|
|
tests.push({name: name, fn: fn, options: options}); |
|
72
|
|
|
}, |
|
73
|
|
|
|
|
74
|
|
|
addAsyncTest: function(fn) { |
|
75
|
|
|
tests.push({name: null, fn: fn}); |
|
76
|
|
|
} |
|
77
|
|
|
}; |
|
78
|
|
|
|
|
79
|
|
|
|
|
80
|
|
|
|
|
81
|
|
|
// Fake some of Object.create so we can force non test results to be non "own" properties. |
|
82
|
|
|
var Modernizr = function() {}; |
|
83
|
|
|
Modernizr.prototype = ModernizrProto; |
|
84
|
|
|
|
|
85
|
|
|
// Leak modernizr globally when you `require` it rather than force it here. |
|
86
|
|
|
// Overwrite name so constructor name is nicer :D |
|
87
|
|
|
Modernizr = new Modernizr(); |
|
88
|
|
|
|
|
89
|
|
|
|
|
90
|
|
|
|
|
91
|
|
|
/** |
|
92
|
|
|
* is returns a boolean if the typeof an obj is exactly type. |
|
93
|
|
|
* |
|
94
|
|
|
* @access private |
|
95
|
|
|
* @function is |
|
96
|
|
|
* @param {*} obj - A thing we want to check the type of |
|
97
|
|
|
* @param {string} type - A string to compare the typeof against |
|
98
|
|
|
* @returns {boolean} |
|
99
|
|
|
*/ |
|
100
|
|
|
|
|
101
|
|
|
function is(obj, type) { |
|
102
|
|
|
return typeof obj === type; |
|
103
|
|
|
} |
|
104
|
|
|
; |
|
105
|
|
|
|
|
106
|
|
|
/** |
|
107
|
|
|
* Run through all tests and detect their support in the current UA. |
|
108
|
|
|
* |
|
109
|
|
|
* @access private |
|
110
|
|
|
*/ |
|
111
|
|
|
|
|
112
|
|
|
function testRunner() { |
|
113
|
|
|
var featureNames; |
|
114
|
|
|
var feature; |
|
115
|
|
|
var aliasIdx; |
|
116
|
|
|
var result; |
|
117
|
|
|
var nameIdx; |
|
118
|
|
|
var featureName; |
|
119
|
|
|
var featureNameSplit; |
|
120
|
|
|
|
|
121
|
|
|
for (var featureIdx in tests) { |
|
122
|
|
|
if (tests.hasOwnProperty(featureIdx)) { |
|
123
|
|
|
featureNames = []; |
|
124
|
|
|
feature = tests[featureIdx]; |
|
125
|
|
|
// run the test, throw the return value into the Modernizr, |
|
126
|
|
|
// then based on that boolean, define an appropriate className |
|
127
|
|
|
// and push it into an array of classes we'll join later. |
|
128
|
|
|
// |
|
129
|
|
|
// If there is no name, it's an 'async' test that is run, |
|
130
|
|
|
// but not directly added to the object. That should |
|
131
|
|
|
// be done with a post-run addTest call. |
|
132
|
|
|
if (feature.name) { |
|
133
|
|
|
featureNames.push(feature.name.toLowerCase()); |
|
134
|
|
|
|
|
135
|
|
|
if (feature.options && feature.options.aliases && feature.options.aliases.length) { |
|
136
|
|
|
// Add all the aliases into the names list |
|
137
|
|
|
for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { |
|
138
|
|
|
featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); |
|
139
|
|
|
} |
|
140
|
|
|
} |
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
|
|
// Run the test, or use the raw value if it's not a function |
|
144
|
|
|
result = is(feature.fn, 'function') ? feature.fn() : feature.fn; |
|
145
|
|
|
|
|
146
|
|
|
|
|
147
|
|
|
// Set each of the names on the Modernizr object |
|
148
|
|
|
for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { |
|
149
|
|
|
featureName = featureNames[nameIdx]; |
|
150
|
|
|
// Support dot properties as sub tests. We don't do checking to make sure |
|
151
|
|
|
// that the implied parent tests have been added. You must call them in |
|
152
|
|
|
// order (either in the test, or make the parent test a dependency). |
|
153
|
|
|
// |
|
154
|
|
|
// Cap it to TWO to make the logic simple and because who needs that kind of subtesting |
|
155
|
|
|
// hashtag famous last words |
|
156
|
|
|
featureNameSplit = featureName.split('.'); |
|
157
|
|
|
|
|
158
|
|
|
if (featureNameSplit.length === 1) { |
|
159
|
|
|
Modernizr[featureNameSplit[0]] = result; |
|
160
|
|
|
} else { |
|
161
|
|
|
// cast to a Boolean, if not one already |
|
162
|
|
|
/* jshint -W053 */ |
|
163
|
|
|
if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { |
|
164
|
|
|
Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; |
|
168
|
|
|
} |
|
169
|
|
|
|
|
170
|
|
|
classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); |
|
171
|
|
|
} |
|
172
|
|
|
} |
|
173
|
|
|
} |
|
174
|
|
|
} |
|
175
|
|
|
; |
|
176
|
|
|
|
|
177
|
|
|
/** |
|
178
|
|
|
* docElement is a convenience wrapper to grab the root element of the document |
|
179
|
|
|
* |
|
180
|
|
|
* @access private |
|
181
|
|
|
* @returns {HTMLElement|SVGElement} The root element of the document |
|
182
|
|
|
*/ |
|
183
|
|
|
|
|
184
|
|
|
var docElement = document.documentElement; |
|
185
|
|
|
|
|
186
|
|
|
|
|
187
|
|
|
/** |
|
188
|
|
|
* A convenience helper to check if the document we are running in is an SVG document |
|
189
|
|
|
* |
|
190
|
|
|
* @access private |
|
191
|
|
|
* @returns {boolean} |
|
192
|
|
|
*/ |
|
193
|
|
|
|
|
194
|
|
|
var isSVG = docElement.nodeName.toLowerCase() === 'svg'; |
|
195
|
|
|
|
|
196
|
|
|
|
|
197
|
|
|
/** |
|
198
|
|
|
* setClasses takes an array of class names and adds them to the root element |
|
199
|
|
|
* |
|
200
|
|
|
* @access private |
|
201
|
|
|
* @function setClasses |
|
202
|
|
|
* @param {string[]} classes - Array of class names |
|
203
|
|
|
*/ |
|
204
|
|
|
|
|
205
|
|
|
// Pass in an and array of class names, e.g.: |
|
206
|
|
|
// ['no-webp', 'borderradius', ...] |
|
207
|
|
|
function setClasses(classes) { |
|
208
|
|
|
var className = docElement.className; |
|
209
|
|
|
var classPrefix = Modernizr._config.classPrefix || ''; |
|
210
|
|
|
|
|
211
|
|
|
if (isSVG) { |
|
212
|
|
|
className = className.baseVal; |
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
|
|
// Change `no-js` to `js` (independently of the `enableClasses` option) |
|
216
|
|
|
// Handle classPrefix on this too |
|
217
|
|
|
if (Modernizr._config.enableJSClass) { |
|
218
|
|
|
var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); |
|
219
|
|
|
className = className.replace(reJS, '$1' + classPrefix + 'js$2'); |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
if (Modernizr._config.enableClasses) { |
|
223
|
|
|
// Add the new classes |
|
224
|
|
|
className += ' ' + classPrefix + classes.join(' ' + classPrefix); |
|
225
|
|
|
isSVG ? docElement.className.baseVal = className : docElement.className = className; |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
} |
|
229
|
|
|
|
|
230
|
|
|
; |
|
231
|
|
|
|
|
232
|
|
|
/** |
|
233
|
|
|
* createElement is a convenience wrapper around document.createElement. Since we |
|
234
|
|
|
* use createElement all over the place, this allows for (slightly) smaller code |
|
235
|
|
|
* as well as abstracting away issues with creating elements in contexts other than |
|
236
|
|
|
* HTML documents (e.g. SVG documents). |
|
237
|
|
|
* |
|
238
|
|
|
* @access private |
|
239
|
|
|
* @function createElement |
|
240
|
|
|
* @returns {HTMLElement|SVGElement} An HTML or SVG element |
|
241
|
|
|
*/ |
|
242
|
|
|
|
|
243
|
|
|
function createElement() { |
|
244
|
|
|
if (typeof document.createElement !== 'function') { |
|
245
|
|
|
// This is the case in IE7, where the type of createElement is "object". |
|
246
|
|
|
// For this reason, we cannot call apply() as Object is not a Function. |
|
247
|
|
|
return document.createElement(arguments[0]); |
|
248
|
|
|
} else if (isSVG) { |
|
249
|
|
|
return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); |
|
250
|
|
|
} else { |
|
251
|
|
|
return document.createElement.apply(document, arguments); |
|
252
|
|
|
} |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
; |
|
256
|
|
|
/*! |
|
257
|
|
|
{ |
|
258
|
|
|
"name": "CSS rgba", |
|
259
|
|
|
"caniuse": "css3-colors", |
|
260
|
|
|
"property": "rgba", |
|
261
|
|
|
"tags": ["css"], |
|
262
|
|
|
"notes": [{ |
|
263
|
|
|
"name": "CSSTricks Tutorial", |
|
264
|
|
|
"href": "https://css-tricks.com/rgba-browser-support/" |
|
265
|
|
|
}] |
|
266
|
|
|
} |
|
267
|
|
|
!*/ |
|
268
|
|
|
|
|
269
|
|
|
Modernizr.addTest('rgba', function() { |
|
270
|
|
|
var style = createElement('a').style; |
|
271
|
|
|
style.cssText = 'background-color:rgba(150,255,150,.5)'; |
|
272
|
|
|
|
|
273
|
|
|
return ('' + style.backgroundColor).indexOf('rgba') > -1; |
|
274
|
|
|
}); |
|
275
|
|
|
|
|
276
|
|
|
|
|
277
|
|
|
|
|
278
|
|
|
/** |
|
279
|
|
|
* contains checks to see if a string contains another string |
|
280
|
|
|
* |
|
281
|
|
|
* @access private |
|
282
|
|
|
* @function contains |
|
283
|
|
|
* @param {string} str - The string we want to check for substrings |
|
284
|
|
|
* @param {string} substr - The substring we want to search the first string for |
|
285
|
|
|
* @returns {boolean} |
|
286
|
|
|
*/ |
|
287
|
|
|
|
|
288
|
|
|
function contains(str, substr) { |
|
289
|
|
|
return !!~('' + str).indexOf(substr); |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
; |
|
293
|
|
|
|
|
294
|
|
|
/** |
|
295
|
|
|
* cssToDOM takes a kebab-case string and converts it to camelCase |
|
296
|
|
|
* e.g. box-sizing -> boxSizing |
|
297
|
|
|
* |
|
298
|
|
|
* @access private |
|
299
|
|
|
* @function cssToDOM |
|
300
|
|
|
* @param {string} name - String name of kebab-case prop we want to convert |
|
301
|
|
|
* @returns {string} The camelCase version of the supplied name |
|
302
|
|
|
*/ |
|
303
|
|
|
|
|
304
|
|
|
function cssToDOM(name) { |
|
305
|
|
|
return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { |
|
306
|
|
|
return m1 + m2.toUpperCase(); |
|
307
|
|
|
}).replace(/^-/, ''); |
|
308
|
|
|
} |
|
309
|
|
|
; |
|
310
|
|
|
|
|
311
|
|
|
/** |
|
312
|
|
|
* If the browsers follow the spec, then they would expose vendor-specific style as: |
|
313
|
|
|
* elem.style.WebkitBorderRadius |
|
314
|
|
|
* instead of something like the following, which would be technically incorrect: |
|
315
|
|
|
* elem.style.webkitBorderRadius |
|
316
|
|
|
|
|
317
|
|
|
* Webkit ghosts their properties in lowercase but Opera & Moz do not. |
|
318
|
|
|
* Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ |
|
319
|
|
|
* erik.eae.net/archives/2008/03/10/21.48.10/ |
|
320
|
|
|
|
|
321
|
|
|
* More here: github.com/Modernizr/Modernizr/issues/issue/21 |
|
322
|
|
|
* |
|
323
|
|
|
* @access private |
|
324
|
|
|
* @returns {string} The string representing the vendor-specific style properties |
|
325
|
|
|
*/ |
|
326
|
|
|
|
|
327
|
|
|
var omPrefixes = 'Moz O ms Webkit'; |
|
328
|
|
|
|
|
329
|
|
|
|
|
330
|
|
|
var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); |
|
331
|
|
|
ModernizrProto._cssomPrefixes = cssomPrefixes; |
|
332
|
|
|
|
|
333
|
|
|
|
|
334
|
|
|
/** |
|
335
|
|
|
* List of JavaScript DOM values used for tests |
|
336
|
|
|
* |
|
337
|
|
|
* @memberof Modernizr |
|
338
|
|
|
* @name Modernizr._domPrefixes |
|
339
|
|
|
* @optionName Modernizr._domPrefixes |
|
340
|
|
|
* @optionProp domPrefixes |
|
341
|
|
|
* @access public |
|
342
|
|
|
* @example |
|
343
|
|
|
* |
|
344
|
|
|
* Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather |
|
345
|
|
|
* than kebab-case properties, all properties are their Capitalized variant |
|
346
|
|
|
* |
|
347
|
|
|
* ```js |
|
348
|
|
|
* Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; |
|
349
|
|
|
* ``` |
|
350
|
|
|
*/ |
|
351
|
|
|
|
|
352
|
|
|
var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); |
|
353
|
|
|
ModernizrProto._domPrefixes = domPrefixes; |
|
354
|
|
|
|
|
355
|
|
|
|
|
356
|
|
|
/** |
|
357
|
|
|
* fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. |
|
358
|
|
|
* |
|
359
|
|
|
* @access private |
|
360
|
|
|
* @function fnBind |
|
361
|
|
|
* @param {function} fn - a function you want to change `this` reference to |
|
362
|
|
|
* @param {object} that - the `this` you want to call the function with |
|
363
|
|
|
* @returns {function} The wrapped version of the supplied function |
|
364
|
|
|
*/ |
|
365
|
|
|
|
|
366
|
|
|
function fnBind(fn, that) { |
|
367
|
|
|
return function() { |
|
368
|
|
|
return fn.apply(that, arguments); |
|
369
|
|
|
}; |
|
370
|
|
|
} |
|
371
|
|
|
|
|
372
|
|
|
; |
|
373
|
|
|
|
|
374
|
|
|
/** |
|
375
|
|
|
* testDOMProps is a generic DOM property test; if a browser supports |
|
376
|
|
|
* a certain property, it won't return undefined for it. |
|
377
|
|
|
* |
|
378
|
|
|
* @access private |
|
379
|
|
|
* @function testDOMProps |
|
380
|
|
|
* @param {array.<string>} props - An array of properties to test for |
|
381
|
|
|
* @param {object} obj - An object or Element you want to use to test the parameters again |
|
382
|
|
|
* @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check |
|
383
|
|
|
*/ |
|
384
|
|
|
function testDOMProps(props, obj, elem) { |
|
385
|
|
|
var item; |
|
386
|
|
|
|
|
387
|
|
|
for (var i in props) { |
|
388
|
|
|
if (props[i] in obj) { |
|
389
|
|
|
|
|
390
|
|
|
// return the property name as a string |
|
391
|
|
|
if (elem === false) { |
|
392
|
|
|
return props[i]; |
|
393
|
|
|
} |
|
394
|
|
|
|
|
395
|
|
|
item = obj[props[i]]; |
|
396
|
|
|
|
|
397
|
|
|
// let's bind a function |
|
398
|
|
|
if (is(item, 'function')) { |
|
399
|
|
|
// bind to obj unless overriden |
|
400
|
|
|
return fnBind(item, elem || obj); |
|
401
|
|
|
} |
|
402
|
|
|
|
|
403
|
|
|
// return the unbound function or obj or value |
|
404
|
|
|
return item; |
|
405
|
|
|
} |
|
406
|
|
|
} |
|
407
|
|
|
return false; |
|
408
|
|
|
} |
|
409
|
|
|
|
|
410
|
|
|
; |
|
411
|
|
|
|
|
412
|
|
|
/** |
|
413
|
|
|
* Create our "modernizr" element that we do most feature tests on. |
|
414
|
|
|
* |
|
415
|
|
|
* @access private |
|
416
|
|
|
*/ |
|
417
|
|
|
|
|
418
|
|
|
var modElem = { |
|
419
|
|
|
elem: createElement('modernizr') |
|
420
|
|
|
}; |
|
421
|
|
|
|
|
422
|
|
|
// Clean up this element |
|
423
|
|
|
Modernizr._q.push(function() { |
|
424
|
|
|
delete modElem.elem; |
|
425
|
|
|
}); |
|
426
|
|
|
|
|
427
|
|
|
|
|
428
|
|
|
|
|
429
|
|
|
var mStyle = { |
|
430
|
|
|
style: modElem.elem.style |
|
431
|
|
|
}; |
|
432
|
|
|
|
|
433
|
|
|
// kill ref for gc, must happen before mod.elem is removed, so we unshift on to |
|
434
|
|
|
// the front of the queue. |
|
435
|
|
|
Modernizr._q.unshift(function() { |
|
436
|
|
|
delete mStyle.style; |
|
437
|
|
|
}); |
|
438
|
|
|
|
|
439
|
|
|
|
|
440
|
|
|
|
|
441
|
|
|
/** |
|
442
|
|
|
* domToCSS takes a camelCase string and converts it to kebab-case |
|
443
|
|
|
* e.g. boxSizing -> box-sizing |
|
444
|
|
|
* |
|
445
|
|
|
* @access private |
|
446
|
|
|
* @function domToCSS |
|
447
|
|
|
* @param {string} name - String name of camelCase prop we want to convert |
|
448
|
|
|
* @returns {string} The kebab-case version of the supplied name |
|
449
|
|
|
*/ |
|
450
|
|
|
|
|
451
|
|
|
function domToCSS(name) { |
|
452
|
|
|
return name.replace(/([A-Z])/g, function(str, m1) { |
|
453
|
|
|
return '-' + m1.toLowerCase(); |
|
454
|
|
|
}).replace(/^ms-/, '-ms-'); |
|
455
|
|
|
} |
|
456
|
|
|
; |
|
457
|
|
|
|
|
458
|
|
|
/** |
|
459
|
|
|
* getBody returns the body of a document, or an element that can stand in for |
|
460
|
|
|
* the body if a real body does not exist |
|
461
|
|
|
* |
|
462
|
|
|
* @access private |
|
463
|
|
|
* @function getBody |
|
464
|
|
|
* @returns {HTMLElement|SVGElement} Returns the real body of a document, or an |
|
465
|
|
|
* artificially created element that stands in for the body |
|
466
|
|
|
*/ |
|
467
|
|
|
|
|
468
|
|
|
function getBody() { |
|
469
|
|
|
// After page load injecting a fake body doesn't work so check if body exists |
|
470
|
|
|
var body = document.body; |
|
471
|
|
|
|
|
472
|
|
|
if (!body) { |
|
473
|
|
|
// Can't use the real body create a fake one. |
|
474
|
|
|
body = createElement(isSVG ? 'svg' : 'body'); |
|
475
|
|
|
body.fake = true; |
|
476
|
|
|
} |
|
477
|
|
|
|
|
478
|
|
|
return body; |
|
479
|
|
|
} |
|
480
|
|
|
|
|
481
|
|
|
; |
|
482
|
|
|
|
|
483
|
|
|
/** |
|
484
|
|
|
* injectElementWithStyles injects an element with style element and some CSS rules |
|
485
|
|
|
* |
|
486
|
|
|
* @access private |
|
487
|
|
|
* @function injectElementWithStyles |
|
488
|
|
|
* @param {string} rule - String representing a css rule |
|
489
|
|
|
* @param {function} callback - A function that is used to test the injected element |
|
490
|
|
|
* @param {number} [nodes] - An integer representing the number of additional nodes you want injected |
|
491
|
|
|
* @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes |
|
492
|
|
|
* @returns {boolean} |
|
493
|
|
|
*/ |
|
494
|
|
|
|
|
495
|
|
|
function injectElementWithStyles(rule, callback, nodes, testnames) { |
|
496
|
|
|
var mod = 'modernizr'; |
|
497
|
|
|
var style; |
|
498
|
|
|
var ret; |
|
499
|
|
|
var node; |
|
500
|
|
|
var docOverflow; |
|
501
|
|
|
var div = createElement('div'); |
|
502
|
|
|
var body = getBody(); |
|
503
|
|
|
|
|
504
|
|
|
if (parseInt(nodes, 10)) { |
|
505
|
|
|
// In order not to give false positives we create a node for each test |
|
506
|
|
|
// This also allows the method to scale for unspecified uses |
|
507
|
|
|
while (nodes--) { |
|
508
|
|
|
node = createElement('div'); |
|
509
|
|
|
node.id = testnames ? testnames[nodes] : mod + (nodes + 1); |
|
510
|
|
|
div.appendChild(node); |
|
511
|
|
|
} |
|
512
|
|
|
} |
|
513
|
|
|
|
|
514
|
|
|
style = createElement('style'); |
|
515
|
|
|
style.type = 'text/css'; |
|
516
|
|
|
style.id = 's' + mod; |
|
517
|
|
|
|
|
518
|
|
|
// IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody. |
|
519
|
|
|
// Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 |
|
520
|
|
|
(!body.fake ? div : body).appendChild(style); |
|
521
|
|
|
body.appendChild(div); |
|
522
|
|
|
|
|
523
|
|
|
if (style.styleSheet) { |
|
524
|
|
|
style.styleSheet.cssText = rule; |
|
525
|
|
|
} else { |
|
526
|
|
|
style.appendChild(document.createTextNode(rule)); |
|
527
|
|
|
} |
|
528
|
|
|
div.id = mod; |
|
529
|
|
|
|
|
530
|
|
|
if (body.fake) { |
|
531
|
|
|
//avoid crashing IE8, if background image is used |
|
532
|
|
|
body.style.background = ''; |
|
533
|
|
|
//Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible |
|
534
|
|
|
body.style.overflow = 'hidden'; |
|
535
|
|
|
docOverflow = docElement.style.overflow; |
|
536
|
|
|
docElement.style.overflow = 'hidden'; |
|
537
|
|
|
docElement.appendChild(body); |
|
538
|
|
|
} |
|
539
|
|
|
|
|
540
|
|
|
ret = callback(div, rule); |
|
541
|
|
|
// If this is done after page load we don't want to remove the body so check if body exists |
|
542
|
|
|
if (body.fake) { |
|
543
|
|
|
body.parentNode.removeChild(body); |
|
544
|
|
|
docElement.style.overflow = docOverflow; |
|
545
|
|
|
// Trigger layout so kinetic scrolling isn't disabled in iOS6+ |
|
546
|
|
|
docElement.offsetHeight; |
|
547
|
|
|
} else { |
|
548
|
|
|
div.parentNode.removeChild(div); |
|
549
|
|
|
} |
|
550
|
|
|
|
|
551
|
|
|
return !!ret; |
|
552
|
|
|
|
|
553
|
|
|
} |
|
554
|
|
|
|
|
555
|
|
|
; |
|
556
|
|
|
|
|
557
|
|
|
/** |
|
558
|
|
|
* nativeTestProps allows for us to use native feature detection functionality if available. |
|
559
|
|
|
* some prefixed form, or false, in the case of an unsupported rule |
|
560
|
|
|
* |
|
561
|
|
|
* @access private |
|
562
|
|
|
* @function nativeTestProps |
|
563
|
|
|
* @param {array} props - An array of property names |
|
564
|
|
|
* @param {string} value - A string representing the value we want to check via @supports |
|
565
|
|
|
* @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise |
|
566
|
|
|
*/ |
|
567
|
|
|
|
|
568
|
|
|
// Accepts a list of property names and a single value |
|
569
|
|
|
// Returns `undefined` if native detection not available |
|
570
|
|
|
function nativeTestProps(props, value) { |
|
571
|
|
|
var i = props.length; |
|
572
|
|
|
// Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface |
|
573
|
|
|
if ('CSS' in window && 'supports' in window.CSS) { |
|
574
|
|
|
// Try every prefixed variant of the property |
|
575
|
|
|
while (i--) { |
|
576
|
|
|
if (window.CSS.supports(domToCSS(props[i]), value)) { |
|
577
|
|
|
return true; |
|
578
|
|
|
} |
|
579
|
|
|
} |
|
580
|
|
|
return false; |
|
581
|
|
|
} |
|
582
|
|
|
// Otherwise fall back to at-rule (for Opera 12.x) |
|
583
|
|
|
else if ('CSSSupportsRule' in window) { |
|
584
|
|
|
// Build a condition string for every prefixed variant |
|
585
|
|
|
var conditionText = []; |
|
586
|
|
|
while (i--) { |
|
587
|
|
|
conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); |
|
588
|
|
|
} |
|
589
|
|
|
conditionText = conditionText.join(' or '); |
|
590
|
|
|
return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { |
|
591
|
|
|
return getComputedStyle(node, null).position == 'absolute'; |
|
592
|
|
|
}); |
|
593
|
|
|
} |
|
594
|
|
|
return undefined; |
|
595
|
|
|
} |
|
596
|
|
|
; |
|
597
|
|
|
|
|
598
|
|
|
// testProps is a generic CSS / DOM property test. |
|
599
|
|
|
|
|
600
|
|
|
// In testing support for a given CSS property, it's legit to test: |
|
601
|
|
|
// `elem.style[styleName] !== undefined` |
|
602
|
|
|
// If the property is supported it will return an empty string, |
|
603
|
|
|
// if unsupported it will return undefined. |
|
604
|
|
|
|
|
605
|
|
|
// We'll take advantage of this quick test and skip setting a style |
|
606
|
|
|
// on our modernizr element, but instead just testing undefined vs |
|
607
|
|
|
// empty string. |
|
608
|
|
|
|
|
609
|
|
|
// Property names can be provided in either camelCase or kebab-case. |
|
610
|
|
|
|
|
611
|
|
|
function testProps(props, prefixed, value, skipValueTest) { |
|
612
|
|
|
skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; |
|
613
|
|
|
|
|
614
|
|
|
// Try native detect first |
|
615
|
|
|
if (!is(value, 'undefined')) { |
|
616
|
|
|
var result = nativeTestProps(props, value); |
|
617
|
|
|
if (!is(result, 'undefined')) { |
|
618
|
|
|
return result; |
|
619
|
|
|
} |
|
620
|
|
|
} |
|
621
|
|
|
|
|
622
|
|
|
// Otherwise do it properly |
|
623
|
|
|
var afterInit, i, propsLength, prop, before; |
|
624
|
|
|
|
|
625
|
|
|
// If we don't have a style element, that means we're running async or after |
|
626
|
|
|
// the core tests, so we'll need to create our own elements to use |
|
627
|
|
|
|
|
628
|
|
|
// inside of an SVG element, in certain browsers, the `style` element is only |
|
629
|
|
|
// defined for valid tags. Therefore, if `modernizr` does not have one, we |
|
630
|
|
|
// fall back to a less used element and hope for the best. |
|
631
|
|
|
var elems = ['modernizr', 'tspan']; |
|
632
|
|
|
while (!mStyle.style) { |
|
633
|
|
|
afterInit = true; |
|
634
|
|
|
mStyle.modElem = createElement(elems.shift()); |
|
635
|
|
|
mStyle.style = mStyle.modElem.style; |
|
636
|
|
|
} |
|
637
|
|
|
|
|
638
|
|
|
// Delete the objects if we created them. |
|
639
|
|
|
function cleanElems() { |
|
640
|
|
|
if (afterInit) { |
|
641
|
|
|
delete mStyle.style; |
|
642
|
|
|
delete mStyle.modElem; |
|
643
|
|
|
} |
|
644
|
|
|
} |
|
645
|
|
|
|
|
646
|
|
|
propsLength = props.length; |
|
647
|
|
|
for (i = 0; i < propsLength; i++) { |
|
648
|
|
|
prop = props[i]; |
|
649
|
|
|
before = mStyle.style[prop]; |
|
650
|
|
|
|
|
651
|
|
|
if (contains(prop, '-')) { |
|
652
|
|
|
prop = cssToDOM(prop); |
|
653
|
|
|
} |
|
654
|
|
|
|
|
655
|
|
|
if (mStyle.style[prop] !== undefined) { |
|
656
|
|
|
|
|
657
|
|
|
// If value to test has been passed in, do a set-and-check test. |
|
658
|
|
|
// 0 (integer) is a valid property value, so check that `value` isn't |
|
659
|
|
|
// undefined, rather than just checking it's truthy. |
|
660
|
|
|
if (!skipValueTest && !is(value, 'undefined')) { |
|
661
|
|
|
|
|
662
|
|
|
// Needs a try catch block because of old IE. This is slow, but will |
|
663
|
|
|
// be avoided in most cases because `skipValueTest` will be used. |
|
664
|
|
|
try { |
|
665
|
|
|
mStyle.style[prop] = value; |
|
666
|
|
|
} catch (e) {} |
|
667
|
|
|
|
|
668
|
|
|
// If the property value has changed, we assume the value used is |
|
669
|
|
|
// supported. If `value` is empty string, it'll fail here (because |
|
670
|
|
|
// it hasn't changed), which matches how browsers have implemented |
|
671
|
|
|
// CSS.supports() |
|
672
|
|
|
if (mStyle.style[prop] != before) { |
|
673
|
|
|
cleanElems(); |
|
674
|
|
|
return prefixed == 'pfx' ? prop : true; |
|
675
|
|
|
} |
|
676
|
|
|
} |
|
677
|
|
|
// Otherwise just return true, or the property name if this is a |
|
678
|
|
|
// `prefixed()` call |
|
679
|
|
|
else { |
|
680
|
|
|
cleanElems(); |
|
681
|
|
|
return prefixed == 'pfx' ? prop : true; |
|
682
|
|
|
} |
|
683
|
|
|
} |
|
684
|
|
|
} |
|
685
|
|
|
cleanElems(); |
|
686
|
|
|
return false; |
|
687
|
|
|
} |
|
688
|
|
|
|
|
689
|
|
|
; |
|
690
|
|
|
|
|
691
|
|
|
/** |
|
692
|
|
|
* testProp() investigates whether a given style property is recognized |
|
693
|
|
|
* Property names can be provided in either camelCase or kebab-case. |
|
694
|
|
|
* |
|
695
|
|
|
* @memberof Modernizr |
|
696
|
|
|
* @name Modernizr.testProp |
|
697
|
|
|
* @access public |
|
698
|
|
|
* @optionName Modernizr.testProp() |
|
699
|
|
|
* @optionProp testProp |
|
700
|
|
|
* @function testProp |
|
701
|
|
|
* @param {string} prop - Name of the CSS property to check |
|
702
|
|
|
* @param {string} [value] - Name of the CSS value to check |
|
703
|
|
|
* @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported |
|
704
|
|
|
* @returns {boolean} |
|
705
|
|
|
* @example |
|
706
|
|
|
* |
|
707
|
|
|
* Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed |
|
708
|
|
|
* version of the string. |
|
709
|
|
|
* |
|
710
|
|
|
* Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing) |
|
711
|
|
|
* |
|
712
|
|
|
* ```js |
|
713
|
|
|
* Modernizr.testProp('pointerEvents') // true |
|
714
|
|
|
* ``` |
|
715
|
|
|
* |
|
716
|
|
|
* You can also provide a value as an optional second argument to check if a |
|
717
|
|
|
* specific value is supported |
|
718
|
|
|
* |
|
719
|
|
|
* ```js |
|
720
|
|
|
* Modernizr.testProp('pointerEvents', 'none') // true |
|
721
|
|
|
* Modernizr.testProp('pointerEvents', 'penguin') // false |
|
722
|
|
|
* ``` |
|
723
|
|
|
*/ |
|
724
|
|
|
|
|
725
|
|
|
var testProp = ModernizrProto.testProp = function(prop, value, useValue) { |
|
726
|
|
|
return testProps([prop], undefined, value, useValue); |
|
727
|
|
|
}; |
|
728
|
|
|
|
|
729
|
|
|
/*! |
|
730
|
|
|
{ |
|
731
|
|
|
"name": "CSS textshadow", |
|
732
|
|
|
"property": "textshadow", |
|
733
|
|
|
"caniuse": "css-textshadow", |
|
734
|
|
|
"tags": ["css"], |
|
735
|
|
|
"knownBugs": ["FF3.0 will false positive on this test"] |
|
736
|
|
|
} |
|
737
|
|
|
!*/ |
|
738
|
|
|
|
|
739
|
|
|
Modernizr.addTest('textshadow', testProp('textShadow', '1px 1px')); |
|
740
|
|
|
|
|
741
|
|
|
|
|
742
|
|
|
/** |
|
743
|
|
|
* testPropsAll tests a list of DOM properties we want to check against. |
|
744
|
|
|
* We specify literally ALL possible (known and/or likely) properties on |
|
745
|
|
|
* the element including the non-vendor prefixed one, for forward- |
|
746
|
|
|
* compatibility. |
|
747
|
|
|
* |
|
748
|
|
|
* @access private |
|
749
|
|
|
* @function testPropsAll |
|
750
|
|
|
* @param {string} prop - A string of the property to test for |
|
751
|
|
|
* @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip |
|
752
|
|
|
* @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against |
|
753
|
|
|
* @param {string} [value] - A string of a css value |
|
754
|
|
|
* @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set |
|
755
|
|
|
*/ |
|
756
|
|
|
function testPropsAll(prop, prefixed, elem, value, skipValueTest) { |
|
757
|
|
|
|
|
758
|
|
|
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), |
|
759
|
|
|
props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); |
|
760
|
|
|
|
|
761
|
|
|
// did they call .prefixed('boxSizing') or are we just testing a prop? |
|
762
|
|
|
if (is(prefixed, 'string') || is(prefixed, 'undefined')) { |
|
763
|
|
|
return testProps(props, prefixed, value, skipValueTest); |
|
764
|
|
|
|
|
765
|
|
|
// otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) |
|
766
|
|
|
} else { |
|
767
|
|
|
props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); |
|
768
|
|
|
return testDOMProps(props, prefixed, elem); |
|
769
|
|
|
} |
|
770
|
|
|
} |
|
771
|
|
|
|
|
772
|
|
|
// Modernizr.testAllProps() investigates whether a given style property, |
|
773
|
|
|
// or any of its vendor-prefixed variants, is recognized |
|
774
|
|
|
// |
|
775
|
|
|
// Note that the property names must be provided in the camelCase variant. |
|
776
|
|
|
// Modernizr.testAllProps('boxSizing') |
|
777
|
|
|
ModernizrProto.testAllProps = testPropsAll; |
|
778
|
|
|
|
|
779
|
|
|
|
|
780
|
|
|
|
|
781
|
|
|
/** |
|
782
|
|
|
* testAllProps determines whether a given CSS property is supported in the browser |
|
783
|
|
|
* |
|
784
|
|
|
* @memberof Modernizr |
|
785
|
|
|
* @name Modernizr.testAllProps |
|
786
|
|
|
* @optionName Modernizr.testAllProps() |
|
787
|
|
|
* @optionProp testAllProps |
|
788
|
|
|
* @access public |
|
789
|
|
|
* @function testAllProps |
|
790
|
|
|
* @param {string} prop - String naming the property to test (either camelCase or kebab-case) |
|
791
|
|
|
* @param {string} [value] - String of the value to test |
|
792
|
|
|
* @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection |
|
793
|
|
|
* @example |
|
794
|
|
|
* |
|
795
|
|
|
* testAllProps determines whether a given CSS property, in some prefixed form, |
|
796
|
|
|
* is supported by the browser. |
|
797
|
|
|
* |
|
798
|
|
|
* ```js |
|
799
|
|
|
* testAllProps('boxSizing') // true |
|
800
|
|
|
* ``` |
|
801
|
|
|
* |
|
802
|
|
|
* It can optionally be given a CSS value in string form to test if a property |
|
803
|
|
|
* value is valid |
|
804
|
|
|
* |
|
805
|
|
|
* ```js |
|
806
|
|
|
* testAllProps('display', 'block') // true |
|
807
|
|
|
* testAllProps('display', 'penguin') // false |
|
808
|
|
|
* ``` |
|
809
|
|
|
* |
|
810
|
|
|
* A boolean can be passed as a third parameter to skip the value check when |
|
811
|
|
|
* native detection (@supports) isn't available. |
|
812
|
|
|
* |
|
813
|
|
|
* ```js |
|
814
|
|
|
* testAllProps('shapeOutside', 'content-box', true); |
|
815
|
|
|
* ``` |
|
816
|
|
|
*/ |
|
817
|
|
|
|
|
818
|
|
|
function testAllProps(prop, value, skipValueTest) { |
|
819
|
|
|
return testPropsAll(prop, undefined, undefined, value, skipValueTest); |
|
820
|
|
|
} |
|
821
|
|
|
ModernizrProto.testAllProps = testAllProps; |
|
822
|
|
|
|
|
823
|
|
|
/*! |
|
824
|
|
|
{ |
|
825
|
|
|
"name": "CSS Animations", |
|
826
|
|
|
"property": "cssanimations", |
|
827
|
|
|
"caniuse": "css-animation", |
|
828
|
|
|
"polyfills": ["transformie", "csssandpaper"], |
|
829
|
|
|
"tags": ["css"], |
|
830
|
|
|
"warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"], |
|
831
|
|
|
"notes": [{ |
|
832
|
|
|
"name" : "Article: 'Dispelling the Android CSS animation myths'", |
|
833
|
|
|
"href": "https://goo.gl/OGw5Gm" |
|
834
|
|
|
}] |
|
835
|
|
|
} |
|
836
|
|
|
!*/ |
|
837
|
|
|
/* DOC |
|
838
|
|
|
Detects whether or not elements can be animated using CSS |
|
839
|
|
|
*/ |
|
840
|
|
|
|
|
841
|
|
|
Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); |
|
842
|
|
|
|
|
843
|
|
|
/*! |
|
844
|
|
|
{ |
|
845
|
|
|
"name": "Background Size", |
|
846
|
|
|
"property": "backgroundsize", |
|
847
|
|
|
"tags": ["css"], |
|
848
|
|
|
"knownBugs": ["This will false positive in Opera Mini - https://github.com/Modernizr/Modernizr/issues/396"], |
|
849
|
|
|
"notes": [{ |
|
850
|
|
|
"name": "Related Issue", |
|
851
|
|
|
"href": "https://github.com/Modernizr/Modernizr/issues/396" |
|
852
|
|
|
}] |
|
853
|
|
|
} |
|
854
|
|
|
!*/ |
|
855
|
|
|
|
|
856
|
|
|
Modernizr.addTest('backgroundsize', testAllProps('backgroundSize', '100%', true)); |
|
857
|
|
|
|
|
858
|
|
|
/*! |
|
859
|
|
|
{ |
|
860
|
|
|
"name": "CSS Transitions", |
|
861
|
|
|
"property": "csstransitions", |
|
862
|
|
|
"caniuse": "css-transitions", |
|
863
|
|
|
"tags": ["css"] |
|
864
|
|
|
} |
|
865
|
|
|
!*/ |
|
866
|
|
|
|
|
867
|
|
|
Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); |
|
868
|
|
|
|
|
869
|
|
|
/*! |
|
870
|
|
|
{ |
|
871
|
|
|
"name": "Box Shadow", |
|
872
|
|
|
"property": "boxshadow", |
|
873
|
|
|
"caniuse": "css-boxshadow", |
|
874
|
|
|
"tags": ["css"], |
|
875
|
|
|
"knownBugs": [ |
|
876
|
|
|
"WebOS false positives on this test.", |
|
877
|
|
|
"The Kindle Silk browser false positives" |
|
878
|
|
|
] |
|
879
|
|
|
} |
|
880
|
|
|
!*/ |
|
881
|
|
|
|
|
882
|
|
|
Modernizr.addTest('boxshadow', testAllProps('boxShadow', '1px 1px', true)); |
|
883
|
|
|
|
|
884
|
|
|
|
|
885
|
|
|
// Run each test |
|
886
|
|
|
testRunner(); |
|
887
|
|
|
|
|
888
|
|
|
// Remove the "no-js" class if it exists |
|
889
|
|
|
setClasses(classes); |
|
890
|
|
|
|
|
891
|
|
|
delete ModernizrProto.addTest; |
|
892
|
|
|
delete ModernizrProto.addAsyncTest; |
|
893
|
|
|
|
|
894
|
|
|
// Run the things that are supposed to run after the tests |
|
895
|
|
|
for (var i = 0; i < Modernizr._q.length; i++) { |
|
896
|
|
|
Modernizr._q[i](); |
|
897
|
|
|
} |
|
898
|
|
|
|
|
899
|
|
|
// Leak Modernizr namespace |
|
900
|
|
|
window.Modernizr = Modernizr; |
|
901
|
|
|
|
|
902
|
|
|
|
|
903
|
|
|
; |
|
904
|
|
|
|
|
905
|
|
|
})(window, document); |