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