Completed
Push — master ( df95e6...30ee70 )
by Jesus
13s
created

amd/src/bowser.js   F

Complexity

Total Complexity 151
Complexity/F 12.58

Size

Lines of Code 575
Function Count 12

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
c 1
b 0
f 0
nc 0
dl 0
loc 575
rs 1.5789
wmc 151
mnd 34
bc 91
fnc 12
bpm 7.5833
cpm 12.5833
noi 7

How to fix   Complexity   

Complexity

Complex classes like amd/src/bowser.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
 * Bowser - a browser detector
3
 * https://github.com/ded/bowser
4
 * MIT License | (c) Dustin Diaz 2015
5
 */
6
7
define([], function() {
8
//!function (root, name, definition) {
9
//  if (typeof module != 'undefined' && module.exports) module.exports = definition()
10
//  else if (typeof define == 'function' && define.amd) define(name, definition)
11
//  else root[name] = definition()
12
//}(this, 'bowser', function () {
13
  /**
14
    * See useragents.js for examples of navigator.userAgent
15
    */
16
17
  var t = true;
18
19
  function detect(ua) {
20
21
    function getFirstMatch(regex) {
22
      var match = ua.match(regex);
23
      return (match && match.length > 1 && match[1]) || '';
24
    }
25
26
    function getSecondMatch(regex) {
27
      var match = ua.match(regex);
28
      return (match && match.length > 1 && match[2]) || '';
29
    }
30
31
    var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase()
32
      , likeAndroid = /like android/i.test(ua)
33
      , android = !likeAndroid && /android/i.test(ua)
34
      , nexusMobile = /nexus\s*[0-6]\s*/i.test(ua)
35
      , nexusTablet = !nexusMobile && /nexus\s*[0-9]+/i.test(ua)
36
      , chromeos = /CrOS/.test(ua)
37
      , silk = /silk/i.test(ua)
38
      , sailfish = /sailfish/i.test(ua)
39
      , tizen = /tizen/i.test(ua)
40
      , webos = /(web|hpw)os/i.test(ua)
41
      , windowsphone = /windows phone/i.test(ua)
42
      , samsungBrowser = /SamsungBrowser/i.test(ua)
43
      , windows = !windowsphone && /windows/i.test(ua)
44
      , mac = !iosdevice && !silk && /macintosh/i.test(ua)
45
      , linux = !android && !sailfish && !tizen && !webos && /linux/i.test(ua)
46
      , edgeVersion = getFirstMatch(/edge\/(\d+(\.\d+)?)/i)
47
      , versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i)
48
      , tablet = /tablet/i.test(ua)
49
      , mobile = !tablet && /[^-]mobi/i.test(ua)
50
      , xbox = /xbox/i.test(ua)
51
      , result
52
53
    if (/opera/i.test(ua)) {
54
      //  an old Opera
55
      result = {
56
        name: 'Opera'
57
      , opera: t
58
      , version: versionIdentifier || getFirstMatch(/(?:opera|opr|opios)[\s\/](\d+(\.\d+)?)/i)
59
      }
60
    } else if (/opr|opios/i.test(ua)) {
61
      // a new Opera
62
      result = {
63
        name: 'Opera'
64
        , opera: t
65
        , version: getFirstMatch(/(?:opr|opios)[\s\/](\d+(\.\d+)?)/i) || versionIdentifier
66
      }
67
    }
68
    else if (/SamsungBrowser/i.test(ua)) {
69
      result = {
70
        name: 'Samsung Internet for Android'
71
        , samsungBrowser: t
72
        , version: versionIdentifier || getFirstMatch(/(?:SamsungBrowser)[\s\/](\d+(\.\d+)?)/i)
73
      }
74
    }
75
    else if (/coast/i.test(ua)) {
76
      result = {
77
        name: 'Opera Coast'
78
        , coast: t
79
        , version: versionIdentifier || getFirstMatch(/(?:coast)[\s\/](\d+(\.\d+)?)/i)
80
      }
81
    }
82
    else if (/yabrowser/i.test(ua)) {
83
      result = {
84
        name: 'Yandex Browser'
85
      , yandexbrowser: t
86
      , version: versionIdentifier || getFirstMatch(/(?:yabrowser)[\s\/](\d+(\.\d+)?)/i)
87
      }
88
    }
89
    else if (/ucbrowser/i.test(ua)) {
90
      result = {
91
          name: 'UC Browser'
92
        , ucbrowser: t
93
        , version: getFirstMatch(/(?:ucbrowser)[\s\/](\d+(?:\.\d+)+)/i)
94
      }
95
    }
96
    else if (/mxios/i.test(ua)) {
97
      result = {
98
        name: 'Maxthon'
99
        , maxthon: t
100
        , version: getFirstMatch(/(?:mxios)[\s\/](\d+(?:\.\d+)+)/i)
101
      }
102
    }
103
    else if (/epiphany/i.test(ua)) {
104
      result = {
105
        name: 'Epiphany'
106
        , epiphany: t
107
        , version: getFirstMatch(/(?:epiphany)[\s\/](\d+(?:\.\d+)+)/i)
108
      }
109
    }
110
    else if (/puffin/i.test(ua)) {
111
      result = {
112
        name: 'Puffin'
113
        , puffin: t
114
        , version: getFirstMatch(/(?:puffin)[\s\/](\d+(?:\.\d+)?)/i)
115
      }
116
    }
117
    else if (/sleipnir/i.test(ua)) {
118
      result = {
119
        name: 'Sleipnir'
120
        , sleipnir: t
121
        , version: getFirstMatch(/(?:sleipnir)[\s\/](\d+(?:\.\d+)+)/i)
122
      }
123
    }
124
    else if (/k-meleon/i.test(ua)) {
125
      result = {
126
        name: 'K-Meleon'
127
        , kMeleon: t
128
        , version: getFirstMatch(/(?:k-meleon)[\s\/](\d+(?:\.\d+)+)/i)
129
      }
130
    }
131
    else if (windowsphone) {
132
      result = {
133
        name: 'Windows Phone'
134
      , windowsphone: t
135
      }
136
      if (edgeVersion) {
137
        result.msedge = t
138
        result.version = edgeVersion
139
      }
140
      else {
141
        result.msie = t
142
        result.version = getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i)
143
      }
144
    }
145
    else if (/msie|trident/i.test(ua)) {
146
      result = {
147
        name: 'Internet Explorer'
148
      , msie: t
149
      , version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i)
150
      }
151
    } else if (chromeos) {
152
      result = {
153
        name: 'Chrome'
154
      , chromeos: t
155
      , chromeBook: t
156
      , chrome: t
157
      , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)
158
      }
159
    } else if (/chrome.+? edge/i.test(ua)) {
160
      result = {
161
        name: 'Microsoft Edge'
162
      , msedge: t
163
      , version: edgeVersion
164
      }
165
    }
166
    else if (/vivaldi/i.test(ua)) {
167
      result = {
168
        name: 'Vivaldi'
169
        , vivaldi: t
170
        , version: getFirstMatch(/vivaldi\/(\d+(\.\d+)?)/i) || versionIdentifier
171
      }
172
    }
173
    else if (sailfish) {
174
      result = {
175
        name: 'Sailfish'
176
      , sailfish: t
177
      , version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i)
178
      }
179
    }
180
    else if (/seamonkey\//i.test(ua)) {
181
      result = {
182
        name: 'SeaMonkey'
183
      , seamonkey: t
184
      , version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i)
185
      }
186
    }
187
    else if (/firefox|iceweasel|fxios/i.test(ua)) {
188
      result = {
189
        name: 'Firefox'
190
      , firefox: t
191
      , version: getFirstMatch(/(?:firefox|iceweasel|fxios)[ \/](\d+(\.\d+)?)/i)
192
      }
193
      if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) {
194
        result.firefoxos = t
195
      }
196
    }
197
    else if (silk) {
198
      result =  {
199
        name: 'Amazon Silk'
200
      , silk: t
201
      , version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i)
202
      }
203
    }
204
    else if (/phantom/i.test(ua)) {
205
      result = {
206
        name: 'PhantomJS'
207
      , phantom: t
208
      , version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i)
209
      }
210
    }
211
    else if (/slimerjs/i.test(ua)) {
212
      result = {
213
        name: 'SlimerJS'
214
        , slimer: t
215
        , version: getFirstMatch(/slimerjs\/(\d+(\.\d+)?)/i)
216
      }
217
    }
218
    else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) {
219
      result = {
220
        name: 'BlackBerry'
221
      , blackberry: t
222
      , version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i)
223
      }
224
    }
225
    else if (webos) {
226
      result = {
227
        name: 'WebOS'
228
      , webos: t
229
      , version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)
230
      };
231
      /touchpad\//i.test(ua) && (result.touchpad = t)
232
    }
233
    else if (/bada/i.test(ua)) {
234
      result = {
235
        name: 'Bada'
236
      , bada: t
237
      , version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i)
238
      };
239
    }
240
    else if (tizen) {
241
      result = {
242
        name: '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
306
    // set webkit or gecko flag for browsers based on these engines
307
    if (!result.msedge && /(apple)?webkit/i.test(ua)) {
308
      if (/(apple)?webkit\/537\.36/i.test(ua)) {
309
        result.name = result.name || "Blink"
310
        result.blink = t
311
      } else {
312
        result.name = result.name || "Webkit"
313
        result.webkit = t
314
      }
315
      if (!result.version && versionIdentifier) {
316
        result.version = versionIdentifier
317
      }
318
    } else if (!result.opera && /gecko\//i.test(ua)) {
319
      result.name = result.name || "Gecko"
320
      result.gecko = t
321
      result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i)
322
    }
323
324
    // set OS flags for platforms that have multiple browsers
325
    if (!result.windowsphone && !result.msedge && (android || result.silk)) {
326
      result.android = t
327
    } else if (!result.windowsphone && !result.msedge && iosdevice) {
328
      result[iosdevice] = t
329
      result.ios = t
330
    } else if (mac) {
331
      result.mac = t
332
    } else if (xbox) {
333
      result.xbox = t
334
    } else if (windows) {
335
      result.windows = t
336
    } else if (linux) {
337
      result.linux = t
338
    }
339
340
    // OS version extraction
341
    var osVersion = '';
342
    if (result.windowsphone) {
343
      osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i);
344
    } else if (iosdevice) {
345
      osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i);
346
      osVersion = osVersion.replace(/[_\s]/g, '.');
347
    } else if (android) {
348
      osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i);
349
    } else if (result.webos) {
350
      osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i);
351
    } else if (result.blackberry) {
352
      osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i);
353
    } else if (result.bada) {
354
      osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i);
355
    } else if (result.tizen) {
356
      osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i);
357
    }
358
    if (osVersion) {
359
      result.osversion = osVersion;
360
    }
361
362
    // device type extraction
363
    var osMajorVersion = osVersion.split('.')[0];
364
    if (
365
         tablet
366
      || nexusTablet
367
      || iosdevice == 'ipad'
368
      || (android && (osMajorVersion == 3 || (osMajorVersion >= 4 && !mobile)))
369
      || result.silk
370
    ) {
371
      result.tablet = t
372
    } else if (
373
         mobile
374
      || iosdevice == 'iphone'
375
      || iosdevice == 'ipod'
376
      || android
377
      || nexusMobile
378
      || result.blackberry
379
      || result.webos
380
      || result.bada
381
    ) {
382
      result.mobile = t
383
    }
384
385
    // Graded Browser Support
386
    // http://developer.yahoo.com/yui/articles/gbs
387
    if (result.msedge ||
388
        (result.msie && result.version >= 10) ||
389
        (result.yandexbrowser && result.version >= 15) ||
390
		    (result.vivaldi && result.version >= 1.0) ||
391
        (result.chrome && result.version >= 20) ||
392
        (result.samsungBrowser && result.version >= 4) ||
393
        (result.firefox && result.version >= 20.0) ||
394
        (result.safari && result.version >= 6) ||
395
        (result.opera && result.version >= 10.0) ||
396
        (result.ios && result.osversion && result.osversion.split(".")[0] >= 6) ||
397
        (result.blackberry && result.version >= 10.1)
398
        || (result.chromium && result.version >= 20)
399
        ) {
400
      result.a = t;
401
    }
402
    else if ((result.msie && result.version < 10) ||
403
        (result.chrome && result.version < 20) ||
404
        (result.firefox && result.version < 20.0) ||
405
        (result.safari && result.version < 6) ||
406
        (result.opera && result.version < 10.0) ||
407
        (result.ios && result.osversion && result.osversion.split(".")[0] < 6)
408
        || (result.chromium && result.version < 20)
409
        ) {
410
      result.c = t
411
    } else result.x = t
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
412
413
    return result
414
  }
415
416
  var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent || '' : '')
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
417
418
  bowser.test = function (browserList) {
419
    for (var i = 0; i < browserList.length; ++i) {
420
      var browserItem = browserList[i];
421
      if (typeof browserItem=== 'string') {
422
        if (browserItem in bowser) {
423
          return true;
424
        }
425
      }
426
    }
427
    return false;
428
  }
429
430
  /**
431
   * Get version precisions count
432
   *
433
   * @example
434
   *   getVersionPrecision("1.10.3") // 3
435
   *
436
   * @param  {string} version
437
   * @return {number}
438
   */
439
  function getVersionPrecision(version) {
440
    return version.split(".").length;
441
  }
442
443
  /**
444
   * Array::map polyfill
445
   *
446
   * @param  {Array} arr
447
   * @param  {Function} iterator
448
   * @return {Array}
449
   */
450
  function map(arr, iterator) {
451
    var result = [], i;
452
    if (Array.prototype.map) {
453
      return Array.prototype.map.call(arr, iterator);
454
    }
455
    for (i = 0; i < arr.length; i++) {
456
      result.push(iterator(arr[i]));
457
    }
458
    return result;
459
  }
460
461
  /**
462
   * Calculate browser version weight
463
   *
464
   * @example
465
   *   compareVersions(['1.10.2.1',  '1.8.2.1.90'])    // 1
466
   *   compareVersions(['1.010.2.1', '1.09.2.1.90']);  // 1
467
   *   compareVersions(['1.10.2.1',  '1.10.2.1']);     // 0
468
   *   compareVersions(['1.10.2.1',  '1.0800.2']);     // -1
469
   *
470
   * @param  {Array<String>} versions versions to compare
471
   * @return {Number} comparison result
472
   */
473
  function compareVersions(versions) {
474
    // 1) get common precision for both versions, for example for "10.0" and "9" it should be 2
475
    var precision = Math.max(getVersionPrecision(versions[0]), getVersionPrecision(versions[1]));
476
    var chunks = map(versions, function (version) {
477
      var delta = precision - getVersionPrecision(version);
478
479
      // 2) "9" -> "9.0" (for precision = 2)
480
      version = version + new Array(delta + 1).join(".0");
481
482
      // 3) "9.0" -> ["000000000"", "000000009"]
483
      return map(version.split("."), function (chunk) {
484
        return new Array(20 - chunk.length).join("0") + chunk;
485
      }).reverse();
486
    });
487
488
    // iterate in reverse order by reversed chunks array
489
    while (--precision >= 0) {
490
      // 4) compare: "000000009" > "000000010" = false (but "9" > "10" = true)
491
      if (chunks[0][precision] > chunks[1][precision]) {
492
        return 1;
493
      }
494
      else if (chunks[0][precision] === chunks[1][precision]) {
495
        if (precision === 0) {
496
          // all version chunks are same
497
          return 0;
498
        }
499
      }
500
      else {
501
        return -1;
502
      }
503
    }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
504
  }
505
506
  /**
507
   * Check if browser is unsupported
508
   *
509
   * @example
510
   *   bowser.isUnsupportedBrowser({
511
   *     msie: "10",
512
   *     firefox: "23",
513
   *     chrome: "29",
514
   *     safari: "5.1",
515
   *     opera: "16",
516
   *     phantom: "534"
517
   *   });
518
   *
519
   * @param  {Object}  minVersions map of minimal version to browser
520
   * @param  {Boolean} [strictMode = false] flag to return false if browser wasn't found in map
0 ignored issues
show
Documentation Bug introduced by
The parameter [strictMode does not exist. Did you maybe mean strictMode instead?
Loading history...
521
   * @param  {String}  [ua] user agent string
522
   * @return {Boolean}
523
   */
524
  function isUnsupportedBrowser(minVersions, strictMode, ua) {
525
    var _bowser = bowser;
526
527
    // make strictMode param optional with ua param usage
528
    if (typeof strictMode === 'string') {
529
      ua = strictMode;
530
      strictMode = void(0);
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
531
    }
532
533
    if (strictMode === void(0)) {
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
534
      strictMode = false;
535
    }
536
    if (ua) {
537
      _bowser = detect(ua);
538
    }
539
540
    var version = "" + _bowser.version;
541
    for (var browser in minVersions) {
542
      if (minVersions.hasOwnProperty(browser)) {
543
        if (_bowser[browser]) {
544
          if (typeof minVersions[browser] !== 'string') {
545
            throw new Error('Browser version in the minVersion map should be a string: ' + browser + ': ' + String(minVersions));
546
          }
547
548
          // browser version and min supported version.
549
          return compareVersions([version, minVersions[browser]]) < 0;
550
        }
551
      }
552
    }
553
554
    return strictMode; // not found
555
  }
556
557
  /**
558
   * Check if browser is supported
559
   *
560
   * @param  {Object} minVersions map of minimal version to browser
561
   * @param  {Boolean} [strictMode = false] flag to return false if browser wasn't found in map
0 ignored issues
show
Documentation Bug introduced by
The parameter [strictMode does not exist. Did you maybe mean strictMode instead?
Loading history...
562
   * @param  {String}  [ua] user agent string
563
   * @return {Boolean}
564
   */
565
  function check(minVersions, strictMode, ua) {
566
    return !isUnsupportedBrowser(minVersions, strictMode, ua);
567
  }
568
569
  bowser.isUnsupportedBrowser = isUnsupportedBrowser;
570
  bowser.compareVersions = compareVersions;
571
  bowser.check = check;
572
573
  /*
574
   * Set our detect method to the main bowser object so we can
575
   * reuse it to test other user agents.
576
   * This is needed to implement future tests.
577
   */
578
  bowser._detect = detect;
579
580
  return bowser;
581
});
582