|
|
| (같은 사용자의 중간 판 35개는 보이지 않습니다) |
| 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;
| |
| }
| |
| };
| |
| }() );
| |