FeatureDetection.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. import Check from "./Check.js";
  2. import defaultValue from "./defaultValue.js";
  3. import defined from "./defined.js";
  4. import DeveloperError from "./DeveloperError.js";
  5. import Fullscreen from "./Fullscreen.js";
  6. let theNavigator;
  7. if (typeof navigator !== "undefined") {
  8. theNavigator = navigator;
  9. } else {
  10. theNavigator = {};
  11. }
  12. function extractVersion(versionString) {
  13. const parts = versionString.split(".");
  14. for (let i = 0, len = parts.length; i < len; ++i) {
  15. parts[i] = parseInt(parts[i], 10);
  16. }
  17. return parts;
  18. }
  19. let isChromeResult;
  20. let chromeVersionResult;
  21. function isChrome() {
  22. if (!defined(isChromeResult)) {
  23. isChromeResult = false;
  24. // Edge contains Chrome in the user agent too
  25. if (!isEdge()) {
  26. const fields = / Chrome\/([\.0-9]+)/.exec(theNavigator.userAgent);
  27. if (fields !== null) {
  28. isChromeResult = true;
  29. chromeVersionResult = extractVersion(fields[1]);
  30. }
  31. }
  32. }
  33. return isChromeResult;
  34. }
  35. function chromeVersion() {
  36. return isChrome() && chromeVersionResult;
  37. }
  38. let isSafariResult;
  39. let safariVersionResult;
  40. function isSafari() {
  41. if (!defined(isSafariResult)) {
  42. isSafariResult = false;
  43. // Chrome and Edge contain Safari in the user agent too
  44. if (
  45. !isChrome() &&
  46. !isEdge() &&
  47. / Safari\/[\.0-9]+/.test(theNavigator.userAgent)
  48. ) {
  49. const fields = / Version\/([\.0-9]+)/.exec(theNavigator.userAgent);
  50. if (fields !== null) {
  51. isSafariResult = true;
  52. safariVersionResult = extractVersion(fields[1]);
  53. }
  54. }
  55. }
  56. return isSafariResult;
  57. }
  58. function safariVersion() {
  59. return isSafari() && safariVersionResult;
  60. }
  61. let isWebkitResult;
  62. let webkitVersionResult;
  63. function isWebkit() {
  64. if (!defined(isWebkitResult)) {
  65. isWebkitResult = false;
  66. const fields = / AppleWebKit\/([\.0-9]+)(\+?)/.exec(theNavigator.userAgent);
  67. if (fields !== null) {
  68. isWebkitResult = true;
  69. webkitVersionResult = extractVersion(fields[1]);
  70. webkitVersionResult.isNightly = !!fields[2];
  71. }
  72. }
  73. return isWebkitResult;
  74. }
  75. function webkitVersion() {
  76. return isWebkit() && webkitVersionResult;
  77. }
  78. let isInternetExplorerResult;
  79. let internetExplorerVersionResult;
  80. function isInternetExplorer() {
  81. if (!defined(isInternetExplorerResult)) {
  82. isInternetExplorerResult = false;
  83. let fields;
  84. if (theNavigator.appName === "Microsoft Internet Explorer") {
  85. fields = /MSIE ([0-9]{1,}[\.0-9]{0,})/.exec(theNavigator.userAgent);
  86. if (fields !== null) {
  87. isInternetExplorerResult = true;
  88. internetExplorerVersionResult = extractVersion(fields[1]);
  89. }
  90. } else if (theNavigator.appName === "Netscape") {
  91. fields = /Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.exec(
  92. theNavigator.userAgent
  93. );
  94. if (fields !== null) {
  95. isInternetExplorerResult = true;
  96. internetExplorerVersionResult = extractVersion(fields[1]);
  97. }
  98. }
  99. }
  100. return isInternetExplorerResult;
  101. }
  102. function internetExplorerVersion() {
  103. return isInternetExplorer() && internetExplorerVersionResult;
  104. }
  105. let isEdgeResult;
  106. let edgeVersionResult;
  107. function isEdge() {
  108. if (!defined(isEdgeResult)) {
  109. isEdgeResult = false;
  110. const fields = / Edg\/([\.0-9]+)/.exec(theNavigator.userAgent);
  111. if (fields !== null) {
  112. isEdgeResult = true;
  113. edgeVersionResult = extractVersion(fields[1]);
  114. }
  115. }
  116. return isEdgeResult;
  117. }
  118. function edgeVersion() {
  119. return isEdge() && edgeVersionResult;
  120. }
  121. let isFirefoxResult;
  122. let firefoxVersionResult;
  123. function isFirefox() {
  124. if (!defined(isFirefoxResult)) {
  125. isFirefoxResult = false;
  126. const fields = /Firefox\/([\.0-9]+)/.exec(theNavigator.userAgent);
  127. if (fields !== null) {
  128. isFirefoxResult = true;
  129. firefoxVersionResult = extractVersion(fields[1]);
  130. }
  131. }
  132. return isFirefoxResult;
  133. }
  134. let isWindowsResult;
  135. function isWindows() {
  136. if (!defined(isWindowsResult)) {
  137. isWindowsResult = /Windows/i.test(theNavigator.appVersion);
  138. }
  139. return isWindowsResult;
  140. }
  141. let isIPadOrIOSResult;
  142. function isIPadOrIOS() {
  143. if (!defined(isIPadOrIOSResult)) {
  144. isIPadOrIOSResult =
  145. navigator.platform === "iPhone" ||
  146. navigator.platform === "iPod" ||
  147. navigator.platform === "iPad";
  148. }
  149. return isIPadOrIOSResult;
  150. }
  151. function firefoxVersion() {
  152. return isFirefox() && firefoxVersionResult;
  153. }
  154. let hasPointerEvents;
  155. function supportsPointerEvents() {
  156. if (!defined(hasPointerEvents)) {
  157. //While navigator.pointerEnabled is deprecated in the W3C specification
  158. //we still need to use it if it exists in order to support browsers
  159. //that rely on it, such as the Windows WebBrowser control which defines
  160. //PointerEvent but sets navigator.pointerEnabled to false.
  161. //Firefox disabled because of https://github.com/CesiumGS/cesium/issues/6372
  162. hasPointerEvents =
  163. !isFirefox() &&
  164. typeof PointerEvent !== "undefined" &&
  165. (!defined(theNavigator.pointerEnabled) || theNavigator.pointerEnabled);
  166. }
  167. return hasPointerEvents;
  168. }
  169. let imageRenderingValueResult;
  170. let supportsImageRenderingPixelatedResult;
  171. function supportsImageRenderingPixelated() {
  172. if (!defined(supportsImageRenderingPixelatedResult)) {
  173. const canvas = document.createElement("canvas");
  174. canvas.setAttribute(
  175. "style",
  176. "image-rendering: -moz-crisp-edges;" + "image-rendering: pixelated;"
  177. );
  178. //canvas.style.imageRendering will be undefined, null or an empty string on unsupported browsers.
  179. const tmp = canvas.style.imageRendering;
  180. supportsImageRenderingPixelatedResult = defined(tmp) && tmp !== "";
  181. if (supportsImageRenderingPixelatedResult) {
  182. imageRenderingValueResult = tmp;
  183. }
  184. }
  185. return supportsImageRenderingPixelatedResult;
  186. }
  187. function imageRenderingValue() {
  188. return supportsImageRenderingPixelated()
  189. ? imageRenderingValueResult
  190. : undefined;
  191. }
  192. function supportsWebP() {
  193. //>>includeStart('debug', pragmas.debug);
  194. if (!supportsWebP.initialized) {
  195. throw new DeveloperError(
  196. "You must call FeatureDetection.supportsWebP.initialize and wait for the promise to resolve before calling FeatureDetection.supportsWebP"
  197. );
  198. }
  199. //>>includeEnd('debug');
  200. return supportsWebP._result;
  201. }
  202. supportsWebP._promise = undefined;
  203. supportsWebP._result = undefined;
  204. supportsWebP.initialize = function () {
  205. // From https://developers.google.com/speed/webp/faq#how_can_i_detect_browser_support_for_webp
  206. if (defined(supportsWebP._promise)) {
  207. return supportsWebP._promise;
  208. }
  209. supportsWebP._promise = new Promise((resolve) => {
  210. const image = new Image();
  211. image.onload = function () {
  212. supportsWebP._result = image.width > 0 && image.height > 0;
  213. resolve(supportsWebP._result);
  214. };
  215. image.onerror = function () {
  216. supportsWebP._result = false;
  217. resolve(supportsWebP._result);
  218. };
  219. image.src =
  220. "data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA";
  221. });
  222. return supportsWebP._promise;
  223. };
  224. Object.defineProperties(supportsWebP, {
  225. initialized: {
  226. get: function () {
  227. return defined(supportsWebP._result);
  228. },
  229. },
  230. });
  231. const typedArrayTypes = [];
  232. if (typeof ArrayBuffer !== "undefined") {
  233. typedArrayTypes.push(
  234. Int8Array,
  235. Uint8Array,
  236. Int16Array,
  237. Uint16Array,
  238. Int32Array,
  239. Uint32Array,
  240. Float32Array,
  241. Float64Array
  242. );
  243. if (typeof Uint8ClampedArray !== "undefined") {
  244. typedArrayTypes.push(Uint8ClampedArray);
  245. }
  246. if (typeof Uint8ClampedArray !== "undefined") {
  247. typedArrayTypes.push(Uint8ClampedArray);
  248. }
  249. if (typeof BigInt64Array !== "undefined") {
  250. // eslint-disable-next-line no-undef
  251. typedArrayTypes.push(BigInt64Array);
  252. }
  253. if (typeof BigUint64Array !== "undefined") {
  254. // eslint-disable-next-line no-undef
  255. typedArrayTypes.push(BigUint64Array);
  256. }
  257. }
  258. /**
  259. * A set of functions to detect whether the current browser supports
  260. * various features.
  261. *
  262. * @namespace FeatureDetection
  263. */
  264. const FeatureDetection = {
  265. isChrome: isChrome,
  266. chromeVersion: chromeVersion,
  267. isSafari: isSafari,
  268. safariVersion: safariVersion,
  269. isWebkit: isWebkit,
  270. webkitVersion: webkitVersion,
  271. isInternetExplorer: isInternetExplorer,
  272. internetExplorerVersion: internetExplorerVersion,
  273. isEdge: isEdge,
  274. edgeVersion: edgeVersion,
  275. isFirefox: isFirefox,
  276. firefoxVersion: firefoxVersion,
  277. isWindows: isWindows,
  278. isIPadOrIOS: isIPadOrIOS,
  279. hardwareConcurrency: defaultValue(theNavigator.hardwareConcurrency, 3),
  280. supportsPointerEvents: supportsPointerEvents,
  281. supportsImageRenderingPixelated: supportsImageRenderingPixelated,
  282. supportsWebP: supportsWebP,
  283. imageRenderingValue: imageRenderingValue,
  284. typedArrayTypes: typedArrayTypes,
  285. };
  286. /**
  287. * Detects whether the current browser supports Basis Universal textures and the web assembly modules needed to transcode them.
  288. *
  289. * @param {Scene} scene
  290. * @returns {boolean} true if the browser supports web assembly modules and the scene supports Basis Universal textures, false if not.
  291. */
  292. FeatureDetection.supportsBasis = function (scene) {
  293. return FeatureDetection.supportsWebAssembly() && scene.context.supportsBasis;
  294. };
  295. /**
  296. * Detects whether the current browser supports the full screen standard.
  297. *
  298. * @returns {boolean} true if the browser supports the full screen standard, false if not.
  299. *
  300. * @see Fullscreen
  301. * @see {@link http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html|W3C Fullscreen Living Specification}
  302. */
  303. FeatureDetection.supportsFullscreen = function () {
  304. return Fullscreen.supportsFullscreen();
  305. };
  306. /**
  307. * Detects whether the current browser supports typed arrays.
  308. *
  309. * @returns {boolean} true if the browser supports typed arrays, false if not.
  310. *
  311. * @see {@link https://tc39.es/ecma262/#sec-typedarray-objects|Typed Array Specification}
  312. */
  313. FeatureDetection.supportsTypedArrays = function () {
  314. return typeof ArrayBuffer !== "undefined";
  315. };
  316. /**
  317. * Detects whether the current browser supports BigInt64Array typed arrays.
  318. *
  319. * @returns {boolean} true if the browser supports BigInt64Array typed arrays, false if not.
  320. *
  321. * @see {@link https://tc39.es/ecma262/#sec-typedarray-objects|Typed Array Specification}
  322. */
  323. FeatureDetection.supportsBigInt64Array = function () {
  324. return typeof BigInt64Array !== "undefined";
  325. };
  326. /**
  327. * Detects whether the current browser supports BigUint64Array typed arrays.
  328. *
  329. * @returns {boolean} true if the browser supports BigUint64Array typed arrays, false if not.
  330. *
  331. * @see {@link https://tc39.es/ecma262/#sec-typedarray-objects|Typed Array Specification}
  332. */
  333. FeatureDetection.supportsBigUint64Array = function () {
  334. return typeof BigUint64Array !== "undefined";
  335. };
  336. /**
  337. * Detects whether the current browser supports BigInt.
  338. *
  339. * @returns {boolean} true if the browser supports BigInt, false if not.
  340. *
  341. * @see {@link https://tc39.es/ecma262/#sec-bigint-objects|BigInt Specification}
  342. */
  343. FeatureDetection.supportsBigInt = function () {
  344. return typeof BigInt !== "undefined";
  345. };
  346. /**
  347. * Detects whether the current browser supports Web Workers.
  348. *
  349. * @returns {boolean} true if the browsers supports Web Workers, false if not.
  350. *
  351. * @see {@link http://www.w3.org/TR/workers/}
  352. */
  353. FeatureDetection.supportsWebWorkers = function () {
  354. return typeof Worker !== "undefined";
  355. };
  356. /**
  357. * Detects whether the current browser supports Web Assembly.
  358. *
  359. * @returns {boolean} true if the browsers supports Web Assembly, false if not.
  360. *
  361. * @see {@link https://developer.mozilla.org/en-US/docs/WebAssembly}
  362. */
  363. FeatureDetection.supportsWebAssembly = function () {
  364. return typeof WebAssembly !== "undefined";
  365. };
  366. /**
  367. * Detects whether the current browser supports a WebGL2 rendering context for the specified scene.
  368. *
  369. * @param {Scene} scene the Cesium scene specifying the rendering context
  370. * @returns {boolean} true if the browser supports a WebGL2 rendering context, false if not.
  371. *
  372. * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext|WebGL2RenderingContext}
  373. */
  374. FeatureDetection.supportsWebgl2 = function (scene) {
  375. //>>includeStart('debug', pragmas.debug);
  376. Check.defined("scene", scene);
  377. //>>includeEnd('debug');
  378. return scene.context.webgl2;
  379. };
  380. export default FeatureDetection;