UNPKG

modernizr

Version:

Modernizr is a JavaScript library that detects HTML5 and CSS3 features in the user’s browser.

209 lines (184 loc) 7.13 kB
define(['ModernizrProto', 'Modernizr', 'hasOwnProp', 'setClasses'], function(ModernizrProto, Modernizr, hasOwnProp, setClasses) { // _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 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`) setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]); // 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; }); return addTest; });