(문서를 비움) 태그: 비우기 |
편집 요약 없음 |
||
| 1번째 줄: | 1번째 줄: | ||
/*! | |||
* jQuery Client 3.0.0 | |||
* https://gerrit.wikimedia.org/g/jquery-client/ | |||
* | |||
* Copyright 2010-2020 wikimedia/jquery-client maintainers and other contributors. | |||
* Released under the MIT license | |||
* https://jquery-client.mit-license.org | |||
*/ | |||
/** | |||
* User-agent detection | |||
* | |||
* @class jQuery.client | |||
* @singleton | |||
*/ | |||
( function () { | |||
/** | |||
* @private | |||
* @property {Object} profileCache Keyed by userAgent string, | |||
* value is the parsed $.client.profile object for that user agent. | |||
*/ | |||
var profileCache = {}; | |||
$.client = { | |||
/** | |||
* Get an object containing information about the client. | |||
* | |||
* 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, | |||
* } | |||
* | |||
* Example: | |||
* | |||
* if ( $.client.profile().layout == 'gecko' ) { | |||
* // This will only run in Gecko browsers, such as Mozilla Firefox. | |||
* } | |||
* | |||
* var profile = $.client.profile(); | |||
* if ( profile.layout == 'gecko' && profile.platform == 'linux' ) { | |||
* // This will only run in Gecko browsers on Linux. | |||
* } | |||
* | |||
* Recognised browser names: | |||
* | |||
* - `android` (legacy Android browser, prior to Chrome Mobile) | |||
* - `chrome` (includes Chrome Mobile, Microsoft Edge, Opera, and others) | |||
* - `crios` (Chrome on iOS, which uses Mobile Safari) | |||
* - `edge` (legacy Microsoft Edge, which uses EdgeHTML) | |||
* - `firefox` (includes Firefox Mobile, Iceweasel, and others) | |||
* - `fxios` (Firefox on iOS, which uses Mobile Safari) | |||
* - `konqueror` | |||
* - `msie` | |||
* - `opera` (legacy Opera, which uses Presto) | |||
* - `rekonq` | |||
* - `safari` (including Mobile Safari) | |||
* - `silk` | |||
* | |||
* Recognised layout engines: | |||
* | |||
* - `edge` (EdgeHTML 12-18, as used by legacy Microsoft Edge) | |||
* - `gecko` | |||
* - `khtml` | |||
* - `presto` | |||
* - `trident` | |||
* - `webkit` | |||
* | |||
* Note that Chrome and Chromium-based browsers like Opera have their layout | |||
* engine identified as `webkit`. | |||
* | |||
* Recognised platforms: | |||
* | |||
* - `ipad` | |||
* - `iphone` | |||
* - `linux` | |||
* - `mac` | |||
* - `solaris` (untested) | |||
* - `win` | |||
* | |||
* @param {Object} [nav] An object with a 'userAgent' and 'platform' property. | |||
* Defaults to the global `navigator` object. | |||
* @return {Object} The client object | |||
*/ | |||
profile: function ( nav ) { | |||
if ( !nav ) { | |||
nav = window.navigator; | |||
} | |||
// Use the cached version if possible | |||
if ( profileCache[ nav.userAgent + '|' + nav.platform ] ) { | |||
return profileCache[ nav.userAgent + '|' + nav.platform ]; | |||
} | |||
// eslint-disable-next-line vars-on-top | |||
var | |||
versionNumber, | |||
key = nav.userAgent + '|' + nav.platform, | |||
// Configuration | |||
// Name of browsers or layout engines we don't recognize | |||
uk = 'unknown', | |||
// Generic version digit | |||
x = 'x', | |||
// Fixups for user agent strings that contain wild words | |||
wildFixups = [ | |||
// Chrome lives in the shadow of Safari still | |||
[ 'Chrome Safari', 'Chrome' ], | |||
// KHTML is the layout engine not the browser - LIES! | |||
[ 'KHTML/', 'Konqueror/' ], | |||
// For Firefox Mobile, strip out "Android;" or "Android [version]" so that we | |||
// classify it as Firefox instead of Android (default browser) | |||
[ /Android(?:;|\s[a-zA-Z0-9.+-]+)(.*Firefox)/, '$1' ] | |||
], | |||
// Strings which precede a version number in a user agent string | |||
versionPrefixes = '(?:chrome|crios|firefox|fxios|opera|version|konqueror|msie|safari|android)', | |||
// This matches the actual version number, with non-capturing groups for the | |||
// separator and suffix | |||
versionSuffix = '(?:\\/|;?\\s|)([a-z0-9\\.\\+]*?)(?:;|dev|rel|\\)|\\s|$)', | |||
// Match the names of known browser families | |||
rName = /(chrome|crios|firefox|fxios|konqueror|msie|opera|safari|rekonq|android)/, | |||
// Match the name of known layout engines | |||
rLayout = /(gecko|konqueror|msie|trident|edge|opera|webkit)/, | |||
// Translations for conforming layout names | |||
layoutMap = { konqueror: 'khtml', msie: 'trident', opera: 'presto' }, | |||
// Match the prefix and version of supported layout engines | |||
rLayoutVersion = /(applewebkit|gecko|trident|edge)\/(\d+)/, | |||
// Match the name of known operating systems | |||
rPlatform = /(win|wow64|mac|linux|sunos|solaris|iphone|ipad)/, | |||
// Translations for conforming operating system names | |||
platformMap = { sunos: 'solaris', wow64: 'win' }, | |||
// Pre-processing | |||
ua = nav.userAgent, | |||
match, | |||
name = uk, | |||
layout = uk, | |||
layoutversion = uk, | |||
platform = uk, | |||
version = x; | |||
// Takes a userAgent string and fixes it into something we can more | |||
// easily work with | |||
wildFixups.forEach( function ( fixup ) { | |||
ua = ua.replace( fixup[ 0 ], fixup[ 1 ] ); | |||
} ); | |||
// Everything will be in lowercase from now on | |||
ua = ua.toLowerCase(); | |||
// Extraction | |||
if ( ( match = rName.exec( ua ) ) ) { | |||
name = match[ 1 ]; | |||
} | |||
if ( ( match = rLayout.exec( ua ) ) ) { | |||
layout = layoutMap[ match[ 1 ] ] || match[ 1 ]; | |||
} | |||
if ( ( match = rLayoutVersion.exec( ua ) ) ) { | |||
layoutversion = parseInt( match[ 2 ], 10 ); | |||
} | |||
if ( ( match = rPlatform.exec( nav.platform.toLowerCase() ) ) ) { | |||
platform = platformMap[ match[ 1 ] ] || match[ 1 ]; | |||
} | |||
if ( ( match = new RegExp( versionPrefixes + versionSuffix ).exec( ua ) ) ) { | |||
version = match[ 1 ]; | |||
} | |||
// Edge Cases -- did I mention about how user agent string lie? | |||
// Decode Safari's crazy 400+ version numbers | |||
if ( name === '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( /\bversion\/([0-9.]*)/ ); | |||
if ( match && match[ 1 ] ) { | |||
version = match[ 1 ]; | |||
} else { | |||
version = '10'; | |||
} | |||
} | |||
// And IE 11's lies about being not being IE | |||
if ( layout === 'trident' && layoutversion >= 7 && ( match = ua.match( /\brv[ :/]([0-9.]*)/ ) ) ) { | |||
if ( match[ 1 ] ) { | |||
name = 'msie'; | |||
version = match[ 1 ]; | |||
} | |||
} | |||
// And MS Edge's lies about being Chrome | |||
// | |||
// It's different enough from classic IE Trident engine that they do this | |||
// to avoid getting caught by MSIE-specific browser sniffing. | |||
if ( name === 'chrome' && ( match = ua.match( /\bedge\/([0-9.]*)/ ) ) ) { | |||
name = 'edge'; | |||
version = match[ 1 ]; | |||
layout = 'edge'; | |||
layoutversion = parseInt( match[ 1 ], 10 ); | |||
} | |||
// And Amazon Silk's lies about being Android on mobile or Safari on desktop | |||
if ( ( match = ua.match( /\bsilk\/([0-9.\-_]*)/ ) ) ) { | |||
if ( match[ 1 ] ) { | |||
name = 'silk'; | |||
version = match[ 1 ]; | |||
} | |||
} | |||
versionNumber = parseFloat( version, 10 ) || 0.0; | |||
// Caching | |||
profileCache[ key ] = { | |||
name: name, | |||
layout: layout, | |||
layoutVersion: layoutversion, | |||
platform: platform, | |||
version: version, | |||
versionBase: ( version !== x ? Math.floor( versionNumber ).toString() : x ), | |||
versionNumber: versionNumber | |||
}; | |||
return profileCache[ key ]; | |||
}, | |||
/** | |||
* Checks the current browser against a support map object. | |||
* | |||
* Version numbers passed as numeric values will be compared like numbers (1.2 > 1.11). | |||
* Version numbers passed as string values will be compared using a simple component-wise | |||
* algorithm, similar to PHP's version_compare ('1.2' < '1.11'). | |||
* | |||
* A browser map is in the following format: | |||
* | |||
* { | |||
* // Multiple rules with configurable operators | |||
* 'msie': [['>=', 7], ['!=', 9]], | |||
* // Match no versions | |||
* 'iphone': false, | |||
* // Match any version | |||
* 'android': null | |||
* } | |||
* | |||
* It can optionally be split into ltr/rtl sections: | |||
* | |||
* { | |||
* 'ltr': { | |||
* 'android': null, | |||
* 'iphone': false | |||
* }, | |||
* 'rtl': { | |||
* 'android': false, | |||
* // rules are not inherited from ltr | |||
* 'iphone': false | |||
* } | |||
* } | |||
* | |||
* @param {Object} map Browser support map | |||
* @param {Object} [profile] A client-profile object | |||
* @param {boolean} [exactMatchOnly=false] Only return true if the browser is matched, | |||
* otherwise returns true if the browser is not found. | |||
* | |||
* @return {boolean} The current browser is in the support map | |||
*/ | |||
test: function ( map, profile, exactMatchOnly ) { | |||
var conditions, dir, i, op, val, j, pieceVersion, pieceVal, compare; | |||
profile = $.isPlainObject( profile ) ? profile : $.client.profile(); | |||
if ( map.ltr && map.rtl ) { | |||
dir = $( document.body ).is( '.rtl' ) ? 'rtl' : 'ltr'; | |||
map = map[ dir ]; | |||
} | |||
// Check over each browser condition to determine if we are running in a | |||
// compatible client | |||
if ( typeof map !== 'object' || map[ profile.name ] === undefined ) { | |||
// Not found, return true if exactMatchOnly not set, false otherwise | |||
return !exactMatchOnly; | |||
} | |||
conditions = map[ profile.name ]; | |||
if ( conditions === false ) { | |||
// Match no versions | |||
return false; | |||
} | |||
if ( conditions === null ) { | |||
// Match all versions | |||
return true; | |||
} | |||
for ( i = 0; i < conditions.length; i++ ) { | |||
op = conditions[ i ][ 0 ]; | |||
val = conditions[ i ][ 1 ]; | |||
if ( typeof val === 'string' ) { | |||
// Perform a component-wise comparison of versions, similar to | |||
// PHP's version_compare but simpler. '1.11' is larger than '1.2'. | |||
pieceVersion = profile.version.toString().split( '.' ); | |||
pieceVal = val.split( '.' ); | |||
// Extend with zeroes to equal length | |||
while ( pieceVersion.length < pieceVal.length ) { | |||
pieceVersion.push( '0' ); | |||
} | |||
while ( pieceVal.length < pieceVersion.length ) { | |||
pieceVal.push( '0' ); | |||
} | |||
// Compare components | |||
compare = 0; | |||
for ( j = 0; j < pieceVersion.length; j++ ) { | |||
if ( Number( pieceVersion[ j ] ) < Number( pieceVal[ j ] ) ) { | |||
compare = -1; | |||
break; | |||
} else if ( Number( pieceVersion[ j ] ) > Number( pieceVal[ j ] ) ) { | |||
compare = 1; | |||
break; | |||
} | |||
} | |||
// compare will be -1, 0 or 1, depending on comparison result | |||
// eslint-disable-next-line no-eval | |||
if ( !( eval( String( compare + op + '0' ) ) ) ) { | |||
return false; | |||
} | |||
} else if ( typeof val === 'number' ) { | |||
// eslint-disable-next-line no-eval | |||
if ( !( eval( 'profile.versionNumber' + op + val ) ) ) { | |||
return false; | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
}; | |||
}() ); | |||
2025년 1월 13일 (월) 16:42 판
/*!
* jQuery Client 3.0.0
* https://gerrit.wikimedia.org/g/jquery-client/
*
* Copyright 2010-2020 wikimedia/jquery-client maintainers and other contributors.
* Released under the MIT license
* https://jquery-client.mit-license.org
*/
/**
* User-agent detection
*
* @class jQuery.client
* @singleton
*/
( function () {
/**
* @private
* @property {Object} profileCache Keyed by userAgent string,
* value is the parsed $.client.profile object for that user agent.
*/
var profileCache = {};
$.client = {
/**
* Get an object containing information about the client.
*
* 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,
* }
*
* Example:
*
* if ( $.client.profile().layout == 'gecko' ) {
* // This will only run in Gecko browsers, such as Mozilla Firefox.
* }
*
* var profile = $.client.profile();
* if ( profile.layout == 'gecko' && profile.platform == 'linux' ) {
* // This will only run in Gecko browsers on Linux.
* }
*
* Recognised browser names:
*
* - `android` (legacy Android browser, prior to Chrome Mobile)
* - `chrome` (includes Chrome Mobile, Microsoft Edge, Opera, and others)
* - `crios` (Chrome on iOS, which uses Mobile Safari)
* - `edge` (legacy Microsoft Edge, which uses EdgeHTML)
* - `firefox` (includes Firefox Mobile, Iceweasel, and others)
* - `fxios` (Firefox on iOS, which uses Mobile Safari)
* - `konqueror`
* - `msie`
* - `opera` (legacy Opera, which uses Presto)
* - `rekonq`
* - `safari` (including Mobile Safari)
* - `silk`
*
* Recognised layout engines:
*
* - `edge` (EdgeHTML 12-18, as used by legacy Microsoft Edge)
* - `gecko`
* - `khtml`
* - `presto`
* - `trident`
* - `webkit`
*
* Note that Chrome and Chromium-based browsers like Opera have their layout
* engine identified as `webkit`.
*
* Recognised platforms:
*
* - `ipad`
* - `iphone`
* - `linux`
* - `mac`
* - `solaris` (untested)
* - `win`
*
* @param {Object} [nav] An object with a 'userAgent' and 'platform' property.
* Defaults to the global `navigator` object.
* @return {Object} The client object
*/
profile: function ( nav ) {
if ( !nav ) {
nav = window.navigator;
}
// Use the cached version if possible
if ( profileCache[ nav.userAgent + '|' + nav.platform ] ) {
return profileCache[ nav.userAgent + '|' + nav.platform ];
}
// eslint-disable-next-line vars-on-top
var
versionNumber,
key = nav.userAgent + '|' + nav.platform,
// Configuration
// Name of browsers or layout engines we don't recognize
uk = 'unknown',
// Generic version digit
x = 'x',
// Fixups for user agent strings that contain wild words
wildFixups = [
// Chrome lives in the shadow of Safari still
[ 'Chrome Safari', 'Chrome' ],
// KHTML is the layout engine not the browser - LIES!
[ 'KHTML/', 'Konqueror/' ],
// For Firefox Mobile, strip out "Android;" or "Android [version]" so that we
// classify it as Firefox instead of Android (default browser)
[ /Android(?:;|\s[a-zA-Z0-9.+-]+)(.*Firefox)/, '$1' ]
],
// Strings which precede a version number in a user agent string
versionPrefixes = '(?:chrome|crios|firefox|fxios|opera|version|konqueror|msie|safari|android)',
// This matches the actual version number, with non-capturing groups for the
// separator and suffix
versionSuffix = '(?:\\/|;?\\s|)([a-z0-9\\.\\+]*?)(?:;|dev|rel|\\)|\\s|$)',
// Match the names of known browser families
rName = /(chrome|crios|firefox|fxios|konqueror|msie|opera|safari|rekonq|android)/,
// Match the name of known layout engines
rLayout = /(gecko|konqueror|msie|trident|edge|opera|webkit)/,
// Translations for conforming layout names
layoutMap = { konqueror: 'khtml', msie: 'trident', opera: 'presto' },
// Match the prefix and version of supported layout engines
rLayoutVersion = /(applewebkit|gecko|trident|edge)\/(\d+)/,
// Match the name of known operating systems
rPlatform = /(win|wow64|mac|linux|sunos|solaris|iphone|ipad)/,
// Translations for conforming operating system names
platformMap = { sunos: 'solaris', wow64: 'win' },
// Pre-processing
ua = nav.userAgent,
match,
name = uk,
layout = uk,
layoutversion = uk,
platform = uk,
version = x;
// Takes a userAgent string and fixes it into something we can more
// easily work with
wildFixups.forEach( function ( fixup ) {
ua = ua.replace( fixup[ 0 ], fixup[ 1 ] );
} );
// Everything will be in lowercase from now on
ua = ua.toLowerCase();
// Extraction
if ( ( match = rName.exec( ua ) ) ) {
name = match[ 1 ];
}
if ( ( match = rLayout.exec( ua ) ) ) {
layout = layoutMap[ match[ 1 ] ] || match[ 1 ];
}
if ( ( match = rLayoutVersion.exec( ua ) ) ) {
layoutversion = parseInt( match[ 2 ], 10 );
}
if ( ( match = rPlatform.exec( nav.platform.toLowerCase() ) ) ) {
platform = platformMap[ match[ 1 ] ] || match[ 1 ];
}
if ( ( match = new RegExp( versionPrefixes + versionSuffix ).exec( ua ) ) ) {
version = match[ 1 ];
}
// Edge Cases -- did I mention about how user agent string lie?
// Decode Safari's crazy 400+ version numbers
if ( name === '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( /\bversion\/([0-9.]*)/ );
if ( match && match[ 1 ] ) {
version = match[ 1 ];
} else {
version = '10';
}
}
// And IE 11's lies about being not being IE
if ( layout === 'trident' && layoutversion >= 7 && ( match = ua.match( /\brv[ :/]([0-9.]*)/ ) ) ) {
if ( match[ 1 ] ) {
name = 'msie';
version = match[ 1 ];
}
}
// And MS Edge's lies about being Chrome
//
// It's different enough from classic IE Trident engine that they do this
// to avoid getting caught by MSIE-specific browser sniffing.
if ( name === 'chrome' && ( match = ua.match( /\bedge\/([0-9.]*)/ ) ) ) {
name = 'edge';
version = match[ 1 ];
layout = 'edge';
layoutversion = parseInt( match[ 1 ], 10 );
}
// And Amazon Silk's lies about being Android on mobile or Safari on desktop
if ( ( match = ua.match( /\bsilk\/([0-9.\-_]*)/ ) ) ) {
if ( match[ 1 ] ) {
name = 'silk';
version = match[ 1 ];
}
}
versionNumber = parseFloat( version, 10 ) || 0.0;
// Caching
profileCache[ key ] = {
name: name,
layout: layout,
layoutVersion: layoutversion,
platform: platform,
version: version,
versionBase: ( version !== x ? Math.floor( versionNumber ).toString() : x ),
versionNumber: versionNumber
};
return profileCache[ key ];
},
/**
* Checks the current browser against a support map object.
*
* Version numbers passed as numeric values will be compared like numbers (1.2 > 1.11).
* Version numbers passed as string values will be compared using a simple component-wise
* algorithm, similar to PHP's version_compare ('1.2' < '1.11').
*
* A browser map is in the following format:
*
* {
* // Multiple rules with configurable operators
* 'msie': [['>=', 7], ['!=', 9]],
* // Match no versions
* 'iphone': false,
* // Match any version
* 'android': null
* }
*
* It can optionally be split into ltr/rtl sections:
*
* {
* 'ltr': {
* 'android': null,
* 'iphone': false
* },
* 'rtl': {
* 'android': false,
* // rules are not inherited from ltr
* 'iphone': false
* }
* }
*
* @param {Object} map Browser support map
* @param {Object} [profile] A client-profile object
* @param {boolean} [exactMatchOnly=false] Only return true if the browser is matched,
* otherwise returns true if the browser is not found.
*
* @return {boolean} The current browser is in the support map
*/
test: function ( map, profile, exactMatchOnly ) {
var conditions, dir, i, op, val, j, pieceVersion, pieceVal, compare;
profile = $.isPlainObject( profile ) ? profile : $.client.profile();
if ( map.ltr && map.rtl ) {
dir = $( document.body ).is( '.rtl' ) ? 'rtl' : 'ltr';
map = map[ dir ];
}
// Check over each browser condition to determine if we are running in a
// compatible client
if ( typeof map !== 'object' || map[ profile.name ] === undefined ) {
// Not found, return true if exactMatchOnly not set, false otherwise
return !exactMatchOnly;
}
conditions = map[ profile.name ];
if ( conditions === false ) {
// Match no versions
return false;
}
if ( conditions === null ) {
// Match all versions
return true;
}
for ( i = 0; i < conditions.length; i++ ) {
op = conditions[ i ][ 0 ];
val = conditions[ i ][ 1 ];
if ( typeof val === 'string' ) {
// Perform a component-wise comparison of versions, similar to
// PHP's version_compare but simpler. '1.11' is larger than '1.2'.
pieceVersion = profile.version.toString().split( '.' );
pieceVal = val.split( '.' );
// Extend with zeroes to equal length
while ( pieceVersion.length < pieceVal.length ) {
pieceVersion.push( '0' );
}
while ( pieceVal.length < pieceVersion.length ) {
pieceVal.push( '0' );
}
// Compare components
compare = 0;
for ( j = 0; j < pieceVersion.length; j++ ) {
if ( Number( pieceVersion[ j ] ) < Number( pieceVal[ j ] ) ) {
compare = -1;
break;
} else if ( Number( pieceVersion[ j ] ) > Number( pieceVal[ j ] ) ) {
compare = 1;
break;
}
}
// compare will be -1, 0 or 1, depending on comparison result
// eslint-disable-next-line no-eval
if ( !( eval( String( compare + op + '0' ) ) ) ) {
return false;
}
} else if ( typeof val === 'number' ) {
// eslint-disable-next-line no-eval
if ( !( eval( 'profile.versionNumber' + op + val ) ) ) {
return false;
}
}
}
return true;
}
};
}() );