1 | /*! |
---|
2 | * Note: While Microsoft is not the author of this file, Microsoft is |
---|
3 | * offering you a license subject to the terms of the Microsoft Software |
---|
4 | * License Terms for Microsoft ASP.NET Model View Controller 3. |
---|
5 | * Microsoft reserves all other rights. The notices below are provided |
---|
6 | * for informational purposes only and are not the license terms under |
---|
7 | * which Microsoft distributed this file. |
---|
8 | * |
---|
9 | * Modernizr v1.7 |
---|
10 | * http://www.modernizr.com |
---|
11 | * |
---|
12 | * Developed by: |
---|
13 | * - Faruk Ates http://farukat.es/ |
---|
14 | * - Paul Irish http://paulirish.com/ |
---|
15 | * |
---|
16 | * Copyright (c) 2009-2011 |
---|
17 | */ |
---|
18 | |
---|
19 | |
---|
20 | /* |
---|
21 | * Modernizr is a script that detects native CSS3 and HTML5 features |
---|
22 | * available in the current UA and provides an object containing all |
---|
23 | * features with a true/false value, depending on whether the UA has |
---|
24 | * native support for it or not. |
---|
25 | * |
---|
26 | * Modernizr will also add classes to the <html> element of the page, |
---|
27 | * one for each feature it detects. If the UA supports it, a class |
---|
28 | * like "cssgradients" will be added. If not, the class name will be |
---|
29 | * "no-cssgradients". This allows for simple if-conditionals in your |
---|
30 | * CSS, giving you fine control over the look & feel of your website. |
---|
31 | * |
---|
32 | * @author Faruk Ates |
---|
33 | * @author Paul Irish |
---|
34 | * @copyright (c) 2009-2011 Faruk Ates. |
---|
35 | * @contributor Ben Alman |
---|
36 | */ |
---|
37 | |
---|
38 | window.Modernizr = (function(window,document,undefined){ |
---|
39 | |
---|
40 | var version = '1.7', |
---|
41 | |
---|
42 | ret = {}, |
---|
43 | |
---|
44 | /** |
---|
45 | * !! DEPRECATED !! |
---|
46 | * |
---|
47 | * enableHTML5 is a private property for advanced use only. If enabled, |
---|
48 | * it will make Modernizr.init() run through a brief while() loop in |
---|
49 | * which it will create all HTML5 elements in the DOM to allow for |
---|
50 | * styling them in Internet Explorer, which does not recognize any |
---|
51 | * non-HTML4 elements unless created in the DOM this way. |
---|
52 | * |
---|
53 | * enableHTML5 is ON by default. |
---|
54 | * |
---|
55 | * The enableHTML5 toggle option is DEPRECATED as per 1.6, and will be |
---|
56 | * replaced in 2.0 in lieu of the modular, configurable nature of 2.0. |
---|
57 | */ |
---|
58 | enableHTML5 = true, |
---|
59 | |
---|
60 | |
---|
61 | docElement = document.documentElement, |
---|
62 | docHead = document.head || document.getElementsByTagName('head')[0], |
---|
63 | |
---|
64 | /** |
---|
65 | * Create our "modernizr" element that we do most feature tests on. |
---|
66 | */ |
---|
67 | mod = 'modernizr', |
---|
68 | modElem = document.createElement( mod ), |
---|
69 | m_style = modElem.style, |
---|
70 | |
---|
71 | /** |
---|
72 | * Create the input element for various Web Forms feature tests. |
---|
73 | */ |
---|
74 | inputElem = document.createElement( 'input' ), |
---|
75 | |
---|
76 | smile = ':)', |
---|
77 | |
---|
78 | tostring = Object.prototype.toString, |
---|
79 | |
---|
80 | // List of property values to set for css tests. See ticket #21 |
---|
81 | prefixes = ' -webkit- -moz- -o- -ms- -khtml- '.split(' '), |
---|
82 | |
---|
83 | // Following spec is to expose vendor-specific style properties as: |
---|
84 | // elem.style.WebkitBorderRadius |
---|
85 | // and the following would be incorrect: |
---|
86 | // elem.style.webkitBorderRadius |
---|
87 | |
---|
88 | // Webkit ghosts their properties in lowercase but Opera & Moz do not. |
---|
89 | // Microsoft foregoes prefixes entirely <= IE8, but appears to |
---|
90 | // use a lowercase `ms` instead of the correct `Ms` in IE9 |
---|
91 | |
---|
92 | // More here: http://github.com/Modernizr/Modernizr/issues/issue/21 |
---|
93 | domPrefixes = 'Webkit Moz O ms Khtml'.split(' '), |
---|
94 | |
---|
95 | ns = {'svg': 'http://www.w3.org/2000/svg'}, |
---|
96 | |
---|
97 | tests = {}, |
---|
98 | inputs = {}, |
---|
99 | attrs = {}, |
---|
100 | |
---|
101 | classes = [], |
---|
102 | |
---|
103 | featurename, // used in testing loop |
---|
104 | |
---|
105 | |
---|
106 | |
---|
107 | // todo: consider using http://javascript.nwbox.com/CSSSupport/css-support.js instead |
---|
108 | testMediaQuery = function(mq){ |
---|
109 | |
---|
110 | var st = document.createElement('style'), |
---|
111 | div = document.createElement('div'), |
---|
112 | ret; |
---|
113 | |
---|
114 | st.textContent = mq + '{#modernizr{height:3px}}'; |
---|
115 | docHead.appendChild(st); |
---|
116 | div.id = 'modernizr'; |
---|
117 | docElement.appendChild(div); |
---|
118 | |
---|
119 | ret = div.offsetHeight === 3; |
---|
120 | |
---|
121 | st.parentNode.removeChild(st); |
---|
122 | div.parentNode.removeChild(div); |
---|
123 | |
---|
124 | return !!ret; |
---|
125 | |
---|
126 | }, |
---|
127 | |
---|
128 | |
---|
129 | /** |
---|
130 | * isEventSupported determines if a given element supports the given event |
---|
131 | * function from http://yura.thinkweb2.com/isEventSupported/ |
---|
132 | */ |
---|
133 | isEventSupported = (function(){ |
---|
134 | |
---|
135 | var TAGNAMES = { |
---|
136 | 'select':'input','change':'input', |
---|
137 | 'submit':'form','reset':'form', |
---|
138 | 'error':'img','load':'img','abort':'img' |
---|
139 | }; |
---|
140 | |
---|
141 | function isEventSupported(eventName, element) { |
---|
142 | |
---|
143 | element = element || document.createElement(TAGNAMES[eventName] || 'div'); |
---|
144 | eventName = 'on' + eventName; |
---|
145 | |
---|
146 | // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those |
---|
147 | var isSupported = (eventName in element); |
---|
148 | |
---|
149 | if (!isSupported) { |
---|
150 | // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element |
---|
151 | if (!element.setAttribute) { |
---|
152 | element = document.createElement('div'); |
---|
153 | } |
---|
154 | if (element.setAttribute && element.removeAttribute) { |
---|
155 | element.setAttribute(eventName, ''); |
---|
156 | isSupported = is(element[eventName], 'function'); |
---|
157 | |
---|
158 | // If property was created, "remove it" (by setting value to `undefined`) |
---|
159 | if (!is(element[eventName], undefined)) { |
---|
160 | element[eventName] = undefined; |
---|
161 | } |
---|
162 | element.removeAttribute(eventName); |
---|
163 | } |
---|
164 | } |
---|
165 | |
---|
166 | element = null; |
---|
167 | return isSupported; |
---|
168 | } |
---|
169 | return isEventSupported; |
---|
170 | })(); |
---|
171 | |
---|
172 | |
---|
173 | // hasOwnProperty shim by kangax needed for Safari 2.0 support |
---|
174 | var _hasOwnProperty = ({}).hasOwnProperty, hasOwnProperty; |
---|
175 | if (!is(_hasOwnProperty, undefined) && !is(_hasOwnProperty.call, undefined)) { |
---|
176 | hasOwnProperty = function (object, property) { |
---|
177 | return _hasOwnProperty.call(object, property); |
---|
178 | }; |
---|
179 | } |
---|
180 | else { |
---|
181 | hasOwnProperty = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ |
---|
182 | return ((property in object) && is(object.constructor.prototype[property], undefined)); |
---|
183 | }; |
---|
184 | } |
---|
185 | |
---|
186 | /** |
---|
187 | * set_css applies given styles to the Modernizr DOM node. |
---|
188 | */ |
---|
189 | function set_css( str ) { |
---|
190 | m_style.cssText = str; |
---|
191 | } |
---|
192 | |
---|
193 | /** |
---|
194 | * set_css_all extrapolates all vendor-specific css strings. |
---|
195 | */ |
---|
196 | function set_css_all( str1, str2 ) { |
---|
197 | return set_css(prefixes.join(str1 + ';') + ( str2 || '' )); |
---|
198 | } |
---|
199 | |
---|
200 | /** |
---|
201 | * is returns a boolean for if typeof obj is exactly type. |
---|
202 | */ |
---|
203 | function is( obj, type ) { |
---|
204 | return typeof obj === type; |
---|
205 | } |
---|
206 | |
---|
207 | /** |
---|
208 | * contains returns a boolean for if substr is found within str. |
---|
209 | */ |
---|
210 | function contains( str, substr ) { |
---|
211 | return (''+str).indexOf( substr ) !== -1; |
---|
212 | } |
---|
213 | |
---|
214 | /** |
---|
215 | * test_props is a generic CSS / DOM property test; if a browser supports |
---|
216 | * a certain property, it won't return undefined for it. |
---|
217 | * A supported CSS property returns empty string when its not yet set. |
---|
218 | */ |
---|
219 | function test_props( props, callback ) { |
---|
220 | for ( var i in props ) { |
---|
221 | if ( m_style[ props[i] ] !== undefined && ( !callback || callback( props[i], modElem ) ) ) { |
---|
222 | return true; |
---|
223 | } |
---|
224 | } |
---|
225 | } |
---|
226 | |
---|
227 | /** |
---|
228 | * test_props_all tests a list of DOM properties we want to check against. |
---|
229 | * We specify literally ALL possible (known and/or likely) properties on |
---|
230 | * the element including the non-vendor prefixed one, for forward- |
---|
231 | * compatibility. |
---|
232 | */ |
---|
233 | function test_props_all( prop, callback ) { |
---|
234 | |
---|
235 | var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1), |
---|
236 | props = (prop + ' ' + domPrefixes.join(uc_prop + ' ') + uc_prop).split(' '); |
---|
237 | |
---|
238 | return !!test_props( props, callback ); |
---|
239 | } |
---|
240 | |
---|
241 | |
---|
242 | /** |
---|
243 | * Tests |
---|
244 | * ----- |
---|
245 | */ |
---|
246 | |
---|
247 | tests['flexbox'] = function() { |
---|
248 | /** |
---|
249 | * set_prefixed_value_css sets the property of a specified element |
---|
250 | * adding vendor prefixes to the VALUE of the property. |
---|
251 | * @param {Element} element |
---|
252 | * @param {string} property The property name. This will not be prefixed. |
---|
253 | * @param {string} value The value of the property. This WILL be prefixed. |
---|
254 | * @param {string=} extra Additional CSS to append unmodified to the end of |
---|
255 | * the CSS string. |
---|
256 | */ |
---|
257 | function set_prefixed_value_css(element, property, value, extra) { |
---|
258 | property += ':'; |
---|
259 | element.style.cssText = (property + prefixes.join(value + ';' + property)).slice(0, -property.length) + (extra || ''); |
---|
260 | } |
---|
261 | |
---|
262 | /** |
---|
263 | * set_prefixed_property_css sets the property of a specified element |
---|
264 | * adding vendor prefixes to the NAME of the property. |
---|
265 | * @param {Element} element |
---|
266 | * @param {string} property The property name. This WILL be prefixed. |
---|
267 | * @param {string} value The value of the property. This will not be prefixed. |
---|
268 | * @param {string=} extra Additional CSS to append unmodified to the end of |
---|
269 | * the CSS string. |
---|
270 | */ |
---|
271 | function set_prefixed_property_css(element, property, value, extra) { |
---|
272 | element.style.cssText = prefixes.join(property + ':' + value + ';') + (extra || ''); |
---|
273 | } |
---|
274 | |
---|
275 | var c = document.createElement('div'), |
---|
276 | elem = document.createElement('div'); |
---|
277 | |
---|
278 | set_prefixed_value_css(c, 'display', 'box', 'width:42px;padding:0;'); |
---|
279 | set_prefixed_property_css(elem, 'box-flex', '1', 'width:10px;'); |
---|
280 | |
---|
281 | c.appendChild(elem); |
---|
282 | docElement.appendChild(c); |
---|
283 | |
---|
284 | var ret = elem.offsetWidth === 42; |
---|
285 | |
---|
286 | c.removeChild(elem); |
---|
287 | docElement.removeChild(c); |
---|
288 | |
---|
289 | return ret; |
---|
290 | }; |
---|
291 | |
---|
292 | // On the S60 and BB Storm, getContext exists, but always returns undefined |
---|
293 | // http://github.com/Modernizr/Modernizr/issues/issue/97/ |
---|
294 | |
---|
295 | tests['canvas'] = function() { |
---|
296 | var elem = document.createElement( 'canvas' ); |
---|
297 | return !!(elem.getContext && elem.getContext('2d')); |
---|
298 | }; |
---|
299 | |
---|
300 | tests['canvastext'] = function() { |
---|
301 | return !!(ret['canvas'] && is(document.createElement( 'canvas' ).getContext('2d').fillText, 'function')); |
---|
302 | }; |
---|
303 | |
---|
304 | // This WebGL test false positives in FF depending on graphics hardware. But really it's quite impossible to know |
---|
305 | // wether webgl will succeed until after you create the context. You might have hardware that can support |
---|
306 | // a 100x100 webgl canvas, but will not support a 1000x1000 webgl canvas. So this feature inference is weak, |
---|
307 | // but intentionally so. |
---|
308 | tests['webgl'] = function(){ |
---|
309 | return !!window.WebGLRenderingContext; |
---|
310 | }; |
---|
311 | |
---|
312 | /* |
---|
313 | * The Modernizr.touch test only indicates if the browser supports |
---|
314 | * touch events, which does not necessarily reflect a touchscreen |
---|
315 | * device, as evidenced by tablets running Windows 7 or, alas, |
---|
316 | * the Palm Pre / WebOS (touch) phones. |
---|
317 | * |
---|
318 | * Additionally, Chrome (desktop) used to lie about its support on this, |
---|
319 | * but that has since been rectified: http://crbug.com/36415 |
---|
320 | * |
---|
321 | * We also test for Firefox 4 Multitouch Support. |
---|
322 | * |
---|
323 | * For more info, see: http://modernizr.github.com/Modernizr/touch.html |
---|
324 | */ |
---|
325 | |
---|
326 | tests['touch'] = function() { |
---|
327 | |
---|
328 | return ('ontouchstart' in window) || testMediaQuery('@media ('+prefixes.join('touch-enabled),(')+'modernizr)'); |
---|
329 | |
---|
330 | }; |
---|
331 | |
---|
332 | |
---|
333 | /** |
---|
334 | * geolocation tests for the new Geolocation API specification. |
---|
335 | * This test is a standards compliant-only test; for more complete |
---|
336 | * testing, including a Google Gears fallback, please see: |
---|
337 | * http://code.google.com/p/geo-location-javascript/ |
---|
338 | * or view a fallback solution using google's geo API: |
---|
339 | * http://gist.github.com/366184 |
---|
340 | */ |
---|
341 | tests['geolocation'] = function() { |
---|
342 | return !!navigator.geolocation; |
---|
343 | }; |
---|
344 | |
---|
345 | // Per 1.6: |
---|
346 | // This used to be Modernizr.crosswindowmessaging but the longer |
---|
347 | // name has been deprecated in favor of a shorter and property-matching one. |
---|
348 | // The old API is still available in 1.6, but as of 2.0 will throw a warning, |
---|
349 | // and in the first release thereafter disappear entirely. |
---|
350 | tests['postmessage'] = function() { |
---|
351 | return !!window.postMessage; |
---|
352 | }; |
---|
353 | |
---|
354 | // Web SQL database detection is tricky: |
---|
355 | |
---|
356 | // In chrome incognito mode, openDatabase is truthy, but using it will |
---|
357 | // throw an exception: http://crbug.com/42380 |
---|
358 | // We can create a dummy database, but there is no way to delete it afterwards. |
---|
359 | |
---|
360 | // Meanwhile, Safari users can get prompted on any database creation. |
---|
361 | // If they do, any page with Modernizr will give them a prompt: |
---|
362 | // http://github.com/Modernizr/Modernizr/issues/closed#issue/113 |
---|
363 | |
---|
364 | // We have chosen to allow the Chrome incognito false positive, so that Modernizr |
---|
365 | // doesn't litter the web with these test databases. As a developer, you'll have |
---|
366 | // to account for this gotcha yourself. |
---|
367 | tests['websqldatabase'] = function() { |
---|
368 | var result = !!window.openDatabase; |
---|
369 | /* if (result){ |
---|
370 | try { |
---|
371 | result = !!openDatabase( mod + "testdb", "1.0", mod + "testdb", 2e4); |
---|
372 | } catch(e) { |
---|
373 | } |
---|
374 | } */ |
---|
375 | return result; |
---|
376 | }; |
---|
377 | |
---|
378 | // Vendors have inconsistent prefixing with the experimental Indexed DB: |
---|
379 | // - Firefox is shipping indexedDB in FF4 as moz_indexedDB |
---|
380 | // - Webkit's implementation is accessible through webkitIndexedDB |
---|
381 | // We test both styles. |
---|
382 | tests['indexedDB'] = function(){ |
---|
383 | for (var i = -1, len = domPrefixes.length; ++i < len; ){ |
---|
384 | var prefix = domPrefixes[i].toLowerCase(); |
---|
385 | if (window[prefix + '_indexedDB'] || window[prefix + 'IndexedDB']){ |
---|
386 | return true; |
---|
387 | } |
---|
388 | } |
---|
389 | return false; |
---|
390 | }; |
---|
391 | |
---|
392 | // documentMode logic from YUI to filter out IE8 Compat Mode |
---|
393 | // which false positives. |
---|
394 | tests['hashchange'] = function() { |
---|
395 | return isEventSupported('hashchange', window) && ( document.documentMode === undefined || document.documentMode > 7 ); |
---|
396 | }; |
---|
397 | |
---|
398 | // Per 1.6: |
---|
399 | // This used to be Modernizr.historymanagement but the longer |
---|
400 | // name has been deprecated in favor of a shorter and property-matching one. |
---|
401 | // The old API is still available in 1.6, but as of 2.0 will throw a warning, |
---|
402 | // and in the first release thereafter disappear entirely. |
---|
403 | tests['history'] = function() { |
---|
404 | return !!(window.history && history.pushState); |
---|
405 | }; |
---|
406 | |
---|
407 | tests['draganddrop'] = function() { |
---|
408 | return isEventSupported('dragstart') && isEventSupported('drop'); |
---|
409 | }; |
---|
410 | |
---|
411 | tests['websockets'] = function(){ |
---|
412 | return ('WebSocket' in window); |
---|
413 | }; |
---|
414 | |
---|
415 | |
---|
416 | // http://css-tricks.com/rgba-browser-support/ |
---|
417 | tests['rgba'] = function() { |
---|
418 | // Set an rgba() color and check the returned value |
---|
419 | |
---|
420 | set_css( 'background-color:rgba(150,255,150,.5)' ); |
---|
421 | |
---|
422 | return contains( m_style.backgroundColor, 'rgba' ); |
---|
423 | }; |
---|
424 | |
---|
425 | tests['hsla'] = function() { |
---|
426 | // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally, |
---|
427 | // except IE9 who retains it as hsla |
---|
428 | |
---|
429 | set_css('background-color:hsla(120,40%,100%,.5)' ); |
---|
430 | |
---|
431 | return contains( m_style.backgroundColor, 'rgba' ) || contains( m_style.backgroundColor, 'hsla' ); |
---|
432 | }; |
---|
433 | |
---|
434 | tests['multiplebgs'] = function() { |
---|
435 | // Setting multiple images AND a color on the background shorthand property |
---|
436 | // and then querying the style.background property value for the number of |
---|
437 | // occurrences of "url(" is a reliable method for detecting ACTUAL support for this! |
---|
438 | |
---|
439 | set_css( 'background:url(//:),url(//:),red url(//:)' ); |
---|
440 | |
---|
441 | // If the UA supports multiple backgrounds, there should be three occurrences |
---|
442 | // of the string "url(" in the return value for elem_style.background |
---|
443 | |
---|
444 | return new RegExp("(url\\s*\\(.*?){3}").test(m_style.background); |
---|
445 | }; |
---|
446 | |
---|
447 | |
---|
448 | // In testing support for a given CSS property, it's legit to test: |
---|
449 | // `elem.style[styleName] !== undefined` |
---|
450 | // If the property is supported it will return an empty string, |
---|
451 | // if unsupported it will return undefined. |
---|
452 | |
---|
453 | // We'll take advantage of this quick test and skip setting a style |
---|
454 | // on our modernizr element, but instead just testing undefined vs |
---|
455 | // empty string. |
---|
456 | |
---|
457 | |
---|
458 | tests['backgroundsize'] = function() { |
---|
459 | return test_props_all( 'backgroundSize' ); |
---|
460 | }; |
---|
461 | |
---|
462 | tests['borderimage'] = function() { |
---|
463 | return test_props_all( 'borderImage' ); |
---|
464 | }; |
---|
465 | |
---|
466 | |
---|
467 | // Super comprehensive table about all the unique implementations of |
---|
468 | // border-radius: http://muddledramblings.com/table-of-css3-border-radius-compliance |
---|
469 | |
---|
470 | tests['borderradius'] = function() { |
---|
471 | return test_props_all( 'borderRadius', '', function( prop ) { |
---|
472 | return contains( prop, 'orderRadius' ); |
---|
473 | }); |
---|
474 | }; |
---|
475 | |
---|
476 | // WebOS unfortunately false positives on this test. |
---|
477 | tests['boxshadow'] = function() { |
---|
478 | return test_props_all( 'boxShadow' ); |
---|
479 | }; |
---|
480 | |
---|
481 | // FF3.0 will false positive on this test |
---|
482 | tests['textshadow'] = function(){ |
---|
483 | return document.createElement('div').style.textShadow === ''; |
---|
484 | }; |
---|
485 | |
---|
486 | |
---|
487 | tests['opacity'] = function() { |
---|
488 | // Browsers that actually have CSS Opacity implemented have done so |
---|
489 | // according to spec, which means their return values are within the |
---|
490 | // range of [0.0,1.0] - including the leading zero. |
---|
491 | |
---|
492 | set_css_all( 'opacity:.55' ); |
---|
493 | |
---|
494 | // The non-literal . in this regex is intentional: |
---|
495 | // German Chrome returns this value as 0,55 |
---|
496 | // https://github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632 |
---|
497 | return /^0.55$/.test(m_style.opacity); |
---|
498 | }; |
---|
499 | |
---|
500 | |
---|
501 | tests['cssanimations'] = function() { |
---|
502 | return test_props_all( 'animationName' ); |
---|
503 | }; |
---|
504 | |
---|
505 | |
---|
506 | tests['csscolumns'] = function() { |
---|
507 | return test_props_all( 'columnCount' ); |
---|
508 | }; |
---|
509 | |
---|
510 | |
---|
511 | tests['cssgradients'] = function() { |
---|
512 | /** |
---|
513 | * For CSS Gradients syntax, please see: |
---|
514 | * http://webkit.org/blog/175/introducing-css-gradients/ |
---|
515 | * https://developer.mozilla.org/en/CSS/-moz-linear-gradient |
---|
516 | * https://developer.mozilla.org/en/CSS/-moz-radial-gradient |
---|
517 | * http://dev.w3.org/csswg/css3-images/#gradients- |
---|
518 | */ |
---|
519 | |
---|
520 | var str1 = 'background-image:', |
---|
521 | str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));', |
---|
522 | str3 = 'linear-gradient(left top,#9f9, white);'; |
---|
523 | |
---|
524 | set_css( |
---|
525 | (str1 + prefixes.join(str2 + str1) + prefixes.join(str3 + str1)).slice(0,-str1.length) |
---|
526 | ); |
---|
527 | |
---|
528 | return contains( m_style.backgroundImage, 'gradient' ); |
---|
529 | }; |
---|
530 | |
---|
531 | |
---|
532 | tests['cssreflections'] = function() { |
---|
533 | return test_props_all( 'boxReflect' ); |
---|
534 | }; |
---|
535 | |
---|
536 | |
---|
537 | tests['csstransforms'] = function() { |
---|
538 | return !!test_props([ 'transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ]); |
---|
539 | }; |
---|
540 | |
---|
541 | |
---|
542 | tests['csstransforms3d'] = function() { |
---|
543 | |
---|
544 | var ret = !!test_props([ 'perspectiveProperty', 'WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective' ]); |
---|
545 | |
---|
546 | // Webkitâs 3D transforms are passed off to the browser's own graphics renderer. |
---|
547 | // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in |
---|
548 | // some conditions. As a result, Webkit typically recognizes the syntax but |
---|
549 | // will sometimes throw a false positive, thus we must do a more thorough check: |
---|
550 | if (ret && 'webkitPerspective' in docElement.style){ |
---|
551 | |
---|
552 | // Webkit allows this media query to succeed only if the feature is enabled. |
---|
553 | // `@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-ms-transform-3d),(-webkit-transform-3d),(modernizr){ ... }` |
---|
554 | ret = testMediaQuery('@media ('+prefixes.join('transform-3d),(')+'modernizr)'); |
---|
555 | } |
---|
556 | return ret; |
---|
557 | }; |
---|
558 | |
---|
559 | |
---|
560 | tests['csstransitions'] = function() { |
---|
561 | return test_props_all( 'transitionProperty' ); |
---|
562 | }; |
---|
563 | |
---|
564 | |
---|
565 | // @font-face detection routine by Diego Perini |
---|
566 | // http://javascript.nwbox.com/CSSSupport/ |
---|
567 | tests['fontface'] = function(){ |
---|
568 | |
---|
569 | var |
---|
570 | sheet, bool, |
---|
571 | head = docHead || docElement, |
---|
572 | style = document.createElement("style"), |
---|
573 | impl = document.implementation || { hasFeature: function() { return false; } }; |
---|
574 | |
---|
575 | style.type = 'text/css'; |
---|
576 | head.insertBefore(style, head.firstChild); |
---|
577 | sheet = style.sheet || style.styleSheet; |
---|
578 | |
---|
579 | var supportAtRule = impl.hasFeature('CSS2', '') ? |
---|
580 | function(rule) { |
---|
581 | if (!(sheet && rule)) return false; |
---|
582 | var result = false; |
---|
583 | try { |
---|
584 | sheet.insertRule(rule, 0); |
---|
585 | result = (/src/i).test(sheet.cssRules[0].cssText); |
---|
586 | sheet.deleteRule(sheet.cssRules.length - 1); |
---|
587 | } catch(e) { } |
---|
588 | return result; |
---|
589 | } : |
---|
590 | function(rule) { |
---|
591 | if (!(sheet && rule)) return false; |
---|
592 | sheet.cssText = rule; |
---|
593 | |
---|
594 | return sheet.cssText.length !== 0 && (/src/i).test(sheet.cssText) && |
---|
595 | sheet.cssText |
---|
596 | .replace(/\r+|\n+/g, '') |
---|
597 | .indexOf(rule.split(' ')[0]) === 0; |
---|
598 | }; |
---|
599 | |
---|
600 | bool = supportAtRule('@font-face { font-family: "font"; src: url(data:,); }'); |
---|
601 | head.removeChild(style); |
---|
602 | return bool; |
---|
603 | }; |
---|
604 | |
---|
605 | |
---|
606 | // These tests evaluate support of the video/audio elements, as well as |
---|
607 | // testing what types of content they support. |
---|
608 | // |
---|
609 | // We're using the Boolean constructor here, so that we can extend the value |
---|
610 | // e.g. Modernizr.video // true |
---|
611 | // Modernizr.video.ogg // 'probably' |
---|
612 | // |
---|
613 | // Codec values from : http://github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 |
---|
614 | // thx to NielsLeenheer and zcorpan |
---|
615 | |
---|
616 | // Note: in FF 3.5.1 and 3.5.0, "no" was a return value instead of empty string. |
---|
617 | // Modernizr does not normalize for that. |
---|
618 | |
---|
619 | tests['video'] = function() { |
---|
620 | var elem = document.createElement('video'), |
---|
621 | bool = !!elem.canPlayType; |
---|
622 | |
---|
623 | if (bool){ |
---|
624 | bool = new Boolean(bool); |
---|
625 | bool.ogg = elem.canPlayType('video/ogg; codecs="theora"'); |
---|
626 | |
---|
627 | // Workaround required for IE9, which doesn't report video support without audio codec specified. |
---|
628 | // bug 599718 @ msft connect |
---|
629 | var h264 = 'video/mp4; codecs="avc1.42E01E'; |
---|
630 | bool.h264 = elem.canPlayType(h264 + '"') || elem.canPlayType(h264 + ', mp4a.40.2"'); |
---|
631 | |
---|
632 | bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"'); |
---|
633 | } |
---|
634 | return bool; |
---|
635 | }; |
---|
636 | |
---|
637 | tests['audio'] = function() { |
---|
638 | var elem = document.createElement('audio'), |
---|
639 | bool = !!elem.canPlayType; |
---|
640 | |
---|
641 | if (bool){ |
---|
642 | bool = new Boolean(bool); |
---|
643 | bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"'); |
---|
644 | bool.mp3 = elem.canPlayType('audio/mpeg;'); |
---|
645 | |
---|
646 | // Mimetypes accepted: |
---|
647 | // https://developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements |
---|
648 | // http://bit.ly/iphoneoscodecs |
---|
649 | bool.wav = elem.canPlayType('audio/wav; codecs="1"'); |
---|
650 | bool.m4a = elem.canPlayType('audio/x-m4a;') || elem.canPlayType('audio/aac;'); |
---|
651 | } |
---|
652 | return bool; |
---|
653 | }; |
---|
654 | |
---|
655 | |
---|
656 | // Firefox has made these tests rather unfun. |
---|
657 | |
---|
658 | // In FF4, if disabled, window.localStorage should === null. |
---|
659 | |
---|
660 | // Normally, we could not test that directly and need to do a |
---|
661 | // `('localStorage' in window) && ` test first because otherwise Firefox will |
---|
662 | // throw http://bugzil.la/365772 if cookies are disabled |
---|
663 | |
---|
664 | // However, in Firefox 4 betas, if dom.storage.enabled == false, just mentioning |
---|
665 | // the property will throw an exception. http://bugzil.la/599479 |
---|
666 | // This looks to be fixed for FF4 Final. |
---|
667 | |
---|
668 | // Because we are forced to try/catch this, we'll go aggressive. |
---|
669 | |
---|
670 | // FWIW: IE8 Compat mode supports these features completely: |
---|
671 | // http://www.quirksmode.org/dom/html5.html |
---|
672 | // But IE8 doesn't support either with local files |
---|
673 | |
---|
674 | tests['localstorage'] = function() { |
---|
675 | try { |
---|
676 | return !!localStorage.getItem; |
---|
677 | } catch(e) { |
---|
678 | return false; |
---|
679 | } |
---|
680 | }; |
---|
681 | |
---|
682 | tests['sessionstorage'] = function() { |
---|
683 | try { |
---|
684 | return !!sessionStorage.getItem; |
---|
685 | } catch(e){ |
---|
686 | return false; |
---|
687 | } |
---|
688 | }; |
---|
689 | |
---|
690 | |
---|
691 | tests['webWorkers'] = function () { |
---|
692 | return !!window.Worker; |
---|
693 | }; |
---|
694 | |
---|
695 | |
---|
696 | tests['applicationcache'] = function() { |
---|
697 | return !!window.applicationCache; |
---|
698 | }; |
---|
699 | |
---|
700 | |
---|
701 | // Thanks to Erik Dahlstrom |
---|
702 | tests['svg'] = function(){ |
---|
703 | return !!document.createElementNS && !!document.createElementNS(ns.svg, "svg").createSVGRect; |
---|
704 | }; |
---|
705 | |
---|
706 | tests['inlinesvg'] = function() { |
---|
707 | var div = document.createElement('div'); |
---|
708 | div.innerHTML = '<svg/>'; |
---|
709 | return (div.firstChild && div.firstChild.namespaceURI) == ns.svg; |
---|
710 | }; |
---|
711 | |
---|
712 | // Thanks to F1lt3r and lucideer |
---|
713 | // http://github.com/Modernizr/Modernizr/issues#issue/35 |
---|
714 | tests['smil'] = function(){ |
---|
715 | return !!document.createElementNS && /SVG/.test(tostring.call(document.createElementNS(ns.svg,'animate'))); |
---|
716 | }; |
---|
717 | |
---|
718 | tests['svgclippaths'] = function(){ |
---|
719 | // Possibly returns a false positive in Safari 3.2? |
---|
720 | return !!document.createElementNS && /SVG/.test(tostring.call(document.createElementNS(ns.svg,'clipPath'))); |
---|
721 | }; |
---|
722 | |
---|
723 | |
---|
724 | // input features and input types go directly onto the ret object, bypassing the tests loop. |
---|
725 | // Hold this guy to execute in a moment. |
---|
726 | function webforms(){ |
---|
727 | |
---|
728 | // Run through HTML5's new input attributes to see if the UA understands any. |
---|
729 | // We're using f which is the <input> element created early on |
---|
730 | // Mike Taylr has created a comprehensive resource for testing these attributes |
---|
731 | // when applied to all input types: |
---|
732 | // http://miketaylr.com/code/input-type-attr.html |
---|
733 | // spec: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary |
---|
734 | ret['input'] = (function(props) { |
---|
735 | for (var i = 0, len = props.length; i<len; i++) { |
---|
736 | attrs[ props[i] ] = !!(props[i] in inputElem); |
---|
737 | } |
---|
738 | return attrs; |
---|
739 | })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' ')); |
---|
740 | |
---|
741 | // Run through HTML5's new input types to see if the UA understands any. |
---|
742 | // This is put behind the tests runloop because it doesn't return a |
---|
743 | // true/false like all the other tests; instead, it returns an object |
---|
744 | // containing each input type with its corresponding true/false value |
---|
745 | |
---|
746 | // Big thanks to @miketaylr for the html5 forms expertise. http://miketaylr.com/ |
---|
747 | ret['inputtypes'] = (function(props) { |
---|
748 | |
---|
749 | for (var i = 0, bool, inputElemType, defaultView, len=props.length; i < len; i++) { |
---|
750 | |
---|
751 | inputElem.setAttribute('type', inputElemType = props[i]); |
---|
752 | bool = inputElem.type !== 'text'; |
---|
753 | |
---|
754 | // We first check to see if the type we give it sticks.. |
---|
755 | // If the type does, we feed it a textual value, which shouldn't be valid. |
---|
756 | // If the value doesn't stick, we know there's input sanitization which infers a custom UI |
---|
757 | if (bool){ |
---|
758 | |
---|
759 | inputElem.value = smile; |
---|
760 | inputElem.style.cssText = 'position:absolute;visibility:hidden;'; |
---|
761 | |
---|
762 | if (/^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined){ |
---|
763 | |
---|
764 | docElement.appendChild(inputElem); |
---|
765 | defaultView = document.defaultView; |
---|
766 | |
---|
767 | // Safari 2-4 allows the smiley as a value, despite making a slider |
---|
768 | bool = defaultView.getComputedStyle && |
---|
769 | defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && |
---|
770 | // Mobile android web browser has false positive, so must |
---|
771 | // check the height to see if the widget is actually there. |
---|
772 | (inputElem.offsetHeight !== 0); |
---|
773 | |
---|
774 | docElement.removeChild(inputElem); |
---|
775 | |
---|
776 | } else if (/^(search|tel)$/.test(inputElemType)){ |
---|
777 | // Spec doesnt define any special parsing or detectable UI |
---|
778 | // behaviors so we pass these through as true |
---|
779 | |
---|
780 | // Interestingly, opera fails the earlier test, so it doesn't |
---|
781 | // even make it here. |
---|
782 | |
---|
783 | } else if (/^(url|email)$/.test(inputElemType)) { |
---|
784 | // Real url and email support comes with prebaked validation. |
---|
785 | bool = inputElem.checkValidity && inputElem.checkValidity() === false; |
---|
786 | |
---|
787 | } else if (/^color$/.test(inputElemType)) { |
---|
788 | // chuck into DOM and force reflow for Opera bug in 11.00 |
---|
789 | // github.com/Modernizr/Modernizr/issues#issue/159 |
---|
790 | docElement.appendChild(inputElem); |
---|
791 | docElement.offsetWidth; |
---|
792 | bool = inputElem.value != smile; |
---|
793 | docElement.removeChild(inputElem); |
---|
794 | |
---|
795 | } else { |
---|
796 | // If the upgraded input compontent rejects the :) text, we got a winner |
---|
797 | bool = inputElem.value != smile; |
---|
798 | } |
---|
799 | } |
---|
800 | |
---|
801 | inputs[ props[i] ] = !!bool; |
---|
802 | } |
---|
803 | return inputs; |
---|
804 | })('search tel url email datetime date month week time datetime-local number range color'.split(' ')); |
---|
805 | |
---|
806 | } |
---|
807 | |
---|
808 | |
---|
809 | |
---|
810 | // End of test definitions |
---|
811 | // ----------------------- |
---|
812 | |
---|
813 | |
---|
814 | |
---|
815 | // Run through all tests and detect their support in the current UA. |
---|
816 | // todo: hypothetically we could be doing an array of tests and use a basic loop here. |
---|
817 | for ( var feature in tests ) { |
---|
818 | if ( hasOwnProperty( tests, feature ) ) { |
---|
819 | // run the test, throw the return value into the Modernizr, |
---|
820 | // then based on that boolean, define an appropriate className |
---|
821 | // and push it into an array of classes we'll join later. |
---|
822 | featurename = feature.toLowerCase(); |
---|
823 | ret[ featurename ] = tests[ feature ](); |
---|
824 | |
---|
825 | classes.push( ( ret[ featurename ] ? '' : 'no-' ) + featurename ); |
---|
826 | } |
---|
827 | } |
---|
828 | |
---|
829 | // input tests need to run. |
---|
830 | if (!ret.input) webforms(); |
---|
831 | |
---|
832 | |
---|
833 | |
---|
834 | // Per 1.6: deprecated API is still accesible for now: |
---|
835 | ret.crosswindowmessaging = ret.postmessage; |
---|
836 | ret.historymanagement = ret.history; |
---|
837 | |
---|
838 | |
---|
839 | |
---|
840 | /** |
---|
841 | * Addtest allows the user to define their own feature tests |
---|
842 | * the result will be added onto the Modernizr object, |
---|
843 | * as well as an appropriate className set on the html element |
---|
844 | * |
---|
845 | * @param feature - String naming the feature |
---|
846 | * @param test - Function returning true if feature is supported, false if not |
---|
847 | */ |
---|
848 | ret.addTest = function (feature, test) { |
---|
849 | feature = feature.toLowerCase(); |
---|
850 | |
---|
851 | if (ret[ feature ]) { |
---|
852 | return; // quit if you're trying to overwrite an existing test |
---|
853 | } |
---|
854 | test = !!(test()); |
---|
855 | docElement.className += ' ' + (test ? '' : 'no-') + feature; |
---|
856 | ret[ feature ] = test; |
---|
857 | return ret; // allow chaining. |
---|
858 | }; |
---|
859 | |
---|
860 | /** |
---|
861 | * Reset m.style.cssText to nothing to reduce memory footprint. |
---|
862 | */ |
---|
863 | set_css( '' ); |
---|
864 | modElem = inputElem = null; |
---|
865 | |
---|
866 | //>>BEGIN IEPP |
---|
867 | // Enable HTML 5 elements for styling in IE. |
---|
868 | // fyi: jscript version does not reflect trident version |
---|
869 | // therefore ie9 in ie7 mode will still have a jScript v.9 |
---|
870 | if ( enableHTML5 && window.attachEvent && (function(){ var elem = document.createElement("div"); |
---|
871 | elem.innerHTML = "<elem></elem>"; |
---|
872 | return elem.childNodes.length !== 1; })()) { |
---|
873 | // iepp v1.6.2 by @jon_neal : code.google.com/p/ie-print-protector |
---|
874 | (function(win, doc) { |
---|
875 | var elems = 'abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video', |
---|
876 | elemsArr = elems.split('|'), |
---|
877 | elemsArrLen = elemsArr.length, |
---|
878 | elemRegExp = new RegExp('(^|\\s)('+elems+')', 'gi'), |
---|
879 | tagRegExp = new RegExp('<(\/*)('+elems+')', 'gi'), |
---|
880 | ruleRegExp = new RegExp('(^|[^\\n]*?\\s)('+elems+')([^\\n]*)({[\\n\\w\\W]*?})', 'gi'), |
---|
881 | docFrag = doc.createDocumentFragment(), |
---|
882 | html = doc.documentElement, |
---|
883 | head = html.firstChild, |
---|
884 | bodyElem = doc.createElement('body'), |
---|
885 | styleElem = doc.createElement('style'), |
---|
886 | body; |
---|
887 | function shim(doc) { |
---|
888 | var a = -1; |
---|
889 | while (++a < elemsArrLen) |
---|
890 | // Use createElement so IE allows HTML5-named elements in a document |
---|
891 | doc.createElement(elemsArr[a]); |
---|
892 | } |
---|
893 | function getCSS(styleSheetList, mediaType) { |
---|
894 | var a = -1, |
---|
895 | len = styleSheetList.length, |
---|
896 | styleSheet, |
---|
897 | cssTextArr = []; |
---|
898 | while (++a < len) { |
---|
899 | styleSheet = styleSheetList[a]; |
---|
900 | // Get css from all non-screen stylesheets and their imports |
---|
901 | if ((mediaType = styleSheet.media || mediaType) != 'screen') cssTextArr.push(getCSS(styleSheet.imports, mediaType), styleSheet.cssText); |
---|
902 | } |
---|
903 | return cssTextArr.join(''); |
---|
904 | } |
---|
905 | // Shim the document and iepp fragment |
---|
906 | shim(doc); |
---|
907 | shim(docFrag); |
---|
908 | // Add iepp custom print style element |
---|
909 | head.insertBefore(styleElem, head.firstChild); |
---|
910 | styleElem.media = 'print'; |
---|
911 | win.attachEvent( |
---|
912 | 'onbeforeprint', |
---|
913 | function() { |
---|
914 | var a = -1, |
---|
915 | cssText = getCSS(doc.styleSheets, 'all'), |
---|
916 | cssTextArr = [], |
---|
917 | rule; |
---|
918 | body = body || doc.body; |
---|
919 | // Get only rules which reference HTML5 elements by name |
---|
920 | while ((rule = ruleRegExp.exec(cssText)) != null) |
---|
921 | // Replace all html5 element references with iepp substitute classnames |
---|
922 | cssTextArr.push((rule[1]+rule[2]+rule[3]).replace(elemRegExp, '$1.iepp_$2')+rule[4]); |
---|
923 | // Write iepp custom print CSS |
---|
924 | styleElem.styleSheet.cssText = cssTextArr.join('\n'); |
---|
925 | while (++a < elemsArrLen) { |
---|
926 | var nodeList = doc.getElementsByTagName(elemsArr[a]), |
---|
927 | nodeListLen = nodeList.length, |
---|
928 | b = -1; |
---|
929 | while (++b < nodeListLen) |
---|
930 | if (nodeList[b].className.indexOf('iepp_') < 0) |
---|
931 | // Append iepp substitute classnames to all html5 elements |
---|
932 | nodeList[b].className += ' iepp_'+elemsArr[a]; |
---|
933 | } |
---|
934 | docFrag.appendChild(body); |
---|
935 | html.appendChild(bodyElem); |
---|
936 | // Write iepp substitute print-safe document |
---|
937 | bodyElem.className = body.className; |
---|
938 | // Replace HTML5 elements with <font> which is print-safe and shouldn't conflict since it isn't part of html5 |
---|
939 | bodyElem.innerHTML = body.innerHTML.replace(tagRegExp, '<$1font'); |
---|
940 | } |
---|
941 | ); |
---|
942 | win.attachEvent( |
---|
943 | 'onafterprint', |
---|
944 | function() { |
---|
945 | // Undo everything done in onbeforeprint |
---|
946 | bodyElem.innerHTML = ''; |
---|
947 | html.removeChild(bodyElem); |
---|
948 | html.appendChild(body); |
---|
949 | styleElem.styleSheet.cssText = ''; |
---|
950 | } |
---|
951 | ); |
---|
952 | })(window, document); |
---|
953 | } |
---|
954 | //>>END IEPP |
---|
955 | |
---|
956 | // Assign private properties to the return object with prefix |
---|
957 | ret._enableHTML5 = enableHTML5; |
---|
958 | ret._version = version; |
---|
959 | |
---|
960 | // Remove "no-js" class from <html> element, if it exists: |
---|
961 | docElement.className = docElement.className.replace(/\bno-js\b/,'') |
---|
962 | + ' js ' |
---|
963 | |
---|
964 | // Add the new classes to the <html> element. |
---|
965 | + classes.join( ' ' ); |
---|
966 | |
---|
967 | return ret; |
---|
968 | |
---|
969 | })(this,this.document); |
---|