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 at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15/**
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), see
19 * goog.userAgent.product.
20 * @see ../demos/useragent.html
21 */
22
23goog.provide('goog.userAgent');
24
25goog.require('goog.string');
26
27
28/**
29 * @define {boolean} Whether we know at compile-time that the browser is IE.
30 */
31goog.define('goog.userAgent.ASSUME_IE', false);
32
33
34/**
35 * @define {boolean} Whether we know at compile-time that the browser is GECKO.
36 */
37goog.define('goog.userAgent.ASSUME_GECKO', false);
38
39
40/**
41 * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
42 */
43goog.define('goog.userAgent.ASSUME_WEBKIT', false);
44
45
46/**
47 * @define {boolean} Whether we know at compile-time that the browser is a
48 * mobile device running WebKit e.g. iPhone or Android.
49 */
50goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
51
52
53/**
54 * @define {boolean} Whether we know at compile-time that the browser is OPERA.
55 */
56goog.define('goog.userAgent.ASSUME_OPERA', false);
57
58
59/**
60 * @define {boolean} Whether the
61 * {@code goog.userAgent.isVersionOrHigher}
62 * function will return true for any version.
63 */
64goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
65
66
67/**
68 * Whether we know the browser engine at compile-time.
69 * @type {boolean}
70 * @private
71 */
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;
78
79
80/**
81 * Returns the userAgent string for the current browser.
82 * Some user agents (I'm thinking of you, Gears WorkerPool) do not expose a
83 * 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};
90
91
92/**
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};
101
102
103/**
104 * Initializer for goog.userAgent.
105 *
106 * This is a named function so that it can be stripped via the jscompiler
107 * option for stripping types.
108 * @private
109 */
110goog.userAgent.init_ = function() {
111 /**
112 * Whether the user agent string denotes Opera.
113 * @type {boolean}
114 * @private
115 */
116 goog.userAgent.detectedOpera_ = false;
117
118 /**
119 * Whether the user agent string denotes Internet Explorer. This includes
120 * other browsers using Trident as its rendering engine. For example AOL
121 * and Netscape 8
122 * @type {boolean}
123 * @private
124 */
125 goog.userAgent.detectedIe_ = false;
126
127 /**
128 * Whether the user agent string denotes WebKit. WebKit is the rendering
129 * engine that Safari, Android and others use.
130 * @type {boolean}
131 * @private
132 */
133 goog.userAgent.detectedWebkit_ = false;
134
135 /**
136 * Whether the user agent string denotes a mobile device.
137 * @type {boolean}
138 * @private
139 */
140 goog.userAgent.detectedMobile_ = false;
141
142 /**
143 * Whether the user agent string denotes Gecko. Gecko is the rendering
144 * engine used by Mozilla, Mozilla Firefox, Camino and many more.
145 * @type {boolean}
146 * @private
147 */
148 goog.userAgent.detectedGecko_ = false;
149
150 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};
166
167
168if (!goog.userAgent.BROWSER_KNOWN_) {
169 goog.userAgent.init_();
170}
171
172
173/**
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_;
179
180
181/**
182 * Whether the user agent is Internet Explorer. This includes other browsers
183 * using Trident as its rendering engine. For example AOL and Netscape 8
184 * @type {boolean}
185 */
186goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
187 goog.userAgent.ASSUME_IE : goog.userAgent.detectedIe_;
188
189
190/**
191 * Whether the user agent is Gecko. Gecko is the rendering engine used by
192 * 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_;
198
199
200/**
201 * Whether the user agent is WebKit. WebKit is the rendering engine that
202 * 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_;
208
209
210/**
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_;
216
217
218/**
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;
225
226
227/**
228 * @return {string} the platform (operating system) the user agent is running
229 * on. Default to empty string because navigator.platform may not be defined
230 * (on Rhino, for example).
231 * @private
232 */
233goog.userAgent.determinePlatform_ = function() {
234 var navigator = goog.userAgent.getNavigator();
235 return navigator && navigator.platform || '';
236};
237
238
239/**
240 * The platform (operating system) the user agent is running on. Default to
241 * empty string because navigator.platform may not be defined (on Rhino, for
242 * example).
243 * @type {string}
244 */
245goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
246
247
248/**
249 * @define {boolean} Whether the user agent is running on a Macintosh operating
250 * system.
251 */
252goog.define('goog.userAgent.ASSUME_MAC', false);
253
254
255/**
256 * @define {boolean} Whether the user agent is running on a Windows operating
257 * system.
258 */
259goog.define('goog.userAgent.ASSUME_WINDOWS', false);
260
261
262/**
263 * @define {boolean} Whether the user agent is running on a Linux operating
264 * system.
265 */
266goog.define('goog.userAgent.ASSUME_LINUX', false);
267
268
269/**
270 * @define {boolean} Whether the user agent is running on a X11 windowing
271 * system.
272 */
273goog.define('goog.userAgent.ASSUME_X11', false);
274
275
276/**
277 * @define {boolean} Whether the user agent is running on Android.
278 */
279goog.define('goog.userAgent.ASSUME_ANDROID', false);
280
281
282/**
283 * @define {boolean} Whether the user agent is running on an iPhone.
284 */
285goog.define('goog.userAgent.ASSUME_IPHONE', false);
286
287
288/**
289 * @define {boolean} Whether the user agent is running on an iPad.
290 */
291goog.define('goog.userAgent.ASSUME_IPAD', false);
292
293
294/**
295 * @type {boolean}
296 * @private
297 */
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;
306
307
308/**
309 * Initialize the goog.userAgent constants that define which platform the user
310 * agent is running on.
311 * @private
312 */
313goog.userAgent.initPlatform_ = function() {
314 /**
315 * Whether the user agent is running on a Macintosh operating system.
316 * @type {boolean}
317 * @private
318 */
319 goog.userAgent.detectedMac_ = goog.string.contains(goog.userAgent.PLATFORM,
320 'Mac');
321
322 /**
323 * Whether the user agent is running on a Windows operating system.
324 * @type {boolean}
325 * @private
326 */
327 goog.userAgent.detectedWindows_ = goog.string.contains(
328 goog.userAgent.PLATFORM, 'Win');
329
330 /**
331 * Whether the user agent is running on a Linux operating system.
332 * @type {boolean}
333 * @private
334 */
335 goog.userAgent.detectedLinux_ = goog.string.contains(goog.userAgent.PLATFORM,
336 'Linux');
337
338 /**
339 * Whether the user agent is running on a X11 windowing system.
340 * @type {boolean}
341 * @private
342 */
343 goog.userAgent.detectedX11_ = !!goog.userAgent.getNavigator() &&
344 goog.string.contains(goog.userAgent.getNavigator()['appVersion'] || '',
345 'X11');
346
347 // Need user agent string for Android/IOS detection
348 var ua = goog.userAgent.getUserAgentString();
349
350 /**
351 * Whether the user agent is running on Android.
352 * @type {boolean}
353 * @private
354 */
355 goog.userAgent.detectedAndroid_ = !!ua && ua.indexOf('Android') >= 0;
356
357 /**
358 * Whether the user agent is running on an iPhone.
359 * @type {boolean}
360 * @private
361 */
362 goog.userAgent.detectedIPhone_ = !!ua && ua.indexOf('iPhone') >= 0;
363
364 /**
365 * Whether the user agent is running on an iPad.
366 * @type {boolean}
367 * @private
368 */
369 goog.userAgent.detectedIPad_ = !!ua && ua.indexOf('iPad') >= 0;
370};
371
372
373if (!goog.userAgent.PLATFORM_KNOWN_) {
374 goog.userAgent.initPlatform_();
375}
376
377
378/**
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_;
384
385
386/**
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_;
392
393
394/**
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_;
400
401
402/**
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_;
408
409
410/**
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_;
416
417
418/**
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_;
424
425
426/**
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_;
432
433
434/**
435 * @return {string} The string that describes the version number of the user
436 * agent.
437 * @private
438 */
439goog.userAgent.determineVersion_ = function() {
440 // All browsers have different ways to detect the version and they all have
441 // different naming schemes.
442
443 // version is a string rather than a number because it may contain 'b', 'a',
444 // and so on.
445 var version = '', re;
446
447 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.4
457 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 agent
466 // version. If it is identifying as a version lower than 9 we take the
467 // documentMode as the version instead. IE8 has similar behavior.
468 // It is recommended to set the X-UA-Compatible header to ensure that IE9
469 // uses documentMode 9.
470 var docMode = goog.userAgent.getDocumentMode_();
471 if (docMode > parseFloat(version)) {
472 return String(docMode);
473 }
474 }
475 return version;
476};
477
478
479/**
480 * @return {number|undefined} Returns the document mode (for testing).
481 * @private
482 */
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};
488
489
490/**
491 * The version of the user agent. This is a string because it might contain
492 * 'b' (as in beta) as well as multiple dots.
493 * @type {string}
494 */
495goog.userAgent.VERSION = goog.userAgent.determineVersion_();
496
497
498/**
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 higher
505 * 0 if arguments are equal
506 * -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};
512
513
514/**
515 * Cache for {@link goog.userAgent.isVersionOrHigher}.
516 * Calls to compareVersions are surprisingly expensive and, as a browser's
517 * version number is unlikely to change during a session, we cache the results.
518 * @const
519 * @private
520 */
521goog.userAgent.isVersionOrHigherCache_ = {};
522
523
524/**
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 to
527 * 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 as
536 * 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};
544
545
546/**
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 as
550 * the given version.
551 * @deprecated Use goog.userAgent.isVersionOrHigher().
552 */
553goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
554
555
556/**
557 * Whether the IE effective document mode is higher or the same as the given
558 * 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 the
563 * same as the given version.
564 */
565goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
566 return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;
567};
568
569
570/**
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 the
574 * same as the given version.
575 */
576goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
577
578
579/**
580 * For IE version < 7, documentMode is undefined, so attempt to use the
581 * CSS1Compat property to see if we are in standards mode. If we are in
582 * standards mode, treat the browser version as the document mode. Otherwise,
583 * IE is emulating version 5.
584 * @type {number|undefined}
585 * @const
586 */
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})();