bowser.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. /*!
  2. * Bowser - a browser detector
  3. * https://github.com/ded/bowser
  4. * MIT License | (c) Dustin Diaz 2015
  5. */
  6. !function (root, name, definition) {
  7. if (typeof module != 'undefined' && module.exports) module.exports = definition()
  8. else if (typeof define == 'function' && define.amd) define(name, definition)
  9. else root[name] = definition()
  10. }(this, 'bowser', function () {
  11. /**
  12. * See useragents.js for examples of navigator.userAgent
  13. */
  14. var t = true
  15. function detect(ua) {
  16. function getFirstMatch(regex) {
  17. var match = ua.match(regex);
  18. return (match && match.length > 1 && match[1]) || '';
  19. }
  20. function getSecondMatch(regex) {
  21. var match = ua.match(regex);
  22. return (match && match.length > 1 && match[2]) || '';
  23. }
  24. var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase()
  25. , likeAndroid = /like android/i.test(ua)
  26. , android = !likeAndroid && /android/i.test(ua)
  27. , nexusMobile = /nexus\s*[0-6]\s*/i.test(ua)
  28. , nexusTablet = !nexusMobile && /nexus\s*[0-9]+/i.test(ua)
  29. , chromeos = /CrOS/.test(ua)
  30. , silk = /silk/i.test(ua)
  31. , sailfish = /sailfish/i.test(ua)
  32. , tizen = /tizen/i.test(ua)
  33. , webos = /(web|hpw)os/i.test(ua)
  34. , windowsphone = /windows phone/i.test(ua)
  35. , samsungBrowser = /SamsungBrowser/i.test(ua)
  36. , windows = !windowsphone && /windows/i.test(ua)
  37. , mac = !iosdevice && !silk && /macintosh/i.test(ua)
  38. , linux = !android && !sailfish && !tizen && !webos && /linux/i.test(ua)
  39. , edgeVersion = getSecondMatch(/edg([ea]|ios)\/(\d+(\.\d+)?)/i)
  40. , versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i)
  41. , tablet = /tablet/i.test(ua) && !/tablet pc/i.test(ua)
  42. , mobile = !tablet && /[^-]mobi/i.test(ua)
  43. , xbox = /xbox/i.test(ua)
  44. , result
  45. if (/opera/i.test(ua)) {
  46. // an old Opera
  47. result = {
  48. name: 'Opera'
  49. , opera: t
  50. , version: versionIdentifier || getFirstMatch(/(?:opera|opr|opios)[\s\/](\d+(\.\d+)?)/i)
  51. }
  52. } else if (/opr\/|opios/i.test(ua)) {
  53. // a new Opera
  54. result = {
  55. name: 'Opera'
  56. , opera: t
  57. , version: getFirstMatch(/(?:opr|opios)[\s\/](\d+(\.\d+)?)/i) || versionIdentifier
  58. }
  59. }
  60. else if (/SamsungBrowser/i.test(ua)) {
  61. result = {
  62. name: 'Samsung Internet for Android'
  63. , samsungBrowser: t
  64. , version: versionIdentifier || getFirstMatch(/(?:SamsungBrowser)[\s\/](\d+(\.\d+)?)/i)
  65. }
  66. }
  67. else if (/coast/i.test(ua)) {
  68. result = {
  69. name: 'Opera Coast'
  70. , coast: t
  71. , version: versionIdentifier || getFirstMatch(/(?:coast)[\s\/](\d+(\.\d+)?)/i)
  72. }
  73. }
  74. else if (/yabrowser/i.test(ua)) {
  75. result = {
  76. name: 'Yandex Browser'
  77. , yandexbrowser: t
  78. , version: versionIdentifier || getFirstMatch(/(?:yabrowser)[\s\/](\d+(\.\d+)?)/i)
  79. }
  80. }
  81. else if (/ucbrowser/i.test(ua)) {
  82. result = {
  83. name: 'UC Browser'
  84. , ucbrowser: t
  85. , version: getFirstMatch(/(?:ucbrowser)[\s\/](\d+(?:\.\d+)+)/i)
  86. }
  87. }
  88. else if (/mxios/i.test(ua)) {
  89. result = {
  90. name: 'Maxthon'
  91. , maxthon: t
  92. , version: getFirstMatch(/(?:mxios)[\s\/](\d+(?:\.\d+)+)/i)
  93. }
  94. }
  95. else if (/epiphany/i.test(ua)) {
  96. result = {
  97. name: 'Epiphany'
  98. , epiphany: t
  99. , version: getFirstMatch(/(?:epiphany)[\s\/](\d+(?:\.\d+)+)/i)
  100. }
  101. }
  102. else if (/puffin/i.test(ua)) {
  103. result = {
  104. name: 'Puffin'
  105. , puffin: t
  106. , version: getFirstMatch(/(?:puffin)[\s\/](\d+(?:\.\d+)?)/i)
  107. }
  108. }
  109. else if (/sleipnir/i.test(ua)) {
  110. result = {
  111. name: 'Sleipnir'
  112. , sleipnir: t
  113. , version: getFirstMatch(/(?:sleipnir)[\s\/](\d+(?:\.\d+)+)/i)
  114. }
  115. }
  116. else if (/k-meleon/i.test(ua)) {
  117. result = {
  118. name: 'K-Meleon'
  119. , kMeleon: t
  120. , version: getFirstMatch(/(?:k-meleon)[\s\/](\d+(?:\.\d+)+)/i)
  121. }
  122. }
  123. else if (windowsphone) {
  124. result = {
  125. name: 'Windows Phone'
  126. , osname: 'Windows Phone'
  127. , windowsphone: t
  128. }
  129. if (edgeVersion) {
  130. result.msedge = t
  131. result.version = edgeVersion
  132. }
  133. else {
  134. result.msie = t
  135. result.version = getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i)
  136. }
  137. }
  138. else if (/msie|trident/i.test(ua)) {
  139. result = {
  140. name: 'Internet Explorer'
  141. , msie: t
  142. , version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i)
  143. }
  144. } else if (chromeos) {
  145. result = {
  146. name: 'Chrome'
  147. , osname: 'Chrome OS'
  148. , chromeos: t
  149. , chromeBook: t
  150. , chrome: t
  151. , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)
  152. }
  153. } else if (/edg([ea]|ios)/i.test(ua)) {
  154. result = {
  155. name: 'Microsoft Edge'
  156. , msedge: t
  157. , version: edgeVersion
  158. }
  159. }
  160. else if (/vivaldi/i.test(ua)) {
  161. result = {
  162. name: 'Vivaldi'
  163. , vivaldi: t
  164. , version: getFirstMatch(/vivaldi\/(\d+(\.\d+)?)/i) || versionIdentifier
  165. }
  166. }
  167. else if (sailfish) {
  168. result = {
  169. name: 'Sailfish'
  170. , osname: 'Sailfish OS'
  171. , sailfish: t
  172. , version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i)
  173. }
  174. }
  175. else if (/seamonkey\//i.test(ua)) {
  176. result = {
  177. name: 'SeaMonkey'
  178. , seamonkey: t
  179. , version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i)
  180. }
  181. }
  182. else if (/firefox|iceweasel|fxios/i.test(ua)) {
  183. result = {
  184. name: 'Firefox'
  185. , firefox: t
  186. , version: getFirstMatch(/(?:firefox|iceweasel|fxios)[ \/](\d+(\.\d+)?)/i)
  187. }
  188. if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) {
  189. result.firefoxos = t
  190. result.osname = 'Firefox OS'
  191. }
  192. }
  193. else if (silk) {
  194. result = {
  195. name: 'Amazon Silk'
  196. , silk: t
  197. , version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i)
  198. }
  199. }
  200. else if (/phantom/i.test(ua)) {
  201. result = {
  202. name: 'PhantomJS'
  203. , phantom: t
  204. , version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i)
  205. }
  206. }
  207. else if (/slimerjs/i.test(ua)) {
  208. result = {
  209. name: 'SlimerJS'
  210. , slimer: t
  211. , version: getFirstMatch(/slimerjs\/(\d+(\.\d+)?)/i)
  212. }
  213. }
  214. else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) {
  215. result = {
  216. name: 'BlackBerry'
  217. , osname: 'BlackBerry OS'
  218. , blackberry: t
  219. , version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i)
  220. }
  221. }
  222. else if (webos) {
  223. result = {
  224. name: 'WebOS'
  225. , osname: 'WebOS'
  226. , webos: t
  227. , version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)
  228. };
  229. /touchpad\//i.test(ua) && (result.touchpad = t)
  230. }
  231. else if (/bada/i.test(ua)) {
  232. result = {
  233. name: 'Bada'
  234. , osname: 'Bada'
  235. , bada: t
  236. , version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i)
  237. };
  238. }
  239. else if (tizen) {
  240. result = {
  241. name: 'Tizen'
  242. , osname: 'Tizen'
  243. , tizen: t
  244. , version: getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i) || versionIdentifier
  245. };
  246. }
  247. else if (/qupzilla/i.test(ua)) {
  248. result = {
  249. name: 'QupZilla'
  250. , qupzilla: t
  251. , version: getFirstMatch(/(?:qupzilla)[\s\/](\d+(?:\.\d+)+)/i) || versionIdentifier
  252. }
  253. }
  254. else if (/chromium/i.test(ua)) {
  255. result = {
  256. name: 'Chromium'
  257. , chromium: t
  258. , version: getFirstMatch(/(?:chromium)[\s\/](\d+(?:\.\d+)?)/i) || versionIdentifier
  259. }
  260. }
  261. else if (/chrome|crios|crmo/i.test(ua)) {
  262. result = {
  263. name: 'Chrome'
  264. , chrome: t
  265. , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)
  266. }
  267. }
  268. else if (android) {
  269. result = {
  270. name: 'Android'
  271. , version: versionIdentifier
  272. }
  273. }
  274. else if (/safari|applewebkit/i.test(ua)) {
  275. result = {
  276. name: 'Safari'
  277. , safari: t
  278. }
  279. if (versionIdentifier) {
  280. result.version = versionIdentifier
  281. }
  282. }
  283. else if (iosdevice) {
  284. result = {
  285. name : iosdevice == 'iphone' ? 'iPhone' : iosdevice == 'ipad' ? 'iPad' : 'iPod'
  286. }
  287. // WTF: version is not part of user agent in web apps
  288. if (versionIdentifier) {
  289. result.version = versionIdentifier
  290. }
  291. }
  292. else if(/googlebot/i.test(ua)) {
  293. result = {
  294. name: 'Googlebot'
  295. , googlebot: t
  296. , version: getFirstMatch(/googlebot\/(\d+(\.\d+))/i) || versionIdentifier
  297. }
  298. }
  299. else {
  300. result = {
  301. name: getFirstMatch(/^(.*)\/(.*) /),
  302. version: getSecondMatch(/^(.*)\/(.*) /)
  303. };
  304. }
  305. // set webkit or gecko flag for browsers based on these engines
  306. if (!result.msedge && /(apple)?webkit/i.test(ua)) {
  307. if (/(apple)?webkit\/537\.36/i.test(ua)) {
  308. result.name = result.name || "Blink"
  309. result.blink = t
  310. } else {
  311. result.name = result.name || "Webkit"
  312. result.webkit = t
  313. }
  314. if (!result.version && versionIdentifier) {
  315. result.version = versionIdentifier
  316. }
  317. } else if (!result.opera && /gecko\//i.test(ua)) {
  318. result.name = result.name || "Gecko"
  319. result.gecko = t
  320. result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i)
  321. }
  322. // set OS flags for platforms that have multiple browsers
  323. if (!result.windowsphone && (android || result.silk)) {
  324. result.android = t
  325. result.osname = 'Android'
  326. } else if (!result.windowsphone && iosdevice) {
  327. result[iosdevice] = t
  328. result.ios = t
  329. result.osname = 'iOS'
  330. } else if (mac) {
  331. result.mac = t
  332. result.osname = 'macOS'
  333. } else if (xbox) {
  334. result.xbox = t
  335. result.osname = 'Xbox'
  336. } else if (windows) {
  337. result.windows = t
  338. result.osname = 'Windows'
  339. } else if (linux) {
  340. result.linux = t
  341. result.osname = 'Linux'
  342. }
  343. function getWindowsVersion (s) {
  344. switch (s) {
  345. case 'NT': return 'NT'
  346. case 'XP': return 'XP'
  347. case 'NT 5.0': return '2000'
  348. case 'NT 5.1': return 'XP'
  349. case 'NT 5.2': return '2003'
  350. case 'NT 6.0': return 'Vista'
  351. case 'NT 6.1': return '7'
  352. case 'NT 6.2': return '8'
  353. case 'NT 6.3': return '8.1'
  354. case 'NT 10.0': return '10'
  355. default: return undefined
  356. }
  357. }
  358. // OS version extraction
  359. var osVersion = '';
  360. if (result.windows) {
  361. osVersion = getWindowsVersion(getFirstMatch(/Windows ((NT|XP)( \d\d?.\d)?)/i))
  362. } else if (result.windowsphone) {
  363. osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i);
  364. } else if (result.mac) {
  365. osVersion = getFirstMatch(/Mac OS X (\d+([_\.\s]\d+)*)/i);
  366. osVersion = osVersion.replace(/[_\s]/g, '.');
  367. } else if (iosdevice) {
  368. osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i);
  369. osVersion = osVersion.replace(/[_\s]/g, '.');
  370. } else if (android) {
  371. osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i);
  372. } else if (result.webos) {
  373. osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i);
  374. } else if (result.blackberry) {
  375. osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i);
  376. } else if (result.bada) {
  377. osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i);
  378. } else if (result.tizen) {
  379. osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i);
  380. }
  381. if (osVersion) {
  382. result.osversion = osVersion;
  383. }
  384. // device type extraction
  385. var osMajorVersion = !result.windows && osVersion.split('.')[0];
  386. if (
  387. tablet
  388. || nexusTablet
  389. || iosdevice == 'ipad'
  390. || (android && (osMajorVersion == 3 || (osMajorVersion >= 4 && !mobile)))
  391. || result.silk
  392. ) {
  393. result.tablet = t
  394. } else if (
  395. mobile
  396. || iosdevice == 'iphone'
  397. || iosdevice == 'ipod'
  398. || android
  399. || nexusMobile
  400. || result.blackberry
  401. || result.webos
  402. || result.bada
  403. ) {
  404. result.mobile = t
  405. }
  406. // Graded Browser Support
  407. // http://developer.yahoo.com/yui/articles/gbs
  408. if (result.msedge ||
  409. (result.msie && result.version >= 10) ||
  410. (result.yandexbrowser && result.version >= 15) ||
  411. (result.vivaldi && result.version >= 1.0) ||
  412. (result.chrome && result.version >= 20) ||
  413. (result.samsungBrowser && result.version >= 4) ||
  414. (result.firefox && result.version >= 20.0) ||
  415. (result.safari && result.version >= 6) ||
  416. (result.opera && result.version >= 10.0) ||
  417. (result.ios && result.osversion && result.osversion.split(".")[0] >= 6) ||
  418. (result.blackberry && result.version >= 10.1)
  419. || (result.chromium && result.version >= 20)
  420. ) {
  421. result.a = t;
  422. }
  423. else if ((result.msie && result.version < 10) ||
  424. (result.chrome && result.version < 20) ||
  425. (result.firefox && result.version < 20.0) ||
  426. (result.safari && result.version < 6) ||
  427. (result.opera && result.version < 10.0) ||
  428. (result.ios && result.osversion && result.osversion.split(".")[0] < 6)
  429. || (result.chromium && result.version < 20)
  430. ) {
  431. result.c = t
  432. } else result.x = t
  433. return result
  434. }
  435. var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent || '' : '')
  436. bowser.test = function (browserList) {
  437. for (var i = 0; i < browserList.length; ++i) {
  438. var browserItem = browserList[i];
  439. if (typeof browserItem=== 'string') {
  440. if (browserItem in bowser) {
  441. return true;
  442. }
  443. }
  444. }
  445. return false;
  446. }
  447. /**
  448. * Get version precisions count
  449. *
  450. * @example
  451. * getVersionPrecision("1.10.3") // 3
  452. *
  453. * @param {string} version
  454. * @return {number}
  455. */
  456. function getVersionPrecision(version) {
  457. return version.split(".").length;
  458. }
  459. /**
  460. * Array::map polyfill
  461. *
  462. * @param {Array} arr
  463. * @param {Function} iterator
  464. * @return {Array}
  465. */
  466. function map(arr, iterator) {
  467. var result = [], i;
  468. if (Array.prototype.map) {
  469. return Array.prototype.map.call(arr, iterator);
  470. }
  471. for (i = 0; i < arr.length; i++) {
  472. result.push(iterator(arr[i]));
  473. }
  474. return result;
  475. }
  476. /**
  477. * Calculate browser version weight
  478. *
  479. * @example
  480. * compareVersions(['1.10.2.1', '1.8.2.1.90']) // 1
  481. * compareVersions(['1.010.2.1', '1.09.2.1.90']); // 1
  482. * compareVersions(['1.10.2.1', '1.10.2.1']); // 0
  483. * compareVersions(['1.10.2.1', '1.0800.2']); // -1
  484. *
  485. * @param {Array<String>} versions versions to compare
  486. * @return {Number} comparison result
  487. */
  488. function compareVersions(versions) {
  489. // 1) get common precision for both versions, for example for "10.0" and "9" it should be 2
  490. var precision = Math.max(getVersionPrecision(versions[0]), getVersionPrecision(versions[1]));
  491. var chunks = map(versions, function (version) {
  492. var delta = precision - getVersionPrecision(version);
  493. // 2) "9" -> "9.0" (for precision = 2)
  494. version = version + new Array(delta + 1).join(".0");
  495. // 3) "9.0" -> ["000000000"", "000000009"]
  496. return map(version.split("."), function (chunk) {
  497. return new Array(20 - chunk.length).join("0") + chunk;
  498. }).reverse();
  499. });
  500. // iterate in reverse order by reversed chunks array
  501. while (--precision >= 0) {
  502. // 4) compare: "000000009" > "000000010" = false (but "9" > "10" = true)
  503. if (chunks[0][precision] > chunks[1][precision]) {
  504. return 1;
  505. }
  506. else if (chunks[0][precision] === chunks[1][precision]) {
  507. if (precision === 0) {
  508. // all version chunks are same
  509. return 0;
  510. }
  511. }
  512. else {
  513. return -1;
  514. }
  515. }
  516. }
  517. /**
  518. * Check if browser is unsupported
  519. *
  520. * @example
  521. * bowser.isUnsupportedBrowser({
  522. * msie: "10",
  523. * firefox: "23",
  524. * chrome: "29",
  525. * safari: "5.1",
  526. * opera: "16",
  527. * phantom: "534"
  528. * });
  529. *
  530. * @param {Object} minVersions map of minimal version to browser
  531. * @param {Boolean} [strictMode = false] flag to return false if browser wasn't found in map
  532. * @param {String} [ua] user agent string
  533. * @return {Boolean}
  534. */
  535. function isUnsupportedBrowser(minVersions, strictMode, ua) {
  536. var _bowser = bowser;
  537. // make strictMode param optional with ua param usage
  538. if (typeof strictMode === 'string') {
  539. ua = strictMode;
  540. strictMode = void(0);
  541. }
  542. if (strictMode === void(0)) {
  543. strictMode = false;
  544. }
  545. if (ua) {
  546. _bowser = detect(ua);
  547. }
  548. var version = "" + _bowser.version;
  549. for (var browser in minVersions) {
  550. if (minVersions.hasOwnProperty(browser)) {
  551. if (_bowser[browser]) {
  552. if (typeof minVersions[browser] !== 'string') {
  553. throw new Error('Browser version in the minVersion map should be a string: ' + browser + ': ' + String(minVersions));
  554. }
  555. // browser version and min supported version.
  556. return compareVersions([version, minVersions[browser]]) < 0;
  557. }
  558. }
  559. }
  560. return strictMode; // not found
  561. }
  562. /**
  563. * Check if browser is supported
  564. *
  565. * @param {Object} minVersions map of minimal version to browser
  566. * @param {Boolean} [strictMode = false] flag to return false if browser wasn't found in map
  567. * @param {String} [ua] user agent string
  568. * @return {Boolean}
  569. */
  570. function check(minVersions, strictMode, ua) {
  571. return !isUnsupportedBrowser(minVersions, strictMode, ua);
  572. }
  573. bowser.isUnsupportedBrowser = isUnsupportedBrowser;
  574. bowser.compareVersions = compareVersions;
  575. bowser.check = check;
  576. /*
  577. * Set our detect method to the main bowser object so we can
  578. * reuse it to test other user agents.
  579. * This is needed to implement future tests.
  580. */
  581. bowser._detect = detect;
  582. /*
  583. * Set our detect public method to the main bowser object
  584. * This is needed to implement bowser in server side
  585. */
  586. bowser.detect = detect;
  587. return bowser
  588. });