1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 window.SC = window.SC || { MODULE_INFO: {}, LAZY_INSTANTIATION: {} }; 9 10 11 /** 12 The list of browsers that are automatically identified. 13 14 @readonly 15 @enum 16 */ 17 SC.BROWSER = { 18 android: 'android', 19 blackberry: 'blackberry', 20 chrome: 'chrome', 21 firefox: 'firefox', 22 ie: 'ie', 23 opera: 'opera', 24 safari: 'safari', 25 unknown: 'unknown' 26 }; 27 28 /** 29 The list of browser specific object prefixes, these are matched to the 30 browser engine. 31 32 @readonly 33 @enum 34 */ 35 SC.CLASS_PREFIX = { 36 gecko: 'Moz', 37 opera: 'O', 38 presto: 'O', 39 trident: 'Ms', // Note the uppercase 'M' 40 webkit: 'WebKit' // Note the uppercase 'K' 41 }; 42 43 /** 44 The list of browser specific CSS prefixes, these are matched to the 45 browser engine. 46 47 @readonly 48 @enum 49 */ 50 SC.CSS_PREFIX = { 51 gecko: '-moz-', 52 opera: '-o-', 53 presto: '-o-', 54 trident: '-ms-', 55 webkit: '-webkit-' 56 }; 57 58 /** 59 The list of devices that are automatically identified. 60 61 @readonly 62 @enum 63 */ 64 SC.DEVICE = { 65 android: 'android', 66 blackberry: 'blackberry', 67 desktop: 'desktop', 68 ipad: 'ipad', 69 iphone: 'iphone', 70 ipod: 'ipod', 71 mobile: 'mobile' 72 }; 73 74 /** 75 The list of browser specific DOM prefixes, these are matched to the 76 browser engine. 77 78 @readonly 79 @enum 80 */ 81 SC.DOM_PREFIX = { 82 gecko: 'Moz', 83 opera: 'O', 84 presto: 'O', 85 trident: 'ms', 86 webkit: 'Webkit' 87 }; 88 89 /** 90 The list of browser engines that are automatically identified. 91 92 @readonly 93 @enum 94 */ 95 SC.ENGINE = { 96 gecko: 'gecko', 97 opera: 'opera', 98 presto: 'presto', 99 trident: 'trident', 100 webkit: 'webkit' 101 }; 102 103 /** 104 The list of operating systems that are automatically identified. 105 106 @readonly 107 @enum 108 */ 109 SC.OS = { 110 android: 'android', 111 blackberry: 'blackberry', 112 ios: 'ios', 113 linux: 'linux', 114 mac: 'mac', 115 win: 'windows' 116 }; 117 118 119 /** 120 Detects browser properties based on the given userAgent and language. 121 122 @private 123 */ 124 SC.detectBrowser = function (userAgent, language) { 125 var browser = {}, 126 device, 127 engineAndVersion, 128 isIOSDevice, 129 conExp = '(?:[\\/:\\::\\s:;])', // Match the connecting character 130 numExp = '(\\S+[^\\s:;:\\)]|)', // Match the "number" 131 nameAndVersion, 132 os, osAndVersion, 133 override; 134 135 // Use the current values if none are provided. 136 userAgent = (userAgent || navigator.userAgent).toLowerCase(); 137 language = language || navigator.language || navigator.browserLanguage; 138 139 // Calculations to determine the device. See SC.DEVICE. 140 device = 141 userAgent.match(new RegExp('(android|ipad|iphone|ipod|blackberry)')) || 142 userAgent.match(new RegExp('(mobile)')) || 143 ['', SC.DEVICE.desktop]; 144 145 /** 146 @name SC.browser.device 147 @type SC.DEVICE|SC.BROWSER.unknown 148 */ 149 browser.device = device[1]; 150 151 152 // It simplifies further matching by recognizing this group of devices. 153 isIOSDevice = 154 browser.device === SC.DEVICE.ipad || 155 browser.device === SC.DEVICE.iphone || 156 browser.device === SC.DEVICE.ipod; 157 158 159 // Calculations to determine the name and version. See SC.BROWSER. 160 161 nameAndVersion = 162 // Match the specific names first, avoiding commonly spoofed browsers. 163 userAgent.match(new RegExp('(opera|chrome|firefox|android|blackberry)' + conExp + numExp)) || 164 userAgent.match(new RegExp('(ie|safari)' + conExp + numExp)) || 165 userAgent.match(new RegExp('(trident)')) || 166 ['', SC.BROWSER.unknown, '0']; 167 168 // If the device is an iOS device, use SC.BROWSER.safari for browser.name. 169 if (isIOSDevice) { nameAndVersion[1] = SC.BROWSER.safari; } 170 171 // If a `Version` number is found, use that over the `Name` number 172 override = userAgent.match(new RegExp('(version)' + conExp + numExp)); 173 if (override) { nameAndVersion[2] = override[2]; } 174 // If there is no `Version` in Safari, don't use the Safari number since it is 175 // the Webkit number. 176 else if (nameAndVersion[1] === SC.BROWSER.safari) { nameAndVersion[2] = '0'; } 177 else if (nameAndVersion[1] === SC.ENGINE.trident) { 178 // Special handling for IE11 (no 'ie' component, only 'trident' + 'rv') 179 nameAndVersion[1] = SC.BROWSER.ie; 180 this._ieVersion = nameAndVersion[2]; 181 nameAndVersion[2] = userAgent.match(new RegExp('(rv)' + conExp + numExp))[2]; 182 } 183 184 /** 185 @name SC.browser.name 186 @type SC.BROWSER|SC.BROWSER.unknown 187 */ 188 browser.name = nameAndVersion[1]; 189 190 /** 191 @name SC.browser.version 192 @type String 193 */ 194 browser.version = nameAndVersion[2]; 195 196 197 // Calculations to determine the engine and version. See SC.ENGINE. 198 engineAndVersion = 199 // Match the specific engines first, avoiding commonly spoofed browsers. 200 userAgent.match(new RegExp('(presto)' + conExp + numExp)) || 201 userAgent.match(new RegExp('(opera|trident|webkit|gecko)' + conExp + numExp)) || 202 ['', SC.BROWSER.unknown, '0']; 203 204 // If the browser is SC.BROWSER.ie, use SC.ENGINE.trident. 205 override = browser.name === SC.BROWSER.ie ? SC.ENGINE.trident : false; 206 if (override) { engineAndVersion[1] = override; } 207 208 // If the engineVersion is unknown and the browser is SC.BROWSER.ie, use 209 // browser.version for browser.engineVersion. 210 override = browser.name === SC.BROWSER.ie && engineAndVersion[2] === '0'; 211 if (override) { engineAndVersion[2] = browser.version; } 212 213 // If a `rv` number is found, use that over the engine number (except for IE11+ where 'rv' now indicates the browser version). 214 override = userAgent.match(new RegExp('(rv)' + conExp + numExp)); 215 if (override && engineAndVersion[1] !== SC.ENGINE.trident) { engineAndVersion[2] = override[2]; } 216 217 218 /** 219 @name SC.browser.engine 220 @type SC.ENGINE|SC.BROWSER.unknown 221 */ 222 browser.engine = engineAndVersion[1]; 223 224 /** 225 @name SC.browser.engineVersion 226 @type String 227 */ 228 browser.engineVersion = engineAndVersion[2]; 229 230 /** 231 The prefix of browser specific methods on this platform. 232 233 @name SC.browser.domPrefix 234 @type String 235 */ 236 browser.domPrefix = SC.DOM_PREFIX[browser.engine]; 237 238 /** 239 The prefix of browser specific properties on this platform. 240 241 @name SC.browser.classPrefix 242 @type String 243 */ 244 browser.classPrefix = SC.CLASS_PREFIX[browser.engine]; 245 246 /** 247 The prefix of browser specific CSS properties on this platform. 248 249 @name SC.browser.cssPrefix 250 @type String 251 */ 252 browser.cssPrefix = SC.CSS_PREFIX[browser.engine]; 253 254 255 // If we don't know the name of the browser, use the name of the engine. 256 if (browser.name === SC.BROWSER.unknown) { browser.name = browser.engine; } 257 258 // Calculations to determine the os and version. See SC.OS. 259 osAndVersion = 260 // Match the specific names first, avoiding commonly spoofed os's. 261 userAgent.match(new RegExp('(blackberry)')) || 262 userAgent.match(new RegExp('(android|iphone(?: os)|windows(?: nt))' + conExp + numExp)) || 263 userAgent.match(new RegExp('(os|mac(?: os)(?: x))' + conExp + numExp)) || 264 userAgent.match(new RegExp('(linux)')) || 265 [null, SC.BROWSER.unknown, '0']; 266 267 // Normalize the os name. 268 if (isIOSDevice) { os = SC.OS.ios; } 269 else if (osAndVersion[1] === 'mac os x' || osAndVersion[1] === 'mac os') { os = SC.OS.mac; } 270 else if (osAndVersion[1] === 'windows nt') { os = SC.OS.win; } 271 else { os = osAndVersion[1]; } 272 273 // Normalize the os version. 274 osAndVersion[2] = osAndVersion[2] ? osAndVersion[2].replace(/_/g, '.') : '0'; 275 276 277 /** 278 @name SC.browser.os 279 @type SC.OS|SC.BROWSER.unknown 280 */ 281 browser.os = os; 282 283 /** 284 @name SC.browser.osVersion 285 @type String 286 */ 287 browser.osVersion = osAndVersion[2]; 288 289 290 // The following long list of properties have all been deprecated. While they 291 // are a bit less verbose then the above constants, they lack standardization 292 // and can be prone to failure. Rather than continuing to expand this list 293 // with more and more one-off comparisons, which often muddle the line between 294 // the browser, the engine, the os and the device, it seems more practical to 295 // only maintain the 7 identifiable properties listed above: device, name, 296 // version, os, osVersion, engine and engineVersion. 297 298 /** @deprecated Version 1.7. Use browser.os === SC.OS.win. 299 @name SC.browser.isWindows 300 @type Boolean 301 */ 302 browser.windows = browser.isWindows = browser.os === SC.OS.win; 303 304 /** @deprecated Version 1.7. Use browser.os === SC.OS.mac. 305 @name SC.browser.isMac 306 @type Boolean 307 */ 308 browser.mac = browser.isMac = browser.os === SC.OS.mac; 309 310 /** @deprecated Version 1.7. Use browser.os === SC.OS.mac && browser.compare(browser.osVersion, '10.7') == 0 311 @name SC.browser.isLion 312 @type Boolean 313 */ 314 browser.lion = browser.isLion = !!(/mac os x 10_7/.test(userAgent) && !/like mac os x 10_7/.test(userAgent)); 315 316 /** @deprecated Version 1.7. Use browser.device === SC.DEVICE.iphone. 317 @name SC.browser.isiPhone 318 @type Boolean 319 */ 320 browser.iPhone = browser.isiPhone = browser.device === SC.DEVICE.iphone; 321 322 /** @deprecated Version 1.7. Use browser.device === SC.DEVICE.ipod. 323 @name SC.browser.isiPod 324 @type Boolean 325 */ 326 browser.iPod = browser.isiPod = browser.device === SC.DEVICE.ipod; 327 328 /** @deprecated Version 1.7. Use browser.device === SC.DEVICE.ipad. 329 @name SC.browser.isiPad 330 @type Boolean 331 */ 332 browser.iPad = browser.isiPad = browser.device === SC.DEVICE.ipad; 333 334 /** @deprecated Version 1.7. Use browser.os === SC.OS.ios. 335 @name SC.browser.isiOS 336 @type Boolean 337 */ 338 browser.iOS = browser.isiOS = browser.os === SC.OS.ios; 339 340 /** @deprecated Version 1.7. Use browser.os === SC.OS.android or browser.name === SC.BROWSER.android or browser.device === SC.DEVICE.android. 341 @name SC.browser.isAndroid 342 @type Boolean 343 */ 344 browser.android = browser.isAndroid = browser.os === SC.OS.android; 345 346 /** @deprecated Version 1.7. Use browser.version or browser.engineVersion. 347 @name SC.browser.opera 348 @type String 349 */ 350 browser.opera = browser.name === SC.BROWSER.opera ? browser.version : '0'; 351 352 /** @deprecated Version 1.7. Use browser.name === SC.BROWSER.opera. 353 @name SC.browser.isOpera 354 @type Boolean 355 */ 356 browser.isOpera = browser.name === SC.BROWSER.opera; 357 358 /** @deprecated Version 1.7. Use browser.version or browser.engineVersion. 359 @name SC.browser.msie 360 @type String 361 */ 362 browser.msie = browser.name === SC.BROWSER.ie ? browser.version : '0'; 363 364 /** @deprecated Version 1.7. Use browser.engine === SC.ENGINE.trident. 365 @name SC.browser.isIE 366 @type Boolean 367 */ 368 browser.isIE = browser.engine === SC.ENGINE.trident; 369 370 /** @deprecated Version 1.7. Use browser.compare(browser.version, '8') <= 0. 371 @name SC.browser.isIE8OrLower 372 @type Boolean 373 */ 374 browser.isIE8OrLower = browser.name === SC.BROWSER.ie && browser.version <= 8; 375 376 /** @deprecated Version 1.7. Use browser.version or browser.engineVersion. 377 @name SC.browser.mozilla 378 @type String 379 */ 380 browser.mozilla = browser.engine === SC.ENGINE.gecko ? browser.version : '0'; 381 382 /** @deprecated Version 1.7. Use browser.name === SC.BROWSER.firefox or browser.engine === SC.ENGINE.gecko. 383 @name SC.browser.isMozilla 384 @type Boolean 385 */ 386 browser.isMozilla = browser.engine === SC.ENGINE.gecko; 387 388 /** @deprecated Version 1.7. Use browser.engineVersion. 389 @name SC.browser.webkit 390 @type String 391 */ 392 browser.webkit = browser.engine === SC.ENGINE.webkit ? browser.engineVersion : '0'; 393 394 /** @deprecated Version 1.7. Use browser.engine === SC.ENGINE.webkit. 395 @name SC.browser.isWebkit 396 @type Boolean 397 */ 398 browser.isWebkit = browser.engine === SC.ENGINE.webkit; 399 400 /** @deprecated Version 1.7. Use browser.version. 401 @name SC.browser.chrome 402 @type String 403 */ 404 browser.chrome = browser.name === SC.BROWSER.chrome ? browser.version : '0'; 405 406 /** @deprecated Version 1.7. Use browser.name === SC.BROWSER.chrome. 407 @name SC.browser.isChrome 408 @type Boolean 409 */ 410 browser.isChrome = browser.name === SC.BROWSER.chrome; 411 412 /** @deprecated Version 1.7. Use browser.version. 413 @name SC.browser.mobileSafari 414 @type String 415 */ 416 browser.mobileSafari = browser.os === SC.OS.ios ? browser.version : '0'; 417 418 /** @deprecated Version 1.7. Use browser.name === SC.BROWSER.safari && browser.os === SC.OS.ios 419 @name SC.browser.isMobileSafari 420 @type Boolean 421 */ 422 browser.isMobileSafari = browser.name === SC.BROWSER.safari && browser.os === SC.OS.ios; 423 424 /** @deprecated Version 1.7. Use browser.version. 425 @name SC.browser.iPadSafari 426 @type String 427 */ 428 browser.iPadSafari = browser.device === SC.DEVICE.ipad && browser.name === SC.BROWSER.safari ? 429 browser.version : 0; 430 431 /** @deprecated Version 1.7. Use browser.device === SC.DEVICE.ipad && browser.name === SC.BROWSER.safari 432 @name SC.browser.isiPadSafari 433 @type Boolean 434 */ 435 browser.isiPadSafari = browser.device === SC.DEVICE.ipad && browser.name === SC.BROWSER.safari; 436 437 /** @deprecated Version 1.7. Use browser.version. 438 @name SC.browser.iPhoneSafari 439 @type String 440 */ 441 browser.iPhoneSafari = browser.device === SC.DEVICE.iphone && browser.name === SC.BROWSER.safari ? 442 browser.version : 0; 443 444 /** @deprecated Version 1.7. Use browser.device === SC.DEVICE.iphone && browser.name === SC.BROWSER.safari 445 @name SC.browser.isiPhoneSafari 446 @type Boolean 447 */ 448 browser.isiPhoneSafari = browser.device === SC.DEVICE.iphone && browser.name === SC.BROWSER.safari; 449 450 /** @deprecated Version 1.7. Use browser.version. 451 @name SC.browser.iPodSafari 452 @type String 453 */ 454 browser.iPodSafari = browser.device === SC.DEVICE.ipod && browser.name === SC.BROWSER.safari ? 455 browser.version : 0; 456 457 /** @deprecated Version 1.7. Use browser.device === SC.DEVICE.ipod && browser.name === SC.BROWSER.safari 458 @name SC.browser.isiPodSafari 459 @type Boolean 460 */ 461 browser.isiPodSafari = browser.device === SC.DEVICE.ipod && browser.name === SC.BROWSER.safari; 462 463 /** @deprecated Version 1.7. Use SC.platform.standalone. 464 @name SC.browser.isiOSHomeScreen 465 @type Boolean 466 */ 467 browser.isiOSHomeScreen = browser.isMobileSafari && !(/apple.*mobile.*safari/.test(userAgent)); 468 469 /** @deprecated Version 1.7. Use browser.version. 470 @name SC.browser.safari 471 @type String 472 */ 473 browser.safari = browser.name === SC.BROWSER.safari && browser.os === SC.OS.mac ? 474 browser.version : 0; 475 476 /** @deprecated Version 1.7. Use browser.name === SC.BROWSER.safari && browser.os === SC.OS.mac. 477 @name SC.browser.isSafari 478 @type Boolean 479 */ 480 browser.isSafari = browser.name === SC.BROWSER.safari && browser.os === SC.OS.mac; 481 482 /** 483 @name SC.browser.language 484 @type String 485 */ 486 browser.language = language.split('-', 1)[0]; 487 488 /** 489 @name SC.browser.countryCode 490 @type String 491 */ 492 browser.countryCode = language.split('-')[1] ? language.split('-')[1].toLowerCase() : undefined; 493 494 /** @deprecated Version 1.7. Use browser.name. See SC.BROWSER for possible values. 495 @name SC.browser.current 496 @type String 497 */ 498 browser.current = browser.name; 499 500 return browser; 501 }; 502 503 504 /** @class 505 506 This object contains information about the browser environment SproutCore is 507 running in. This includes the following properties: 508 509 - browser.device ex. SC.DEVICE.ipad 510 - browser.name ex. SC.BROWSER.chrome 511 - browser.version ex. '16.0.2.34' 512 - browser.os ex. SC.OS.mac 513 - browser.osVersion ex. '10.6' 514 - browser.engine ex. SC.ENGINE.webkit 515 - browser.engineVersion ex. '533.29' 516 - browser.cssPrefix ex. '-webkit-' 517 - browser.classPrefix ex. 'WebKit' 518 - browser.domPrefix ex. 'webkit' 519 520 Note: User agent sniffing does not provide guaranteed results and spoofing may 521 affect the accuracy. Therefore, as a general rule, it is much better 522 to rely on the browser's verified capabilities in SC.platform. But if you must 523 write browser specific code, understand that SC.browser does an exceptional 524 job at identifying the current browser. 525 526 Based on the unit test samples, the most stable browser properties appear to 527 be `engine` and `engineVersion`. 528 529 @since Version 1.0 530 */ 531 SC.browser = SC.detectBrowser(); 532