mediawiki-extensions-Visual.../modules/jquery/jquery.client.js
Roan Kattouw 6d2a6491c8 Add a copy of jquery.client so the standalone still works
The tests passed just fine in Gerrit, because it runs the tests via
MediaWiki using ResourceLoader, which was able to load jquery.client
just fine from MW core.

Change-Id: I004514ab761107b687be2fe1ff49ecfd25bead5b
2013-04-26 14:40:03 -07:00

234 lines
7.6 KiB
JavaScript

/**
* User-agent detection
*/
( function ( $ ) {
/* Private Members */
/**
* @var profileCache {Object} Keyed by userAgent string,
* value is the parsed $.client.profile object for that user agent.
*/
var profileCache = {};
/* Public Methods */
$.client = {
/**
* Get an object containing information about the client.
*
* @param nav {Object} An object with atleast a 'userAgent' and 'platform' key.
* Defaults to the global Navigator object.
* @return {Object} The resulting client object will be in the following format:
* {
* 'name': 'firefox',
* 'layout': 'gecko',
* 'layoutVersion': 20101026,
* 'platform': 'linux'
* 'version': '3.5.1',
* 'versionBase': '3',
* 'versionNumber': 3.5,
* }
*/
profile: function ( nav ) {
/*jshint boss: true */
if ( nav === undefined ) {
nav = window.navigator;
}
// Use the cached version if possible
if ( profileCache[nav.userAgent] === undefined ) {
var
versionNumber,
/* Configuration */
// Name of browsers or layout engines we don't recognize
uk = 'unknown',
// Generic version digit
x = 'x',
// Strings found in user agent strings that need to be conformed
wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'],
// Translations for conforming user agent strings
userAgentTranslations = [
// Tons of browsers lie about being something they are not
[/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''],
// Chrome lives in the shadow of Safari still
['Chrome Safari', 'Chrome'],
// KHTML is the layout engine not the browser - LIES!
['KHTML', 'Konqueror'],
// Firefox nightly builds
['Minefield', 'Firefox'],
// This helps keep differnt versions consistent
['Navigator', 'Netscape'],
// This prevents version extraction issues, otherwise translation would happen later
['PLAYSTATION 3', 'PS3']
],
// Strings which precede a version number in a user agent string - combined and used as match 1 in
// version detectection
versionPrefixes = [
'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'netscape6', 'opera', 'version', 'konqueror',
'lynx', 'msie', 'safari', 'ps3'
],
// Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number
versionSuffix = '(\\/|\\;?\\s|)([a-z0-9\\.\\+]*?)(\\;|dev|rel|\\)|\\s|$)',
// Names of known browsers
names = [
'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'konqueror', 'lynx', 'msie', 'opera',
'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq'
],
// Tanslations for conforming browser names
nameTranslations = [],
// Names of known layout engines
layouts = ['gecko', 'konqueror', 'msie', 'opera', 'webkit'],
// Translations for conforming layout names
layoutTranslations = [ ['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto'] ],
// Names of supported layout engines for version number
layoutVersions = ['applewebkit', 'gecko'],
// Names of known operating systems
platforms = ['win', 'mac', 'linux', 'sunos', 'solaris', 'iphone'],
// Translations for conforming operating system names
platformTranslations = [ ['sunos', 'solaris'] ],
/* Methods */
/**
* Performs multiple replacements on a string
*/
translate = function ( source, translations ) {
var i;
for ( i = 0; i < translations.length; i++ ) {
source = source.replace( translations[i][0], translations[i][1] );
}
return source;
},
/* Pre-processing */
ua = nav.userAgent,
match,
name = uk,
layout = uk,
layoutversion = uk,
platform = uk,
version = x;
if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( ua ) ) {
// Takes a userAgent string and translates given text into something we can more easily work with
ua = translate( ua, userAgentTranslations );
}
// Everything will be in lowercase from now on
ua = ua.toLowerCase();
/* Extraction */
if ( match = new RegExp( '(' + names.join( '|' ) + ')' ).exec( ua ) ) {
name = translate( match[1], nameTranslations );
}
if ( match = new RegExp( '(' + layouts.join( '|' ) + ')' ).exec( ua ) ) {
layout = translate( match[1], layoutTranslations );
}
if ( match = new RegExp( '(' + layoutVersions.join( '|' ) + ')\\\/(\\d+)').exec( ua ) ) {
layoutversion = parseInt( match[2], 10 );
}
if ( match = new RegExp( '(' + platforms.join( '|' ) + ')' ).exec( nav.platform.toLowerCase() ) ) {
platform = translate( match[1], platformTranslations );
}
if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( ua ) ) {
version = match[3];
}
/* Edge Cases -- did I mention about how user agent string lie? */
// Decode Safari's crazy 400+ version numbers
if ( name.match( /safari/ ) && version > 400 ) {
version = '2.0';
}
// Expose Opera 10's lies about being Opera 9.8
if ( name === 'opera' && version >= 9.8) {
match = ua.match( /version\/([0-9\.]*)/i );
if ( match && match[1] ) {
version = match[1];
} else {
version = '10';
}
}
versionNumber = parseFloat( version, 10 ) || 0.0;
/* Caching */
profileCache[nav.userAgent] = {
name: name,
layout: layout,
layoutVersion: layoutversion,
platform: platform,
version: version,
versionBase: ( version !== x ? Math.floor( versionNumber ).toString() : x ),
versionNumber: versionNumber
};
}
return profileCache[nav.userAgent];
},
/**
* Checks the current browser against a support map object to determine if the browser has been black-listed or
* not. If the browser was not configured specifically it is assumed to work. It is assumed that the body
* element is classified as either "ltr" or "rtl". If neither is set, "ltr" is assumed.
*
* A browser map is in the following format:
* {
* 'ltr': {
* // Multiple rules with configurable operators
* 'msie': [['>=', 7], ['!=', 9]],
* // Blocked entirely
* 'iphone': false
* },
* 'rtl': {
* // Test against a string
* 'msie': [['!==', '8.1.2.3']],
* // RTL rules do not fall through to LTR rules, you must explicity set each of them
* 'iphone': false
* }
* }
*
* @param map {Object} Browser support map
* @param profile {Object} (optional) a client-profile object.
*
* @return Boolean true if browser known or assumed to be supported, false if blacklisted
*/
test: function ( map, profile ) {
/*jshint evil: true */
var conditions, dir, i, op, val;
profile = $.isPlainObject( profile ) ? profile : $.client.profile();
dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr';
// Check over each browser condition to determine if we are running in a compatible client
if ( typeof map[dir] !== 'object' || map[dir][profile.name] === undefined ) {
// Unknown, so we assume it's working
return true;
}
conditions = map[dir][profile.name];
if ( conditions === false ) {
return false;
}
for ( i = 0; i < conditions.length; i++ ) {
op = conditions[i][0];
val = conditions[i][1];
if ( typeof val === 'string' ) {
if ( !( eval( 'profile.version' + op + '"' + val + '"' ) ) ) {
return false;
}
} else if ( typeof val === 'number' ) {
if ( !( eval( 'profile.versionNumber' + op + val ) ) ) {
return false;
}
}
}
return true;
}
};
}( jQuery ) );