# lib/goog/useragent/useragent.js

    1// Copyright 2006 The Closure Library Authors. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS-IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415/**16 * @fileoverview Rendering engine detection.17 * @see <a href="http://www.useragentstring.com/">User agent strings</a>18 * For information on the browser brand (such as Safari versus Chrome), see19 * goog.userAgent.product.20 * @see ../demos/useragent.html21 */2223goog.provide('goog.userAgent');2425goog.require('goog.string');262728/**29 * @define {boolean} Whether we know at compile-time that the browser is IE.30 */31goog.define('goog.userAgent.ASSUME_IE', false);323334/**35 * @define {boolean} Whether we know at compile-time that the browser is GECKO.36 */37goog.define('goog.userAgent.ASSUME_GECKO', false);383940/**41 * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.42 */43goog.define('goog.userAgent.ASSUME_WEBKIT', false);444546/**47 * @define {boolean} Whether we know at compile-time that the browser is a48 *     mobile device running WebKit e.g. iPhone or Android.49 */50goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);515253/**54 * @define {boolean} Whether we know at compile-time that the browser is OPERA.55 */56goog.define('goog.userAgent.ASSUME_OPERA', false);575859/**60 * @define {boolean} Whether the61 *     {@code goog.userAgent.isVersionOrHigher}62 *     function will return true for any version.63 */64goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);656667/**68 * Whether we know the browser engine at compile-time.69 * @type {boolean}70 * @private71 */72goog.userAgent.BROWSER_KNOWN_ =73    goog.userAgent.ASSUME_IE ||74    goog.userAgent.ASSUME_GECKO ||75    goog.userAgent.ASSUME_MOBILE_WEBKIT ||76    goog.userAgent.ASSUME_WEBKIT ||77    goog.userAgent.ASSUME_OPERA;787980/**81 * Returns the userAgent string for the current browser.82 * Some user agents (I'm thinking of you, Gears WorkerPool) do not expose a83 * navigator object off the global scope.  In that case we return null.84 *85 * @return {?string} The userAgent string or null if there is none.86 */87goog.userAgent.getUserAgentString = function() {88  return goog.global['navigator'] ? goog.global['navigator'].userAgent : null;89};909192/**93 * @return {Object} The native navigator object.94 */95goog.userAgent.getNavigator = function() {96  // Need a local navigator reference instead of using the global one,97  // to avoid the rare case where they reference different objects.98  // (in a WorkerPool, for example).99  return goog.global['navigator'];100};101102103/**104 * Initializer for goog.userAgent.105 *106 * This is a named function so that it can be stripped via the jscompiler107 * option for stripping types.108 * @private109 */110goog.userAgent.init_ = function() {111  /**112   * Whether the user agent string denotes Opera.113   * @type {boolean}114   * @private115   */116  goog.userAgent.detectedOpera_ = false;117118  /**119   * Whether the user agent string denotes Internet Explorer. This includes120   * other browsers using Trident as its rendering engine. For example AOL121   * and Netscape 8122   * @type {boolean}123   * @private124   */125  goog.userAgent.detectedIe_ = false;126127  /**128   * Whether the user agent string denotes WebKit. WebKit is the rendering129   * engine that Safari, Android and others use.130   * @type {boolean}131   * @private132   */133  goog.userAgent.detectedWebkit_ = false;134135  /**136   * Whether the user agent string denotes a mobile device.137   * @type {boolean}138   * @private139   */140  goog.userAgent.detectedMobile_ = false;141142  /**143   * Whether the user agent string denotes Gecko. Gecko is the rendering144   * engine used by Mozilla, Mozilla Firefox, Camino and many more.145   * @type {boolean}146   * @private147   */148  goog.userAgent.detectedGecko_ = false;149150  var ua;151  if (!goog.userAgent.BROWSER_KNOWN_ &&152      (ua = goog.userAgent.getUserAgentString())) {153    var navigator = goog.userAgent.getNavigator();154    goog.userAgent.detectedOpera_ = ua.indexOf('Opera') == 0;155    goog.userAgent.detectedIe_ = !goog.userAgent.detectedOpera_ &&156        ua.indexOf('MSIE') != -1;157    goog.userAgent.detectedWebkit_ = !goog.userAgent.detectedOpera_ &&158        ua.indexOf('WebKit') != -1;159    // WebKit also gives navigator.product string equal to 'Gecko'.160    goog.userAgent.detectedMobile_ = goog.userAgent.detectedWebkit_ &&161        ua.indexOf('Mobile') != -1;162    goog.userAgent.detectedGecko_ = !goog.userAgent.detectedOpera_ &&163        !goog.userAgent.detectedWebkit_ && navigator.product == 'Gecko';164  }165};166167168if (!goog.userAgent.BROWSER_KNOWN_) {169  goog.userAgent.init_();170}171172173/**174 * Whether the user agent is Opera.175 * @type {boolean}176 */177goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?178    goog.userAgent.ASSUME_OPERA : goog.userAgent.detectedOpera_;179180181/**182 * Whether the user agent is Internet Explorer. This includes other browsers183 * using Trident as its rendering engine. For example AOL and Netscape 8184 * @type {boolean}185 */186goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?187    goog.userAgent.ASSUME_IE : goog.userAgent.detectedIe_;188189190/**191 * Whether the user agent is Gecko. Gecko is the rendering engine used by192 * Mozilla, Mozilla Firefox, Camino and many more.193 * @type {boolean}194 */195goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?196    goog.userAgent.ASSUME_GECKO :197    goog.userAgent.detectedGecko_;198199200/**201 * Whether the user agent is WebKit. WebKit is the rendering engine that202 * Safari, Android and others use.203 * @type {boolean}204 */205goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?206    goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :207    goog.userAgent.detectedWebkit_;208209210/**211 * Whether the user agent is running on a mobile device.212 * @type {boolean}213 */214goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||215                        goog.userAgent.detectedMobile_;216217218/**219 * Used while transitioning code to use WEBKIT instead.220 * @type {boolean}221 * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.222 * TODO(nicksantos): Delete this from goog.userAgent.223 */224goog.userAgent.SAFARI = goog.userAgent.WEBKIT;225226227/**228 * @return {string} the platform (operating system) the user agent is running229 *     on. Default to empty string because navigator.platform may not be defined230 *     (on Rhino, for example).231 * @private232 */233goog.userAgent.determinePlatform_ = function() {234  var navigator = goog.userAgent.getNavigator();235  return navigator && navigator.platform || '';236};237238239/**240 * The platform (operating system) the user agent is running on. Default to241 * empty string because navigator.platform may not be defined (on Rhino, for242 * example).243 * @type {string}244 */245goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();246247248/**249 * @define {boolean} Whether the user agent is running on a Macintosh operating250 *     system.251 */252goog.define('goog.userAgent.ASSUME_MAC', false);253254255/**256 * @define {boolean} Whether the user agent is running on a Windows operating257 *     system.258 */259goog.define('goog.userAgent.ASSUME_WINDOWS', false);260261262/**263 * @define {boolean} Whether the user agent is running on a Linux operating264 *     system.265 */266goog.define('goog.userAgent.ASSUME_LINUX', false);267268269/**270 * @define {boolean} Whether the user agent is running on a X11 windowing271 *     system.272 */273goog.define('goog.userAgent.ASSUME_X11', false);274275276/**277 * @define {boolean} Whether the user agent is running on Android.278 */279goog.define('goog.userAgent.ASSUME_ANDROID', false);280281282/**283 * @define {boolean} Whether the user agent is running on an iPhone.284 */285goog.define('goog.userAgent.ASSUME_IPHONE', false);286287288/**289 * @define {boolean} Whether the user agent is running on an iPad.290 */291goog.define('goog.userAgent.ASSUME_IPAD', false);292293294/**295 * @type {boolean}296 * @private297 */298goog.userAgent.PLATFORM_KNOWN_ =299    goog.userAgent.ASSUME_MAC ||300    goog.userAgent.ASSUME_WINDOWS ||301    goog.userAgent.ASSUME_LINUX ||302    goog.userAgent.ASSUME_X11 ||303    goog.userAgent.ASSUME_ANDROID ||304    goog.userAgent.ASSUME_IPHONE ||305    goog.userAgent.ASSUME_IPAD;306307308/**309 * Initialize the goog.userAgent constants that define which platform the user310 * agent is running on.311 * @private312 */313goog.userAgent.initPlatform_ = function() {314  /**315   * Whether the user agent is running on a Macintosh operating system.316   * @type {boolean}317   * @private318   */319  goog.userAgent.detectedMac_ = goog.string.contains(goog.userAgent.PLATFORM,320      'Mac');321322  /**323   * Whether the user agent is running on a Windows operating system.324   * @type {boolean}325   * @private326   */327  goog.userAgent.detectedWindows_ = goog.string.contains(328      goog.userAgent.PLATFORM, 'Win');329330  /**331   * Whether the user agent is running on a Linux operating system.332   * @type {boolean}333   * @private334   */335  goog.userAgent.detectedLinux_ = goog.string.contains(goog.userAgent.PLATFORM,336      'Linux');337338  /**339   * Whether the user agent is running on a X11 windowing system.340   * @type {boolean}341   * @private342   */343  goog.userAgent.detectedX11_ = !!goog.userAgent.getNavigator() &&344      goog.string.contains(goog.userAgent.getNavigator()['appVersion'] || '',345          'X11');346347  // Need user agent string for Android/IOS detection348  var ua = goog.userAgent.getUserAgentString();349350  /**351   * Whether the user agent is running on Android.352   * @type {boolean}353   * @private354   */355  goog.userAgent.detectedAndroid_ = !!ua && ua.indexOf('Android') >= 0;356357  /**358   * Whether the user agent is running on an iPhone.359   * @type {boolean}360   * @private361   */362  goog.userAgent.detectedIPhone_ = !!ua && ua.indexOf('iPhone') >= 0;363364  /**365   * Whether the user agent is running on an iPad.366   * @type {boolean}367   * @private368   */369  goog.userAgent.detectedIPad_ = !!ua && ua.indexOf('iPad') >= 0;370};371372373if (!goog.userAgent.PLATFORM_KNOWN_) {374  goog.userAgent.initPlatform_();375}376377378/**379 * Whether the user agent is running on a Macintosh operating system.380 * @type {boolean}381 */382goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?383    goog.userAgent.ASSUME_MAC : goog.userAgent.detectedMac_;384385386/**387 * Whether the user agent is running on a Windows operating system.388 * @type {boolean}389 */390goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?391    goog.userAgent.ASSUME_WINDOWS : goog.userAgent.detectedWindows_;392393394/**395 * Whether the user agent is running on a Linux operating system.396 * @type {boolean}397 */398goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?399    goog.userAgent.ASSUME_LINUX : goog.userAgent.detectedLinux_;400401402/**403 * Whether the user agent is running on a X11 windowing system.404 * @type {boolean}405 */406goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?407    goog.userAgent.ASSUME_X11 : goog.userAgent.detectedX11_;408409410/**411 * Whether the user agent is running on Android.412 * @type {boolean}413 */414goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?415    goog.userAgent.ASSUME_ANDROID : goog.userAgent.detectedAndroid_;416417418/**419 * Whether the user agent is running on an iPhone.420 * @type {boolean}421 */422goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?423    goog.userAgent.ASSUME_IPHONE : goog.userAgent.detectedIPhone_;424425426/**427 * Whether the user agent is running on an iPad.428 * @type {boolean}429 */430goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?431    goog.userAgent.ASSUME_IPAD : goog.userAgent.detectedIPad_;432433434/**435 * @return {string} The string that describes the version number of the user436 *     agent.437 * @private438 */439goog.userAgent.determineVersion_ = function() {440  // All browsers have different ways to detect the version and they all have441  // different naming schemes.442443  // version is a string rather than a number because it may contain 'b', 'a',444  // and so on.445  var version = '', re;446447  if (goog.userAgent.OPERA && goog.global['opera']) {448    var operaVersion = goog.global['opera'].version;449    version = typeof operaVersion == 'function' ? operaVersion() : operaVersion;450  } else {451    if (goog.userAgent.GECKO) {452      re = /rv\:([^\);]+)(\)|;)/;453    } else if (goog.userAgent.IE) {454      re = /MSIE\s+([^\);]+)(\)|;)/;455    } else if (goog.userAgent.WEBKIT) {456      // WebKit/125.4457      re = /WebKit\/(\S+)/;458    }459    if (re) {460      var arr = re.exec(goog.userAgent.getUserAgentString());461      version = arr ? arr[1] : '';462    }463  }464  if (goog.userAgent.IE) {465    // IE9 can be in document mode 9 but be reporting an inconsistent user agent466    // version.  If it is identifying as a version lower than 9 we take the467    // documentMode as the version instead.  IE8 has similar behavior.468    // It is recommended to set the X-UA-Compatible header to ensure that IE9469    // uses documentMode 9.470    var docMode = goog.userAgent.getDocumentMode_();471    if (docMode > parseFloat(version)) {472      return String(docMode);473    }474  }475  return version;476};477478479/**480 * @return {number|undefined} Returns the document mode (for testing).481 * @private482 */483goog.userAgent.getDocumentMode_ = function() {484  // NOTE(user): goog.userAgent may be used in context where there is no DOM.485  var doc = goog.global['document'];486  return doc ? doc['documentMode'] : undefined;487};488489490/**491 * The version of the user agent. This is a string because it might contain492 * 'b' (as in beta) as well as multiple dots.493 * @type {string}494 */495goog.userAgent.VERSION = goog.userAgent.determineVersion_();496497498/**499 * Compares two version numbers.500 *501 * @param {string} v1 Version of first item.502 * @param {string} v2 Version of second item.503 *504 * @return {number}  1 if first argument is higher505 *                   0 if arguments are equal506 *                  -1 if second argument is higher.507 * @deprecated Use goog.string.compareVersions.508 */509goog.userAgent.compare = function(v1, v2) {510  return goog.string.compareVersions(v1, v2);511};512513514/**515 * Cache for {@link goog.userAgent.isVersionOrHigher}.516 * Calls to compareVersions are surprisingly expensive and, as a browser's517 * version number is unlikely to change during a session, we cache the results.518 * @const519 * @private520 */521goog.userAgent.isVersionOrHigherCache_ = {};522523524/**525 * Whether the user agent version is higher or the same as the given version.526 * NOTE: When checking the version numbers for Firefox and Safari, be sure to527 * use the engine's version, not the browser's version number.  For example,528 * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.529 * Opera and Internet Explorer versions match the product release number.<br>530 * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">531 *     Webkit</a>532 * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>533 *534 * @param {string|number} version The version to check.535 * @return {boolean} Whether the user agent version is higher or the same as536 *     the given version.537 */538goog.userAgent.isVersionOrHigher = function(version) {539  return goog.userAgent.ASSUME_ANY_VERSION ||540      goog.userAgent.isVersionOrHigherCache_[version] ||541      (goog.userAgent.isVersionOrHigherCache_[version] =542          goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);543};544545546/**547 * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.548 * @param {string|number} version The version to check.549 * @return {boolean} Whether the user agent version is higher or the same as550 *     the given version.551 * @deprecated Use goog.userAgent.isVersionOrHigher().552 */553goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;554555556/**557 * Whether the IE effective document mode is higher or the same as the given558 * document mode version.559 * NOTE: Only for IE, return false for another browser.560 *561 * @param {number} documentMode The document mode version to check.562 * @return {boolean} Whether the IE effective document mode is higher or the563 *     same as the given version.564 */565goog.userAgent.isDocumentModeOrHigher = function(documentMode) {566  return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;567};568569570/**571 * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.572 * @param {number} version The version to check.573 * @return {boolean} Whether the IE effective document mode is higher or the574 *      same as the given version.575 */576goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;577578579/**580 * For IE version < 7, documentMode is undefined, so attempt to use the581 * CSS1Compat property to see if we are in standards mode. If we are in582 * standards mode, treat the browser version as the document mode. Otherwise,583 * IE is emulating version 5.584 * @type {number|undefined}585 * @const586 */587goog.userAgent.DOCUMENT_MODE = (function() {588  var doc = goog.global['document'];589  if (!doc || !goog.userAgent.IE) {590    return undefined;591  }592  var mode = goog.userAgent.getDocumentMode_();593  return mode || (doc['compatMode'] == 'CSS1Compat' ?594      parseInt(goog.userAgent.VERSION, 10) : 5);595})();