Skip to content

Latest commit

 

History

History
315 lines (239 loc) · 11.3 KB

b4.md

File metadata and controls

315 lines (239 loc) · 11.3 KB

b4

  • Last update: 2017/08/17
  • Version: 2.0.0

An HTML snippet which checks whether the current browser is supported

<!-- Detect the browser support-level    richplastow.com/w4#b4    2017/08/17 -->
<script>!function(u,H,L){u=u.replace('ozilla/','ozilla-');var p,c,t,i,j,k,n,v,m,
V=' Version/',M='match',C=this.CustomEvent;f({w:'indows|; Win|(Win',a:'Android',
i:'iOS|iPh|iPo|iPa',m:'Mac OS X',l:'X11|Linux',_:' '});p=c;f({b:'ungBr|SAMSU',e:
' Edge/',u:' UCBr',l:'Links (|Lynx/',m:'Opera Mi',o:'OPiOS/|OPR/|Opera/|Opera ',
c:'CriOS/|Chrome/',f:'FxiOS/|Firefox/',i:'MSIE |Tride',a:'Android ',s:'Safari/|'
+'ebKit/',_:'/v|'+V});if('Tride'==n)i=u.indexOf('rv:')-4;k=0<=(j=u.indexOf(V))&&
!c[M](/[mca]/)?j+9:n?i+n.length:i;if(n)v=u.slice(k)[M](/^\s|\d+/);v=null!=v?+v:(
j=u[M](/[:\/]\d+\.\d+/g))?+j.pop()[M](/\d+/):(j=u[M](/[- v]\d+\.\d+/g))?+j.pop()
[M](/\d+/):0;if('s'==c&&84<v)for (t=v,k=v=0;t>k;k+=([411,8,106,7,3,1,1,63,1
][v++]));B:for(i=0;j=L[i];i++)for(t=0;m=j.split(' ')[t++];)if(c==m.charAt(0)&&v<
m.slice(1))break B;function f(X){for(c in X)for(t=0;n=X[c].split('|')[t++];)if(0
<=(i=u.indexOf(n)))return}H.className+=[null,'s'+i,c,'v'+v,'d'+('m'==c?'t':u[M](
/bot|crawl|spide|searc|okext/i)?'b':u[M](/Ninte|PS[P3]|YSTA|ySta|Xbo/)?'g':u[M](
/iP[aho]|[Tt]able|Android|obile|ckBer|Noki|Symb[iO]| UP\.Br|NetFr|J2ME|SonyEr/)?
't':'d'),'p'+p].join(' b4');if(C)dispatchEvent(new C('b4-'+(i?'ok':'fail')))}(
navigator.userAgent,document.lastChild,['b2 e12 o10 c15 f3 i6 a2 s4'])</script>

b4 has no dependencies, so you can place the minified code anywhere. When used alongside other w4 snippets, it’s placed near the top - between a4 and c4.

Arguments

  • It’s usual to keep the first argument set to navigator.userAgent. But for testing or spoofing you can set your own user agent string here.
  • It’s usual to keep second argument set to document.lastChild (which is a succinct way of referencing the <html> element). But for testing you could pass in any object with a string property named className.
  • You should change ['b2 e12 o10 c15 f3 i6 a2 s4'] to a list of browser name-codes and versions which you want to support.

Classes

b4 adds five classes to the <html> element. It also triggers a 'b4-ok' or 'b4-fail' event on the window — if you have a4 installed, it will convert these to "b4-ok" or "b4-fail" classes.

For example, Firefox 54 on desktop Mac OS X might look something like this:
<html class="b4s1 b4f b4v54 b4dd b4pm b4-ok">

1. Support Level, eg "b4s1"

If you define two support levels, and a4 is running, then the possible classes are:

  • b4-ok b4s2 The user agent is fully supported, and can run all app features
  • b4-ok b4s1 Legacy mode (medium-age browser, not all features supported)
  • b4-fail b4s0 The user agent is too old, and cannot even run in legacy mode

2. User Agent Name, eg "b4a"

  • b4a The Android browser
  • b4b Samsung Browser
  • b4c Chrome
  • b4e Microsoft Edge
  • b4f Firefox
  • b4i Internet Explorer
  • b4m Opera Mini
  • b4o Opera
  • b4s Safari
  • b4u UC Browser
  • b4_ Underscore signifies name not recognised

3. Version, eg "b4v55"

There may be cases where this is useful - you could target a particular version of Firefox in CSS using:

html.b4f.b4v55 .something {
    color: red;
}

4. Device, eg 'b4dd'

  • b4dc Crawler, or some other kind of bot
  • b4dd Desktop or laptop - likely to have a physical keyboard
  • b4dg Games console, eg PS3
  • b4dt Tablet or Phone - likely to be touchscreen

If not recognised, ‘Device’ defaults to "b4dd".

5. Platform, eg 'b4pa'

  • b4pa Android (tablet, phone or TV)
  • b4pi iOS (tablet, phone or TV)
  • b4pl Linux (desktop)
  • b4pm Mac OS X (desktop)
  • b4pw Windows (desktop)
  • b4p_ Underscore signifies platform not recognised

More Information

Verbose

Here’s a fully indented and commented version of the Minified code:

<!-- Detect the browser support-level    richplastow.com/w4#b4    2017/08/17 -->
<script>
!function ( // use a closure to avoid polluting global scope (pre ES6)
    u // the user-agent string, from `navigator.userAgent`
  , H // the <html> element
  , L // levels of support - an array of space-delimited strings, where each
      // string represents an ascending level of support
) {
    //// Prevent leading 'Mozilla/5.0' from being parsed as the version,
    //// unless all else fails.
    u = u.replace('ozilla/','ozilla-')

    var p // platform code - one of a, i, l, m, w, _
      , c // name code     - one of b, e, u, l, m, o, c, f, i, a, s, _
      , t // part-index, eg 'OPiOS/|OPR/|Opera/' has three parts
      , i // index of user agent name - then reused for ‘level of support’
      , j // index of 'Version/' - then reused to hold a space-delimited string
      , k // index of the start of the version
      , n // matching name string, eg 'msungBrowser/'
      , v // version, will be 0 if the version cannot be determined
      , m // an individual minimum user agent version
      , V = ' Version/' // helps minimise bytes
      , M = 'match'     // helps minimise bytes
      , C = this.CustomEvent

    //// Get `p`, the user agent’s platform.
    f({
        w: 'indows|; Win|(Win'  //
      , a: 'Android'            //
      , i: 'iOS|iPh|iPo|iPa'    //
      , m: 'Mac OS X'           //
      , l: 'X11|Linux'          // Linux
      , _: ' '                  // platform not recognised
    })
    p = c

    //// Get `n`, the user agent’s namecode.
    f({
        b: 'ungBr|SAMSU'        // Samsung browser
      , e: ' Edge/'             // Microsoft Edge
      , u: ' UCBr'              // UC Browser
      , l: 'Links (|Lynx/'      // Various ‘textmode’ user-agents
      , m: 'Opera Mi'           // Opera Mini
      , o: 'OPiOS/|OPR/|Opera/|Opera ' // Opera
      , c: 'CriOS/|Chrome/'     // Chrome
      , f: 'FxiOS/|Firefox/'    // Firefox
      , i: 'MSIE |Trident'      // Internet Explorer (use 'rv:' to get v)
      , a: 'Android '           // Android Webkit Browser
      , s: 'Safari/|ebKit/'     // Safari
      , _: '/v|' + V            // name not recognised
    })

    //// if 'Trident', use 'rv:' to get the version.
    if ('Trident' == n)
        i = u.indexOf('rv:') - 4 // `4` is 'Trident'.length - 'rv:'.length

    //// Get the user agent’s version.
    k = // find it after ' Version/' if available, unless Op’Mini/Chrome/Android
        0 <= (j=u.indexOf(V)) && ! c[M](/[mca]/) ? j + 9
      : n ? i + n.length : i
    if (n) v = u.slice(k)[M](/^\s|\d+/) // get digits after matched string
    v = null != v // found '123' or ' ' after matched string...
      ? +v // ...so convert it to a number (note, 'Chrome/ ' is version `0`)
      : (j = u[M](/[:\/]\d+\.\d+/g)) // else get each ':12.3' or '/4.56'
          ? +j.pop()[M](/\d+/) // use first digit from last occurance, else
          : (j = u[M](/[- v]\d+\.\d+/g)) // get each '-1.2', ' 3.4', 'v5.6'
              ? +j.pop()[M](/\d+/) // use first digit from last occurance
              : 0

    //// Convert a WebKit version to a Safari version (updated July 19 2017).
    // 1  / 85       to 312.6
    // 2  / 412      to 419.3
    // 3  / 420      to 525.29.1
    // 4  / 526.11.2 to 533.19.4
    // 5  / 533.16   to 534.59.10
    // 6  / 536.25   to 537.85.17
    // 7  / 537.71   to 537.85.17
    // 8  / 538.35.8 to 600.7.12
    // 9  / 601.1.56 to 601.7.8
    // 10 / 602.1.50 to 603.3.8
    if ('s' == c && 84 < v)
        for (t=v,k=v=0; t>k; k+=([411,8,106,7,3,1,1,63,1][v++])); // same as...
        // for (t=v,v=0; t>[0,411,419,525,532,535,536,537,600,601][++v];);

    //// Determine the level at which the current user agent supported.
    B: // break-label, to break out of the two-level loop
    for (i=0;j=L[i];i++) // step through each level
        for (t=0; m=j.split(' ')[t++];) // 'o10 c15' becomes 'o10', 'c15'
            if (c == m.charAt(0) // found a matching namecode, whose...
             && v < m.slice(1) ) // ...version is too low (coerce str to num)
                break B          // so the ua is not supported at this level

    //// Finds OScode and namecode.
    function f (X) {
        for (c in X) // `x` is each code
            for (t=0; n=X[c].split('|')[t++];) // `n` is 'iOS', then 'iPh' etc
                if ( 0 <= (i=u.indexOf(n)) ) // if found, `i` is start-index
                    return
    }

    //// Add the various `b4` classes to the <html> element.
    //// '/bot|crawl|spider/i' is a reduction of stackoverflow.com/a/20084661
    H.className += // `classList` is not supported in older user agents
        [   null    // makes `join()` add a ' b4-' in here
          , 's' + i // "b4s0" if the user agent is completely unsupported
          ,       c // the name code, one of b, e, u, l, m, o, c, f, i, a, s, _
          , 'v' + v // major-version, recorded as an integer (0 means unknown)
          , 'd' + ( // the device code, one of b, d, g, t
              'm' == c                               // Opera Mini
                                               ? 't' // phone or tablet
            : u[M](/bot|crawl|spide|searc|okext/i)
                                               ? 'b' // hardwarecode 'b' if bot
            : u[M](/Ninte|PS[P3]|YSTA|ySta|Xbo/)
                                               ? 'g' // games console
            : u[M](/iP[aho]|[Tt]ablet|Android|Mobile|ckBer|Nokia|Symb[iO]| UP\.Br|NetFron|J2ME|SonyEr/)
// Symbian, SymbianOS, SymbOS
                                               ? 't' // phone or tablet
            :                                    'd' // desktop
          )
          , 'p' + p // platform, one of a, i, l, m, w, _
        ].join(' b4')

    //// Trigger success of failure - a4 converts this to an <html> class.
    if (C)
        dispatchEvent( // you can `addEventListener()` for two events...
            new C(
                'b4-' + (i ? 'ok' : 'fail') // ...'b4-fail' and 'b4-ok'
            ) // `i` is `true` if the user agent is supported, `false` if not
        )

}(navigator.userAgent, document.lastChild, ['b2 e12 o10 c15 f3 i6 a2 s4'])
</script>

Usage Example

@TODO

Author

Created by Rich Plastow, during development of richplastow.com.

Tested

  • Android: Android Browser 2+, Chrome 34+, Firefox 51+, Samsung 3+
  • iOS 6: UC Browser 10
  • iOS 3: Safari 4
  • Windows 10: Edge 14+, IE 11
  • Windows 7: IE 10, Yandex 14 (identified as Chrome 38)
  • Windows XP: Firefox 3+, Chrome 15+, Safari 4+, Opera 10+, IE 6+
  • OS X Snow Leopard: Safari 4+

Changelog

  • 1.0.0 Enough for richplastow.com, but needs tests before wider adoption
  • 1.0.1 Outputs to <html class="...">, not window.b4
  • 2.0.0 New class-names and lots of unit tests