2016-12-15 17:55:40 +03:00
/ * !
* modernizr v3 . 3.1
* Build http : //modernizr.com/download?-csstransforms3d-emoji-fontface-svg-addtest-setclasses-dontmin
*
* Copyright ( c )
* Faruk Ates
* Paul Irish
* Alex Sexton
* Ryan Seddon
* Patrick Kettner
* Stu Cox
* Richard Herrera
* MIT License
* /
/ *
* Modernizr tests which native CSS3 and HTML5 features are available in the
* current UA and makes the results available to you in two ways : as properties on
* a global ` Modernizr ` object , and as classes on the ` <html> ` element . This
* information allows you to progressively enhance your pages with a granular level
* of control over the experience .
* /
; ( function ( window , document , undefined ) {
var tests = [ ] ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
*
* ModernizrProto is the constructor for Modernizr
*
* @ class
* @ access public
* /
var ModernizrProto = {
// The current version, dummy
_version : '3.3.1' ,
// Any settings that don't work as separate modules
// can go in here as configuration.
_config : {
'classPrefix' : '' ,
'enableClasses' : true ,
'enableJSClass' : true ,
'usePrefixes' : true
} ,
// Queue of tests
_q : [ ] ,
// Stub these for people who are listening
on : function ( test , cb ) {
// I don't really think people should do this, but we can
// safe guard it a bit.
// -- NOTE:: this gets WAY overridden in src/addTest for actual async tests.
// This is in case people listen to synchronous tests. I would leave it out,
// but the code to *disallow* sync tests in the real version of this
// function is actually larger than this.
var self = this ;
setTimeout ( function ( ) {
cb ( self [ test ] ) ;
} , 0 ) ;
} ,
addTest : function ( name , fn , options ) {
tests . push ( { name : name , fn : fn , options : options } ) ;
} ,
addAsyncTest : function ( fn ) {
tests . push ( { name : null , fn : fn } ) ;
}
} ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
// Fake some of Object.create so we can force non test results to be non "own" properties.
var Modernizr = function ( ) { } ;
Modernizr . prototype = ModernizrProto ;
// Leak modernizr globally when you `require` it rather than force it here.
// Overwrite name so constructor name is nicer :D
Modernizr = new Modernizr ( ) ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
var classes = [ ] ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
* is returns a boolean if the typeof an obj is exactly type .
*
* @ access private
* @ function is
* @ param { * } obj - A thing we want to check the type of
* @ param { string } type - A string to compare the typeof against
* @ returns { boolean }
* /
function is ( obj , type ) {
return typeof obj === type ;
}
;
/ * *
* Run through all tests and detect their support in the current UA .
*
* @ access private
* /
function testRunner ( ) {
var featureNames ;
var feature ;
var aliasIdx ;
var result ;
var nameIdx ;
var featureName ;
var featureNameSplit ;
for ( var featureIdx in tests ) {
if ( tests . hasOwnProperty ( featureIdx ) ) {
featureNames = [ ] ;
feature = tests [ featureIdx ] ;
// run the test, throw the return value into the Modernizr,
// then based on that boolean, define an appropriate className
// and push it into an array of classes we'll join later.
//
// If there is no name, it's an 'async' test that is run,
// but not directly added to the object. That should
// be done with a post-run addTest call.
if ( feature . name ) {
featureNames . push ( feature . name . toLowerCase ( ) ) ;
if ( feature . options && feature . options . aliases && feature . options . aliases . length ) {
// Add all the aliases into the names list
for ( aliasIdx = 0 ; aliasIdx < feature . options . aliases . length ; aliasIdx ++ ) {
featureNames . push ( feature . options . aliases [ aliasIdx ] . toLowerCase ( ) ) ;
}
}
}
// Run the test, or use the raw value if it's not a function
result = is ( feature . fn , 'function' ) ? feature . fn ( ) : feature . fn ;
// Set each of the names on the Modernizr object
for ( nameIdx = 0 ; nameIdx < featureNames . length ; nameIdx ++ ) {
featureName = featureNames [ nameIdx ] ;
// Support dot properties as sub tests. We don't do checking to make sure
// that the implied parent tests have been added. You must call them in
// order (either in the test, or make the parent test a dependency).
//
// Cap it to TWO to make the logic simple and because who needs that kind of subtesting
// hashtag famous last words
featureNameSplit = featureName . split ( '.' ) ;
if ( featureNameSplit . length === 1 ) {
Modernizr [ featureNameSplit [ 0 ] ] = result ;
} else {
// cast to a Boolean, if not one already
/* jshint -W053 */
if ( Modernizr [ featureNameSplit [ 0 ] ] && ! ( Modernizr [ featureNameSplit [ 0 ] ] instanceof Boolean ) ) {
Modernizr [ featureNameSplit [ 0 ] ] = new Boolean ( Modernizr [ featureNameSplit [ 0 ] ] ) ;
}
Modernizr [ featureNameSplit [ 0 ] ] [ featureNameSplit [ 1 ] ] = result ;
}
classes . push ( ( result ? '' : 'no-' ) + featureNameSplit . join ( '-' ) ) ;
}
}
}
}
;
/ * *
* docElement is a convenience wrapper to grab the root element of the document
*
* @ access private
* @ returns { HTMLElement | SVGElement } The root element of the document
* /
var docElement = document . documentElement ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
* A convenience helper to check if the document we are running in is an SVG document
*
* @ access private
* @ returns { boolean }
* /
var isSVG = docElement . nodeName . toLowerCase ( ) === 'svg' ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
* setClasses takes an array of class names and adds them to the root element
*
* @ access private
* @ function setClasses
* @ param { string [ ] } classes - Array of class names
* /
// Pass in an and array of class names, e.g.:
// ['no-webp', 'borderradius', ...]
function setClasses ( classes ) {
var className = docElement . className ;
var classPrefix = Modernizr . _config . classPrefix || '' ;
if ( isSVG ) {
className = className . baseVal ;
}
// Change `no-js` to `js` (independently of the `enableClasses` option)
// Handle classPrefix on this too
if ( Modernizr . _config . enableJSClass ) {
var reJS = new RegExp ( '(^|\\s)' + classPrefix + 'no-js(\\s|$)' ) ;
className = className . replace ( reJS , '$1' + classPrefix + 'js$2' ) ;
}
if ( Modernizr . _config . enableClasses ) {
// Add the new classes
className += ' ' + classPrefix + classes . join ( ' ' + classPrefix ) ;
isSVG ? docElement . className . baseVal = className : docElement . className = className ;
}
}
;
/ * *
* hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support
*
* @ author kangax
* @ access private
* @ function hasOwnProp
* @ param { object } object - The object to check for a property
* @ param { string } property - The property to check for
* @ returns { boolean }
* /
// hasOwnProperty shim by kangax needed for Safari 2.0 support
var hasOwnProp ;
( function ( ) {
var _hasOwnProperty = ( { } ) . hasOwnProperty ;
/* istanbul ignore else */
/ * w e h a v e n o w a y o f t e s t i n g I E 5 . 5 o r s a f a r i 2 ,
* so just assume the else gets hit * /
if ( ! is ( _hasOwnProperty , 'undefined' ) && ! is ( _hasOwnProperty . call , 'undefined' ) ) {
hasOwnProp = function ( object , property ) {
return _hasOwnProperty . call ( object , property ) ;
} ;
}
else {
hasOwnProp = function ( object , property ) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
return ( ( property in object ) && is ( object . constructor . prototype [ property ] , 'undefined' ) ) ;
} ;
}
} ) ( ) ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
// _l tracks listeners for async tests, as well as tests that execute after the initial run
ModernizrProto . _l = { } ;
/ * *
* Modernizr . on is a way to listen for the completion of async tests . Being
* asynchronous , they may not finish before your scripts run . As a result you
* will get a possibly false negative ` undefined ` value .
*
* @ memberof Modernizr
* @ name Modernizr . on
* @ access public
* @ function on
* @ param { string } feature - String name of the feature detect
* @ param { function } cb - Callback function returning a Boolean - true if feature is supported , false if not
* @ example
*
* ` ` ` js
* Modernizr . on ( 'flash' , function ( result ) {
* if ( result ) {
* // the browser has flash
* } else {
* // the browser does not have flash
* }
* } ) ;
* ` ` `
* /
ModernizrProto . on = function ( feature , cb ) {
// Create the list of listeners if it doesn't exist
if ( ! this . _l [ feature ] ) {
this . _l [ feature ] = [ ] ;
}
// Push this test on to the listener list
this . _l [ feature ] . push ( cb ) ;
// If it's already been resolved, trigger it on next tick
if ( Modernizr . hasOwnProperty ( feature ) ) {
// Next Tick
setTimeout ( function ( ) {
Modernizr . _trigger ( feature , Modernizr [ feature ] ) ;
} , 0 ) ;
}
} ;
/ * *
* _trigger is the private function used to signal test completion and run any
* callbacks registered through [ Modernizr . on ] ( # modernizr - on )
*
* @ memberof Modernizr
* @ name Modernizr . _trigger
* @ access private
* @ function _trigger
* @ param { string } feature - string name of the feature detect
* @ param { function | boolean } [ res ] - A feature detection function , or the boolean =
* result of a feature detection function
* /
ModernizrProto . _trigger = function ( feature , res ) {
if ( ! this . _l [ feature ] ) {
return ;
}
var cbs = this . _l [ feature ] ;
// Force async
setTimeout ( function ( ) {
var i , cb ;
for ( i = 0 ; i < cbs . length ; i ++ ) {
cb = cbs [ i ] ;
cb ( res ) ;
}
} , 0 ) ;
// Don't trigger these again
delete this . _l [ feature ] ;
} ;
/ * *
* addTest allows you to define your own feature detects that are not currently
* included in Modernizr ( under the covers it ' s the exact same code Modernizr
* uses for its own [ feature detections ] ( https : //github.com/Modernizr/Modernizr/tree/master/feature-detects)). Just like the offical detects, the result
* will be added onto the Modernizr object , as well as an appropriate className set on
* the html element when configured to do so
*
* @ memberof Modernizr
* @ name Modernizr . addTest
* @ optionName Modernizr . addTest ( )
* @ optionProp addTest
* @ access public
* @ function addTest
* @ param { string | object } feature - The string name of the feature detect , or an
* object of feature detect names and test
* @ param { function | boolean } test - Function returning true if feature is supported ,
* false if not . Otherwise a boolean representing the results of a feature detection
* @ example
*
* The most common way of creating your own feature detects is by calling
* ` Modernizr.addTest ` with a string ( preferably just lowercase , without any
* punctuation ) , and a function you want executed that will return a boolean result
*
* ` ` ` js
* Modernizr . addTest ( 'itsTuesday' , function ( ) {
* var d = new Date ( ) ;
* return d . getDay ( ) === 2 ;
* } ) ;
* ` ` `
*
* When the above is run , it will set Modernizr . itstuesday to ` true ` when it is tuesday ,
* and to ` false ` every other day of the week . One thing to notice is that the names of
* feature detect functions are always lowercased when added to the Modernizr object . That
* means that ` Modernizr.itsTuesday ` will not exist , but ` Modernizr.itstuesday ` will .
*
*
* Since we only look at the returned value from any feature detection function ,
* you do not need to actually use a function . For simple detections , just passing
* in a statement that will return a boolean value works just fine .
*
* ` ` ` js
* Modernizr . addTest ( 'hasJquery' , 'jQuery' in window ) ;
* ` ` `
*
* Just like before , when the above runs ` Modernizr.hasjquery ` will be true if
* jQuery has been included on the page . Not using a function saves a small amount
* of overhead for the browser , as well as making your code much more readable .
*
* Finally , you also have the ability to pass in an object of feature names and
* their tests . This is handy if you want to add multiple detections in one go .
* The keys should always be a string , and the value can be either a boolean or
* function that returns a boolean .
*
* ` ` ` js
* var detects = {
* 'hasjquery' : 'jQuery' in window ,
* 'itstuesday' : function ( ) {
* var d = new Date ( ) ;
* return d . getDay ( ) === 2 ;
* }
* }
*
* Modernizr . addTest ( detects ) ;
* ` ` `
*
* There is really no difference between the first methods and this one , it is
* just a convenience to let you write more readable code .
* /
function addTest ( feature , test ) {
if ( typeof feature == 'object' ) {
for ( var key in feature ) {
if ( hasOwnProp ( feature , key ) ) {
addTest ( key , feature [ key ] ) ;
}
}
} else {
feature = feature . toLowerCase ( ) ;
var featureNameSplit = feature . split ( '.' ) ;
var last = Modernizr [ featureNameSplit [ 0 ] ] ;
// Again, we don't check for parent test existence. Get that right, though.
if ( featureNameSplit . length == 2 ) {
last = last [ featureNameSplit [ 1 ] ] ;
}
if ( typeof last != 'undefined' ) {
// we're going to quit if you're trying to overwrite an existing test
// if we were to allow it, we'd do this:
// var re = new RegExp("\\b(no-)?" + feature + "\\b");
// docElement.className = docElement.className.replace( re, '' );
// but, no rly, stuff 'em.
return Modernizr ;
}
test = typeof test == 'function' ? test ( ) : test ;
// Set the value (this is the magic, right here).
if ( featureNameSplit . length == 1 ) {
Modernizr [ featureNameSplit [ 0 ] ] = test ;
} else {
// cast to a Boolean, if not one already
/* jshint -W053 */
if ( Modernizr [ featureNameSplit [ 0 ] ] && ! ( Modernizr [ featureNameSplit [ 0 ] ] instanceof Boolean ) ) {
Modernizr [ featureNameSplit [ 0 ] ] = new Boolean ( Modernizr [ featureNameSplit [ 0 ] ] ) ;
}
Modernizr [ featureNameSplit [ 0 ] ] [ featureNameSplit [ 1 ] ] = test ;
}
// Set a single class (either `feature` or `no-feature`)
/* jshint -W041 */
setClasses ( [ ( ! ! test && test != false ? '' : 'no-' ) + featureNameSplit . join ( '-' ) ] ) ;
/* jshint +W041 */
// Trigger the event
Modernizr . _trigger ( feature , test ) ;
}
return Modernizr ; // allow chaining.
}
// After all the tests are run, add self to the Modernizr prototype
Modernizr . _q . push ( function ( ) {
ModernizrProto . addTest = addTest ;
} ) ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
* createElement is a convenience wrapper around document . createElement . Since we
* use createElement all over the place , this allows for ( slightly ) smaller code
* as well as abstracting away issues with creating elements in contexts other than
* HTML documents ( e . g . SVG documents ) .
*
* @ access private
* @ function createElement
* @ returns { HTMLElement | SVGElement } An HTML or SVG element
* /
function createElement ( ) {
if ( typeof document . createElement !== 'function' ) {
// This is the case in IE7, where the type of createElement is "object".
// For this reason, we cannot call apply() as Object is not a Function.
return document . createElement ( arguments [ 0 ] ) ;
} else if ( isSVG ) {
return document . createElementNS . call ( document , 'http://www.w3.org/2000/svg' , arguments [ 0 ] ) ;
} else {
return document . createElement . apply ( document , arguments ) ;
}
}
;
/ * !
{
"name" : "Canvas" ,
"property" : "canvas" ,
"caniuse" : "canvas" ,
"tags" : [ "canvas" , "graphics" ] ,
"polyfills" : [ "flashcanvas" , "excanvas" , "slcanvas" , "fxcanvas" ]
}
! * /
/ * D O C
Detects support for the ` <canvas> ` element for 2 D drawing .
* /
// On the S60 and BB Storm, getContext exists, but always returns undefined
// so we actually have to call getContext() to verify
// github.com/Modernizr/Modernizr/issues/issue/97/
Modernizr . addTest ( 'canvas' , function ( ) {
var elem = createElement ( 'canvas' ) ;
return ! ! ( elem . getContext && elem . getContext ( '2d' ) ) ;
} ) ;
/ * !
{
"name" : "Canvas text" ,
"property" : "canvastext" ,
"caniuse" : "canvas-text" ,
"tags" : [ "canvas" , "graphics" ] ,
"polyfills" : [ "canvastext" ]
}
! * /
/ * D O C
Detects support for the text APIs for ` <canvas> ` elements .
* /
Modernizr . addTest ( 'canvastext' , function ( ) {
if ( Modernizr . canvas === false ) {
return false ;
}
return typeof createElement ( 'canvas' ) . getContext ( '2d' ) . fillText == 'function' ;
} ) ;
/ * !
{
"name" : "Emoji" ,
"property" : "emoji"
}
! * /
/ * D O C
Detects support for emoji character sets .
* /
Modernizr . addTest ( 'emoji' , function ( ) {
if ( ! Modernizr . canvastext ) {
return false ;
}
var pixelRatio = window . devicePixelRatio || 1 ;
var offset = 12 * pixelRatio ;
var node = createElement ( 'canvas' ) ;
var ctx = node . getContext ( '2d' ) ;
ctx . fillStyle = '#f00' ;
ctx . textBaseline = 'top' ;
ctx . font = '32px Arial' ;
ctx . fillText ( '\ud83d\udc28' , 0 , 0 ) ; // U+1F428 KOALA
return ctx . getImageData ( offset , offset , 1 , 1 ) . data [ 0 ] !== 0 ;
} ) ;
/ * !
{
"name" : "SVG" ,
"property" : "svg" ,
"caniuse" : "svg" ,
"tags" : [ "svg" ] ,
"authors" : [ "Erik Dahlstrom" ] ,
"polyfills" : [
"svgweb" ,
"raphael" ,
"amplesdk" ,
"canvg" ,
"svg-boilerplate" ,
"sie" ,
"dojogfx" ,
"fabricjs"
]
}
! * /
/ * D O C
Detects support for SVG in ` <embed> ` or ` <object> ` elements .
* /
Modernizr . addTest ( 'svg' , ! ! document . createElementNS && ! ! document . createElementNS ( 'http://www.w3.org/2000/svg' , 'svg' ) . createSVGRect ) ;
/ * *
* getBody returns the body of a document , or an element that can stand in for
* the body if a real body does not exist
*
* @ access private
* @ function getBody
* @ returns { HTMLElement | SVGElement } Returns the real body of a document , or an
* artificially created element that stands in for the body
* /
function getBody ( ) {
// After page load injecting a fake body doesn't work so check if body exists
var body = document . body ;
if ( ! body ) {
// Can't use the real body create a fake one.
body = createElement ( isSVG ? 'svg' : 'body' ) ;
body . fake = true ;
}
return body ;
}
;
/ * *
* injectElementWithStyles injects an element with style element and some CSS rules
*
* @ access private
* @ function injectElementWithStyles
* @ param { string } rule - String representing a css rule
* @ param { function } callback - A function that is used to test the injected element
* @ param { number } [ nodes ] - An integer representing the number of additional nodes you want injected
* @ param { string [ ] } [ testnames ] - An array of strings that are used as ids for the additional nodes
* @ returns { boolean }
* /
function injectElementWithStyles ( rule , callback , nodes , testnames ) {
var mod = 'modernizr' ;
var style ;
var ret ;
var node ;
var docOverflow ;
var div = createElement ( 'div' ) ;
var body = getBody ( ) ;
if ( parseInt ( nodes , 10 ) ) {
// In order not to give false positives we create a node for each test
// This also allows the method to scale for unspecified uses
while ( nodes -- ) {
node = createElement ( 'div' ) ;
node . id = testnames ? testnames [ nodes ] : mod + ( nodes + 1 ) ;
div . appendChild ( node ) ;
}
}
style = createElement ( 'style' ) ;
style . type = 'text/css' ;
style . id = 's' + mod ;
// 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.
// Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
( ! body . fake ? div : body ) . appendChild ( style ) ;
body . appendChild ( div ) ;
if ( style . styleSheet ) {
style . styleSheet . cssText = rule ;
} else {
style . appendChild ( document . createTextNode ( rule ) ) ;
}
div . id = mod ;
if ( body . fake ) {
//avoid crashing IE8, if background image is used
body . style . background = '' ;
//Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
body . style . overflow = 'hidden' ;
docOverflow = docElement . style . overflow ;
docElement . style . overflow = 'hidden' ;
docElement . appendChild ( body ) ;
}
ret = callback ( div , rule ) ;
// If this is done after page load we don't want to remove the body so check if body exists
if ( body . fake ) {
body . parentNode . removeChild ( body ) ;
docElement . style . overflow = docOverflow ;
// Trigger layout so kinetic scrolling isn't disabled in iOS6+
docElement . offsetHeight ;
} else {
div . parentNode . removeChild ( div ) ;
}
return ! ! ret ;
}
;
/ * *
* testStyles injects an element with style element and some CSS rules
*
* @ memberof Modernizr
* @ name Modernizr . testStyles
* @ optionName Modernizr . testStyles ( )
* @ optionProp testStyles
* @ access public
* @ function testStyles
* @ param { string } rule - String representing a css rule
* @ param { function } callback - A function that is used to test the injected element
* @ param { number } [ nodes ] - An integer representing the number of additional nodes you want injected
* @ param { string [ ] } [ testnames ] - An array of strings that are used as ids for the additional nodes
* @ returns { boolean }
* @ example
*
* ` Modernizr.testStyles ` takes a CSS rule and injects it onto the current page
* along with ( possibly multiple ) DOM elements . This lets you check for features
* that can not be detected by simply checking the [ IDL ] ( https : //developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Interface_development_guide/IDL_interface_rules).
*
* ` ` ` js
* Modernizr . testStyles ( '#modernizr { width: 9px; color: papayawhip; }' , function ( elem , rule ) {
* // elem is the first DOM node in the page (by default #modernizr)
* // rule is the first argument you supplied - the CSS rule in string form
*
* addTest ( 'widthworks' , elem . style . width === '9px' )
* } ) ;
* ` ` `
*
* If your test requires multiple nodes , you can include a third argument
* indicating how many additional div elements to include on the page . The
* additional nodes are injected as children of the ` elem ` that is returned as
* the first argument to the callback .
*
* ` ` ` js
* Modernizr . testStyles ( '#modernizr {width: 1px}; #modernizr2 {width: 2px}' , function ( elem ) {
* document . getElementById ( 'modernizr' ) . style . width === '1px' ; // true
* document . getElementById ( 'modernizr2' ) . style . width === '2px' ; // true
* elem . firstChild === document . getElementById ( 'modernizr2' ) ; // true
* } , 1 ) ;
* ` ` `
*
* By default , all of the additional elements have an ID of ` modernizr[n] ` , where
* ` n ` is its index ( e . g . the first additional , second overall is ` #modernizr2 ` ,
* the second additional is ` #modernizr3 ` , etc . ) .
* If you want to have more meaningful IDs for your function , you can provide
* them as the fourth argument , as an array of strings
*
* ` ` ` js
* Modernizr . testStyles ( '#foo {width: 10px}; #bar {height: 20px}' , function ( elem ) {
* elem . firstChild === document . getElementById ( 'foo' ) ; // true
* elem . lastChild === document . getElementById ( 'bar' ) ; // true
* } , 2 , [ 'foo' , 'bar' ] ) ;
* ` ` `
*
* /
var testStyles = ModernizrProto . testStyles = injectElementWithStyles ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * !
{
"name" : "@font-face" ,
"property" : "fontface" ,
"authors" : [ "Diego Perini" , "Mat Marquis" ] ,
"tags" : [ "css" ] ,
"knownBugs" : [
"False Positive: WebOS https://github.com/Modernizr/Modernizr/issues/342" ,
"False Postive: WP7 https://github.com/Modernizr/Modernizr/issues/538"
] ,
"notes" : [ {
"name" : "@font-face detection routine by Diego Perini" ,
"href" : "http://javascript.nwbox.com/CSSSupport/"
} , {
"name" : "Filament Group @font-face compatibility research" ,
"href" : "https://docs.google.com/presentation/d/1n4NyG4uPRjAA8zn_pSQ_Ket0RhcWC6QlZ6LMjKeECo0/edit#slide=id.p"
} , {
"name" : "Filament Grunticon/@font-face device testing results" ,
"href" : "https://docs.google.com/spreadsheet/ccc?key=0Ag5_yGvxpINRdHFYeUJPNnZMWUZKR2ItMEpRTXZPdUE#gid=0"
} , {
"name" : "CSS fonts on Android" ,
"href" : "https://stackoverflow.com/questions/3200069/css-fonts-on-android"
} , {
"name" : "@font-face and Android" ,
"href" : "http://archivist.incutio.com/viewlist/css-discuss/115960"
} ]
}
! * /
var blacklist = ( function ( ) {
var ua = navigator . userAgent ;
var wkvers = ua . match ( /applewebkit\/([0-9]+)/gi ) && parseFloat ( RegExp . $1 ) ;
var webos = ua . match ( /w(eb)?osbrowser/gi ) ;
var wppre8 = ua . match ( /windows phone/gi ) && ua . match ( /iemobile\/([0-9])+/gi ) && parseFloat ( RegExp . $1 ) >= 9 ;
var oldandroid = wkvers < 533 && ua . match ( /android/gi ) ;
return webos || oldandroid || wppre8 ;
} ( ) ) ;
if ( blacklist ) {
Modernizr . addTest ( 'fontface' , false ) ;
} else {
testStyles ( '@font-face {font-family:"font";src:url("https://")}' , function ( node , rule ) {
var style = document . getElementById ( 'smodernizr' ) ;
var sheet = style . sheet || style . styleSheet ;
var cssText = sheet ? ( sheet . cssRules && sheet . cssRules [ 0 ] ? sheet . cssRules [ 0 ] . cssText : sheet . cssText || '' ) : '' ;
var bool = /src/i . test ( cssText ) && cssText . indexOf ( rule . split ( ' ' ) [ 0 ] ) === 0 ;
Modernizr . addTest ( 'fontface' , bool ) ;
} ) ;
}
;
/ * *
* If the browsers follow the spec , then they would expose vendor - specific style as :
* elem . style . WebkitBorderRadius
* instead of something like the following , which would be technically incorrect :
* elem . style . webkitBorderRadius
* Webkit ghosts their properties in lowercase but Opera & Moz do not .
* Microsoft uses a lowercase ` ms ` instead of the correct ` Ms ` in IE8 +
* erik . eae . net / archives / 2008 / 03 / 10 / 21.48 . 10 /
* More here : github . com / Modernizr / Modernizr / issues / issue / 21
*
* @ access private
* @ returns { string } The string representing the vendor - specific style properties
* /
var omPrefixes = 'Moz O ms Webkit' ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
var cssomPrefixes = ( ModernizrProto . _config . usePrefixes ? omPrefixes . split ( ' ' ) : [ ] ) ;
ModernizrProto . _cssomPrefixes = cssomPrefixes ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
* contains checks to see if a string contains another string
*
* @ access private
* @ function contains
* @ param { string } str - The string we want to check for substrings
* @ param { string } substr - The substring we want to search the first string for
* @ returns { boolean }
* /
function contains ( str , substr ) {
return ! ! ~ ( '' + str ) . indexOf ( substr ) ;
}
;
/ * *
* Create our "modernizr" element that we do most feature tests on .
*
* @ access private
* /
var modElem = {
elem : createElement ( 'modernizr' )
} ;
// Clean up this element
Modernizr . _q . push ( function ( ) {
delete modElem . elem ;
} ) ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
var mStyle = {
style : modElem . elem . style
} ;
// kill ref for gc, must happen before mod.elem is removed, so we unshift on to
// the front of the queue.
Modernizr . _q . unshift ( function ( ) {
delete mStyle . style ;
} ) ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
* domToCSS takes a camelCase string and converts it to kebab - case
* e . g . boxSizing - > box - sizing
*
* @ access private
* @ function domToCSS
* @ param { string } name - String name of camelCase prop we want to convert
* @ returns { string } The kebab - case version of the supplied name
* /
function domToCSS ( name ) {
return name . replace ( /([A-Z])/g , function ( str , m1 ) {
return '-' + m1 . toLowerCase ( ) ;
} ) . replace ( /^ms-/ , '-ms-' ) ;
}
;
/ * *
* nativeTestProps allows for us to use native feature detection functionality if available .
* some prefixed form , or false , in the case of an unsupported rule
*
* @ access private
* @ function nativeTestProps
* @ param { array } props - An array of property names
* @ param { string } value - A string representing the value we want to check via @ supports
* @ returns { boolean | undefined } A boolean when @ supports exists , undefined otherwise
* /
// Accepts a list of property names and a single value
// Returns `undefined` if native detection not available
function nativeTestProps ( props , value ) {
var i = props . length ;
// Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface
if ( 'CSS' in window && 'supports' in window . CSS ) {
// Try every prefixed variant of the property
while ( i -- ) {
if ( window . CSS . supports ( domToCSS ( props [ i ] ) , value ) ) {
return true ;
}
}
return false ;
}
// Otherwise fall back to at-rule (for Opera 12.x)
else if ( 'CSSSupportsRule' in window ) {
// Build a condition string for every prefixed variant
var conditionText = [ ] ;
while ( i -- ) {
conditionText . push ( '(' + domToCSS ( props [ i ] ) + ':' + value + ')' ) ;
}
conditionText = conditionText . join ( ' or ' ) ;
return injectElementWithStyles ( '@supports (' + conditionText + ') { #modernizr { position: absolute; } }' , function ( node ) {
return getComputedStyle ( node , null ) . position == 'absolute' ;
} ) ;
}
return undefined ;
}
;
/ * *
* cssToDOM takes a kebab - case string and converts it to camelCase
* e . g . box - sizing - > boxSizing
*
* @ access private
* @ function cssToDOM
* @ param { string } name - String name of kebab - case prop we want to convert
* @ returns { string } The camelCase version of the supplied name
* /
function cssToDOM ( name ) {
return name . replace ( /([a-z])-([a-z])/g , function ( str , m1 , m2 ) {
return m1 + m2 . toUpperCase ( ) ;
} ) . replace ( /^-/ , '' ) ;
}
;
// testProps is a generic CSS / DOM property test.
// In testing support for a given CSS property, it's legit to test:
// `elem.style[styleName] !== undefined`
// If the property is supported it will return an empty string,
// if unsupported it will return undefined.
// We'll take advantage of this quick test and skip setting a style
// on our modernizr element, but instead just testing undefined vs
// empty string.
// Property names can be provided in either camelCase or kebab-case.
function testProps ( props , prefixed , value , skipValueTest ) {
skipValueTest = is ( skipValueTest , 'undefined' ) ? false : skipValueTest ;
// Try native detect first
if ( ! is ( value , 'undefined' ) ) {
var result = nativeTestProps ( props , value ) ;
if ( ! is ( result , 'undefined' ) ) {
return result ;
}
}
// Otherwise do it properly
var afterInit , i , propsLength , prop , before ;
// If we don't have a style element, that means we're running async or after
// the core tests, so we'll need to create our own elements to use
// inside of an SVG element, in certain browsers, the `style` element is only
// defined for valid tags. Therefore, if `modernizr` does not have one, we
// fall back to a less used element and hope for the best.
var elems = [ 'modernizr' , 'tspan' ] ;
while ( ! mStyle . style ) {
afterInit = true ;
mStyle . modElem = createElement ( elems . shift ( ) ) ;
mStyle . style = mStyle . modElem . style ;
}
// Delete the objects if we created them.
function cleanElems ( ) {
if ( afterInit ) {
delete mStyle . style ;
delete mStyle . modElem ;
}
}
propsLength = props . length ;
for ( i = 0 ; i < propsLength ; i ++ ) {
prop = props [ i ] ;
before = mStyle . style [ prop ] ;
if ( contains ( prop , '-' ) ) {
prop = cssToDOM ( prop ) ;
}
if ( mStyle . style [ prop ] !== undefined ) {
// If value to test has been passed in, do a set-and-check test.
// 0 (integer) is a valid property value, so check that `value` isn't
// undefined, rather than just checking it's truthy.
if ( ! skipValueTest && ! is ( value , 'undefined' ) ) {
// Needs a try catch block because of old IE. This is slow, but will
// be avoided in most cases because `skipValueTest` will be used.
try {
mStyle . style [ prop ] = value ;
} catch ( e ) { }
// If the property value has changed, we assume the value used is
// supported. If `value` is empty string, it'll fail here (because
// it hasn't changed), which matches how browsers have implemented
// CSS.supports()
if ( mStyle . style [ prop ] != before ) {
cleanElems ( ) ;
return prefixed == 'pfx' ? prop : true ;
}
}
// Otherwise just return true, or the property name if this is a
// `prefixed()` call
else {
cleanElems ( ) ;
return prefixed == 'pfx' ? prop : true ;
}
}
}
cleanElems ( ) ;
return false ;
}
;
/ * *
* List of JavaScript DOM values used for tests
*
* @ memberof Modernizr
* @ name Modernizr . _domPrefixes
* @ optionName Modernizr . _domPrefixes
* @ optionProp domPrefixes
* @ access public
* @ example
*
* Modernizr . _domPrefixes is exactly the same as [ _prefixes ] ( # modernizr - _prefixes ) , but rather
* than kebab - case properties , all properties are their Capitalized variant
*
* ` ` ` js
* Modernizr . _domPrefixes === [ "Moz" , "O" , "ms" , "Webkit" ] ;
* ` ` `
* /
var domPrefixes = ( ModernizrProto . _config . usePrefixes ? omPrefixes . toLowerCase ( ) . split ( ' ' ) : [ ] ) ;
ModernizrProto . _domPrefixes = domPrefixes ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
* fnBind is a super small [ bind ] ( https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill.
*
* @ access private
* @ function fnBind
* @ param { function } fn - a function you want to change ` this ` reference to
* @ param { object } that - the ` this ` you want to call the function with
* @ returns { function } The wrapped version of the supplied function
* /
function fnBind ( fn , that ) {
return function ( ) {
return fn . apply ( that , arguments ) ;
} ;
}
;
/ * *
* testDOMProps is a generic DOM property test ; if a browser supports
* a certain property , it won ' t return undefined for it .
*
* @ access private
* @ function testDOMProps
* @ param { array . < string > } props - An array of properties to test for
* @ param { object } obj - An object or Element you want to use to test the parameters again
* @ param { boolean | object } elem - An Element to bind the property lookup again . Use ` false ` to prevent the check
* /
function testDOMProps ( props , obj , elem ) {
var item ;
for ( var i in props ) {
if ( props [ i ] in obj ) {
// return the property name as a string
if ( elem === false ) {
return props [ i ] ;
}
item = obj [ props [ i ] ] ;
// let's bind a function
if ( is ( item , 'function' ) ) {
// bind to obj unless overriden
return fnBind ( item , elem || obj ) ;
}
// return the unbound function or obj or value
return item ;
}
}
return false ;
}
;
/ * *
* testPropsAll tests a list of DOM properties we want to check against .
* We specify literally ALL possible ( known and / or likely ) properties on
* the element including the non - vendor prefixed one , for forward -
* compatibility .
*
* @ access private
* @ function testPropsAll
* @ param { string } prop - A string of the property to test for
* @ param { string | object } [ prefixed ] - An object to check the prefixed properties on . Use a string to skip
* @ param { HTMLElement | SVGElement } [ elem ] - An element used to test the property and value against
* @ param { string } [ value ] - A string of a css value
* @ param { boolean } [ skipValueTest ] - An boolean representing if you want to test if value sticks when set
* /
function testPropsAll ( prop , prefixed , elem , value , skipValueTest ) {
var ucProp = prop . charAt ( 0 ) . toUpperCase ( ) + prop . slice ( 1 ) ,
props = ( prop + ' ' + cssomPrefixes . join ( ucProp + ' ' ) + ucProp ) . split ( ' ' ) ;
// did they call .prefixed('boxSizing') or are we just testing a prop?
if ( is ( prefixed , 'string' ) || is ( prefixed , 'undefined' ) ) {
return testProps ( props , prefixed , value , skipValueTest ) ;
// otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
} else {
props = ( prop + ' ' + ( domPrefixes ) . join ( ucProp + ' ' ) + ucProp ) . split ( ' ' ) ;
return testDOMProps ( props , prefixed , elem ) ;
}
}
// Modernizr.testAllProps() investigates whether a given style property,
// or any of its vendor-prefixed variants, is recognized
//
// Note that the property names must be provided in the camelCase variant.
// Modernizr.testAllProps('boxSizing')
ModernizrProto . testAllProps = testPropsAll ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * *
* testAllProps determines whether a given CSS property is supported in the browser
*
* @ memberof Modernizr
* @ name Modernizr . testAllProps
* @ optionName Modernizr . testAllProps ( )
* @ optionProp testAllProps
* @ access public
* @ function testAllProps
* @ param { string } prop - String naming the property to test ( either camelCase or kebab - case )
* @ param { string } [ value ] - String of the value to test
* @ param { boolean } [ skipValueTest = false ] - Whether to skip testing that the value is supported when using non - native detection
* @ example
*
* testAllProps determines whether a given CSS property , in some prefixed form ,
* is supported by the browser .
*
* ` ` ` js
* testAllProps ( 'boxSizing' ) // true
* ` ` `
*
* It can optionally be given a CSS value in string form to test if a property
* value is valid
*
* ` ` ` js
* testAllProps ( 'display' , 'block' ) // true
* testAllProps ( 'display' , 'penguin' ) // false
* ` ` `
*
* A boolean can be passed as a third parameter to skip the value check when
* native detection ( @ supports ) isn ' t available .
*
* ` ` ` js
* testAllProps ( 'shapeOutside' , 'content-box' , true ) ;
* ` ` `
* /
function testAllProps ( prop , value , skipValueTest ) {
return testPropsAll ( prop , undefined , undefined , value , skipValueTest ) ;
}
ModernizrProto . testAllProps = testAllProps ;
2016-12-15 20:29:46 +03:00
2016-12-15 17:55:40 +03:00
/ * !
{
"name" : "CSS Supports" ,
"property" : "supports" ,
"caniuse" : "css-featurequeries" ,
"tags" : [ "css" ] ,
"builderAliases" : [ "css_supports" ] ,
"notes" : [ {
"name" : "W3 Spec" ,
"href" : "http://dev.w3.org/csswg/css3-conditional/#at-supports"
} , {
"name" : "Related Github Issue" ,
"href" : "github.com/Modernizr/Modernizr/issues/648"
} , {
"name" : "W3 Info" ,
"href" : "http://dev.w3.org/csswg/css3-conditional/#the-csssupportsrule-interface"
} ]
}
! * /
var newSyntax = 'CSS' in window && 'supports' in window . CSS ;
var oldSyntax = 'supportsCSS' in window ;
Modernizr . addTest ( 'supports' , newSyntax || oldSyntax ) ;
/ * !
{
"name" : "CSS Transforms 3D" ,
"property" : "csstransforms3d" ,
"caniuse" : "transforms3d" ,
"tags" : [ "css" ] ,
"warnings" : [
"Chrome may occassionally fail this test on some systems; more info: https://code.google.com/p/chromium/issues/detail?id=129004"
]
}
! * /
Modernizr . addTest ( 'csstransforms3d' , function ( ) {
var ret = ! ! testAllProps ( 'perspective' , '1px' , true ) ;
var usePrefix = Modernizr . _config . usePrefixes ;
// Webkit's 3D transforms are passed off to the browser's own graphics renderer.
// It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
// some conditions. As a result, Webkit typically recognizes the syntax but
// will sometimes throw a false positive, thus we must do a more thorough check:
if ( ret && ( ! usePrefix || 'webkitPerspective' in docElement . style ) ) {
var mq ;
var defaultStyle = '#modernizr{width:0;height:0}' ;
// Use CSS Conditional Rules if available
if ( Modernizr . supports ) {
mq = '@supports (perspective: 1px)' ;
} else {
// Otherwise, Webkit allows this media query to succeed only if the feature is enabled.
// `@media (transform-3d),(-webkit-transform-3d){ ... }`
mq = '@media (transform-3d)' ;
if ( usePrefix ) {
mq += ',(-webkit-transform-3d)' ;
}
}
mq += '{#modernizr{width:7px;height:18px;margin:0;padding:0;border:0}}' ;
testStyles ( defaultStyle + mq , function ( elem ) {
ret = elem . offsetWidth === 7 && elem . offsetHeight === 18 ;
} ) ;
}
return ret ;
} ) ;
// Run each test
testRunner ( ) ;
// Remove the "no-js" class if it exists
setClasses ( classes ) ;
delete ModernizrProto . addTest ;
delete ModernizrProto . addAsyncTest ;
// Run the things that are supposed to run after the tests
for ( var i = 0 ; i < Modernizr . _q . length ; i ++ ) {
Modernizr . _q [ i ] ( ) ;
}
// Leak Modernizr namespace
window . Modernizr = Modernizr ;
;
2016-12-15 20:29:46 +03:00
} ) ( window , document ) ;