# 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.labs.userAgent.browser');26goog.require('goog.labs.userAgent.engine');27goog.require('goog.labs.userAgent.util');28goog.require('goog.string');293031/**32 * @define {boolean} Whether we know at compile-time that the browser is IE.33 */34goog.define('goog.userAgent.ASSUME_IE', false);353637/**38 * @define {boolean} Whether we know at compile-time that the browser is GECKO.39 */40goog.define('goog.userAgent.ASSUME_GECKO', false);414243/**44 * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.45 */46goog.define('goog.userAgent.ASSUME_WEBKIT', false);474849/**50 * @define {boolean} Whether we know at compile-time that the browser is a51 *     mobile device running WebKit e.g. iPhone or Android.52 */53goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);545556/**57 * @define {boolean} Whether we know at compile-time that the browser is OPERA.58 */59goog.define('goog.userAgent.ASSUME_OPERA', false);606162/**63 * @define {boolean} Whether the64 *     {@code goog.userAgent.isVersionOrHigher}65 *     function will return true for any version.66 */67goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);686970/**71 * Whether we know the browser engine at compile-time.72 * @type {boolean}73 * @private74 */75goog.userAgent.BROWSER_KNOWN_ =76    goog.userAgent.ASSUME_IE ||77    goog.userAgent.ASSUME_GECKO ||78    goog.userAgent.ASSUME_MOBILE_WEBKIT ||79    goog.userAgent.ASSUME_WEBKIT ||80    goog.userAgent.ASSUME_OPERA;818283/**84 * Returns the userAgent string for the current browser.85 *86 * @return {string} The userAgent string.87 */88goog.userAgent.getUserAgentString = function() {89  return goog.labs.userAgent.util.getUserAgent();90};919293/**94 * TODO(nnaze): Change type to "Navigator" and update compilation targets.95 * @return {Object} The native navigator object.96 */97goog.userAgent.getNavigator = function() {98  // Need a local navigator reference instead of using the global one,99  // to avoid the rare case where they reference different objects.100  // (in a WorkerPool, for example).101  return goog.global['navigator'] || null;102};103104105/**106 * Whether the user agent is Opera.107 * @type {boolean}108 */109goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?110    goog.userAgent.ASSUME_OPERA :111    goog.labs.userAgent.browser.isOpera();112113114/**115 * Whether the user agent is Internet Explorer.116 * @type {boolean}117 */118goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?119    goog.userAgent.ASSUME_IE :120    goog.labs.userAgent.browser.isIE();121122123/**124 * Whether the user agent is Gecko. Gecko is the rendering engine used by125 * Mozilla, Firefox, and others.126 * @type {boolean}127 */128goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?129    goog.userAgent.ASSUME_GECKO :130    goog.labs.userAgent.engine.isGecko();131132133/**134 * Whether the user agent is WebKit. WebKit is the rendering engine that135 * Safari, Android and others use.136 * @type {boolean}137 */138goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?139    goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :140    goog.labs.userAgent.engine.isWebKit();141142143/**144 * Whether the user agent is running on a mobile device.145 *146 * This is a separate function so that the logic can be tested.147 *148 * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().149 *150 * @return {boolean} Whether the user agent is running on a mobile device.151 * @private152 */153goog.userAgent.isMobile_ = function() {154  return goog.userAgent.WEBKIT &&155         goog.labs.userAgent.util.matchUserAgent('Mobile');156};157158159/**160 * Whether the user agent is running on a mobile device.161 *162 * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent163 *   is promoted as the gecko/webkit logic is likely inaccurate.164 *165 * @type {boolean}166 */167goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||168                        goog.userAgent.isMobile_();169170171/**172 * Used while transitioning code to use WEBKIT instead.173 * @type {boolean}174 * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.175 * TODO(nicksantos): Delete this from goog.userAgent.176 */177goog.userAgent.SAFARI = goog.userAgent.WEBKIT;178179180/**181 * @return {string} the platform (operating system) the user agent is running182 *     on. Default to empty string because navigator.platform may not be defined183 *     (on Rhino, for example).184 * @private185 */186goog.userAgent.determinePlatform_ = function() {187  var navigator = goog.userAgent.getNavigator();188  return navigator && navigator.platform || '';189};190191192/**193 * The platform (operating system) the user agent is running on. Default to194 * empty string because navigator.platform may not be defined (on Rhino, for195 * example).196 * @type {string}197 */198goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();199200201/**202 * @define {boolean} Whether the user agent is running on a Macintosh operating203 *     system.204 */205goog.define('goog.userAgent.ASSUME_MAC', false);206207208/**209 * @define {boolean} Whether the user agent is running on a Windows operating210 *     system.211 */212goog.define('goog.userAgent.ASSUME_WINDOWS', false);213214215/**216 * @define {boolean} Whether the user agent is running on a Linux operating217 *     system.218 */219goog.define('goog.userAgent.ASSUME_LINUX', false);220221222/**223 * @define {boolean} Whether the user agent is running on a X11 windowing224 *     system.225 */226goog.define('goog.userAgent.ASSUME_X11', false);227228229/**230 * @define {boolean} Whether the user agent is running on Android.231 */232goog.define('goog.userAgent.ASSUME_ANDROID', false);233234235/**236 * @define {boolean} Whether the user agent is running on an iPhone.237 */238goog.define('goog.userAgent.ASSUME_IPHONE', false);239240241/**242 * @define {boolean} Whether the user agent is running on an iPad.243 */244goog.define('goog.userAgent.ASSUME_IPAD', false);245246247/**248 * @type {boolean}249 * @private250 */251goog.userAgent.PLATFORM_KNOWN_ =252    goog.userAgent.ASSUME_MAC ||253    goog.userAgent.ASSUME_WINDOWS ||254    goog.userAgent.ASSUME_LINUX ||255    goog.userAgent.ASSUME_X11 ||256    goog.userAgent.ASSUME_ANDROID ||257    goog.userAgent.ASSUME_IPHONE ||258    goog.userAgent.ASSUME_IPAD;259260261/**262 * Initialize the goog.userAgent constants that define which platform the user263 * agent is running on.264 * @private265 */266goog.userAgent.initPlatform_ = function() {267  /**268   * Whether the user agent is running on a Macintosh operating system.269   * @type {boolean}270   * @private271   */272  goog.userAgent.detectedMac_ = goog.string.contains(goog.userAgent.PLATFORM,273      'Mac');274275  /**276   * Whether the user agent is running on a Windows operating system.277   * @type {boolean}278   * @private279   */280  goog.userAgent.detectedWindows_ = goog.string.contains(281      goog.userAgent.PLATFORM, 'Win');282283  /**284   * Whether the user agent is running on a Linux operating system.285   * @type {boolean}286   * @private287   */288  goog.userAgent.detectedLinux_ = goog.string.contains(goog.userAgent.PLATFORM,289      'Linux');290291  /**292   * Whether the user agent is running on a X11 windowing system.293   * @type {boolean}294   * @private295   */296  goog.userAgent.detectedX11_ = !!goog.userAgent.getNavigator() &&297      goog.string.contains(goog.userAgent.getNavigator()['appVersion'] || '',298          'X11');299300  // Need user agent string for Android/IOS detection301  var ua = goog.userAgent.getUserAgentString();302303  /**304   * Whether the user agent is running on Android.305   * @type {boolean}306   * @private307   */308  goog.userAgent.detectedAndroid_ = !!ua &&309      goog.string.contains(ua, 'Android');310311  /**312   * Whether the user agent is running on an iPhone.313   * @type {boolean}314   * @private315   */316  goog.userAgent.detectedIPhone_ = !!ua && goog.string.contains(ua, 'iPhone');317318  /**319   * Whether the user agent is running on an iPad.320   * @type {boolean}321   * @private322   */323  goog.userAgent.detectedIPad_ = !!ua && goog.string.contains(ua, 'iPad');324};325326327if (!goog.userAgent.PLATFORM_KNOWN_) {328  goog.userAgent.initPlatform_();329}330331332/**333 * Whether the user agent is running on a Macintosh operating system.334 * @type {boolean}335 */336goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?337    goog.userAgent.ASSUME_MAC : goog.userAgent.detectedMac_;338339340/**341 * Whether the user agent is running on a Windows operating system.342 * @type {boolean}343 */344goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?345    goog.userAgent.ASSUME_WINDOWS : goog.userAgent.detectedWindows_;346347348/**349 * Whether the user agent is running on a Linux operating system.350 * @type {boolean}351 */352goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?353    goog.userAgent.ASSUME_LINUX : goog.userAgent.detectedLinux_;354355356/**357 * Whether the user agent is running on a X11 windowing system.358 * @type {boolean}359 */360goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?361    goog.userAgent.ASSUME_X11 : goog.userAgent.detectedX11_;362363364/**365 * Whether the user agent is running on Android.366 * @type {boolean}367 */368goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?369    goog.userAgent.ASSUME_ANDROID : goog.userAgent.detectedAndroid_;370371372/**373 * Whether the user agent is running on an iPhone.374 * @type {boolean}375 */376goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?377    goog.userAgent.ASSUME_IPHONE : goog.userAgent.detectedIPhone_;378379380/**381 * Whether the user agent is running on an iPad.382 * @type {boolean}383 */384goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?385    goog.userAgent.ASSUME_IPAD : goog.userAgent.detectedIPad_;386387388/**389 * @return {string} The string that describes the version number of the user390 *     agent.391 * @private392 */393goog.userAgent.determineVersion_ = function() {394  // All browsers have different ways to detect the version and they all have395  // different naming schemes.396397  // version is a string rather than a number because it may contain 'b', 'a',398  // and so on.399  var version = '', re;400401  if (goog.userAgent.OPERA && goog.global['opera']) {402    var operaVersion = goog.global['opera'].version;403    return goog.isFunction(operaVersion) ? operaVersion() : operaVersion;404  }405406  if (goog.userAgent.GECKO) {407    re = /rv\:([^\);]+)(\)|;)/;408  } else if (goog.userAgent.IE) {409    re = /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/;410  } else if (goog.userAgent.WEBKIT) {411    // WebKit/125.4412    re = /WebKit\/(\S+)/;413  }414415  if (re) {416    var arr = re.exec(goog.userAgent.getUserAgentString());417    version = arr ? arr[1] : '';418  }419420  if (goog.userAgent.IE) {421    // IE9 can be in document mode 9 but be reporting an inconsistent user agent422    // version.  If it is identifying as a version lower than 9 we take the423    // documentMode as the version instead.  IE8 has similar behavior.424    // It is recommended to set the X-UA-Compatible header to ensure that IE9425    // uses documentMode 9.426    var docMode = goog.userAgent.getDocumentMode_();427    if (docMode > parseFloat(version)) {428      return String(docMode);429    }430  }431432  return version;433};434435436/**437 * @return {number|undefined} Returns the document mode (for testing).438 * @private439 */440goog.userAgent.getDocumentMode_ = function() {441  // NOTE(user): goog.userAgent may be used in context where there is no DOM.442  var doc = goog.global['document'];443  return doc ? doc['documentMode'] : undefined;444};445446447/**448 * The version of the user agent. This is a string because it might contain449 * 'b' (as in beta) as well as multiple dots.450 * @type {string}451 */452goog.userAgent.VERSION = goog.userAgent.determineVersion_();453454455/**456 * Compares two version numbers.457 *458 * @param {string} v1 Version of first item.459 * @param {string} v2 Version of second item.460 *461 * @return {number}  1 if first argument is higher462 *                   0 if arguments are equal463 *                  -1 if second argument is higher.464 * @deprecated Use goog.string.compareVersions.465 */466goog.userAgent.compare = function(v1, v2) {467  return goog.string.compareVersions(v1, v2);468};469470471/**472 * Cache for {@link goog.userAgent.isVersionOrHigher}.473 * Calls to compareVersions are surprisingly expensive and, as a browser's474 * version number is unlikely to change during a session, we cache the results.475 * @const476 * @private477 */478goog.userAgent.isVersionOrHigherCache_ = {};479480481/**482 * Whether the user agent version is higher or the same as the given version.483 * NOTE: When checking the version numbers for Firefox and Safari, be sure to484 * use the engine's version, not the browser's version number.  For example,485 * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.486 * Opera and Internet Explorer versions match the product release number.<br>487 * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">488 *     Webkit</a>489 * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>490 *491 * @param {string|number} version The version to check.492 * @return {boolean} Whether the user agent version is higher or the same as493 *     the given version.494 */495goog.userAgent.isVersionOrHigher = function(version) {496  return goog.userAgent.ASSUME_ANY_VERSION ||497      goog.userAgent.isVersionOrHigherCache_[version] ||498      (goog.userAgent.isVersionOrHigherCache_[version] =499          goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);500};501502503/**504 * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.505 * @param {string|number} version The version to check.506 * @return {boolean} Whether the user agent version is higher or the same as507 *     the given version.508 * @deprecated Use goog.userAgent.isVersionOrHigher().509 */510goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;511512513/**514 * Whether the IE effective document mode is higher or the same as the given515 * document mode version.516 * NOTE: Only for IE, return false for another browser.517 *518 * @param {number} documentMode The document mode version to check.519 * @return {boolean} Whether the IE effective document mode is higher or the520 *     same as the given version.521 */522goog.userAgent.isDocumentModeOrHigher = function(documentMode) {523  return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;524};525526527/**528 * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.529 * @param {number} version The version to check.530 * @return {boolean} Whether the IE effective document mode is higher or the531 *      same as the given version.532 * @deprecated Use goog.userAgent.isDocumentModeOrHigher().533 */534goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;535536537/**538 * For IE version < 7, documentMode is undefined, so attempt to use the539 * CSS1Compat property to see if we are in standards mode. If we are in540 * standards mode, treat the browser version as the document mode. Otherwise,541 * IE is emulating version 5.542 * @type {number|undefined}543 * @const544 */545goog.userAgent.DOCUMENT_MODE = (function() {546  var doc = goog.global['document'];547  if (!doc || !goog.userAgent.IE) {548    return undefined;549  }550  var mode = goog.userAgent.getDocumentMode_();551  return mode || (doc['compatMode'] == 'CSS1Compat' ?552      parseInt(goog.userAgent.VERSION, 10) : 5);553})();