Skip to content

Instantly share code, notes, and snippets.

@WebMechanic
Created May 2, 2020 21:39
Show Gist options
  • Select an option

  • Save WebMechanic/6f05fbc688d055c89bd1b0d0db32ff15 to your computer and use it in GitHub Desktop.

Select an option

Save WebMechanic/6f05fbc688d055c89bd1b0d0db32ff15 to your computer and use it in GitHub Desktop.
A simple browser feature sleuth that sets class names on html and body.
/**
* A simple browser feature sleuth that sets class names on html and body.
* - Sleuth.sniff()
* Tested with IE9-11, all evergreen Chrome, Firefox, EdgeHTML/Edgium, Opera.
*
* @version 1.3.3 02.05.2020
* @author WebMechanic.biz (c)2019
* @license DWTF
*/
const Sleuth = {agents:{}, features:{},
setFeature: function (key, elt) {
elt.classList.add(key);
},
sniff: function () {
this.root = document.documentElement;
let site = this,
body = document.body,
ua = (navigator.userAgent || 'Mozilla/5').replace(/\w+\/537.36/gi, 'FAKE/537.36'),
ff = ('originalTarget' in Event.prototype) && ('CSS2Properties' in window),
msie = ('runtimeStyle' in HTMLBodyElement.prototype),
edge = !msie && ('msHyphenateLimitChars' in body.style) && ('maxTouchPoints' in navigator),
edg = !edge,
CSSOM = ('CSS' in window) && (CSSRule['SUPPORTS_RULE']);
/* collection of booleans, undefined or nulls */
site.features = {
touch: ('ontouchstart' in this.root),
lazyloading: ('loading' in HTMLImageElement.prototype),
mediaquery: ('matchMedia' in window),
resize: ('ResizeObserver' in window),
intersection: ('IntersectionObserver' in window),
fontface: ('FontFace' in window) && !!document['fonts'],
cssom: CSSOM,
csstypes: !!(CSSOM && CSS.number), /* typed CSSOM that is; 'CSSUnitValue' in window */
houdini: CSSOM && ('paintWorklet' in CSS),
flex: CSSOM && CSS.supports("display", "flex"),
msflex: msie && ('msFlexLinePack' in body.style),
grid: CSSOM && CSS.supports("display", "grid"),
msgrid: msie && ('msGridColumnAlign' in body.style),
backdropfilter: CSSOM && (CSS.supports('backdrop-filter', 'blur(1px)') || CSS.supports('-webkit-backdrop-filter', 'blur(1px)')),
cssmin: CSSOM && CSS.supports("width", "min(1px,2px)"),
cssmax: CSSOM && CSS.supports("width", "max(1px,2px)"),
cssclamp: CSSOM && CSS.supports("width", "clamp(1px,2px,3px)")
};
/* keep what's true */
Object.keys(site.features).forEach(function (key, index) {
if (!site.features[key]) {
delete site.features[key];
} else {
site.setFeature(key, body);
}
});
(this.debug) && console.log('features',site.features);
/* engines and user agents, collection of booleans, undefined or nulls */
site.agents = {
msie : msie,
trident: msie && !!(ua.match("Trident/(\\d+)", "g")),
presto : ('supportsCSS' in window) && ('opera' in window && window.opera.version() <= 13), /* !!(ua.match("Presto/(\\d+)", "g")) */
edge : edge && CSSOM, /* 12:navigator.maxTouchPoints, 14:navigator.sendBeacon() */
edgium : edg = CSSOM && !!(ua.match("Edg/(\\d+)", "g")),
gecko : ff && !!(ua.match("rv:(\\d+).*Firefox/\\1", "g")),
servo : ff && CSSOM && (Error.prototype instanceof Object), /* Firefox 53+ Quantum CSS Engine */
chrome : (window.chrome) && ('CSSVariableReferenceValue' in window), /* CR66+,EG79,OP53 */
opera : !!(ua.match("OPR/(\\d+)", "g")) && ('opr' in window),
blink : !edge && CSSOM && !!(ua.match("(?:Chrome|ChriOS)/(\\d+)", "g")), /* Chrome, OPera (OPR/15+), VIvaldi, Amazon Silk, Edg/79+, SamsungInternet, Android Webview */
webkit : false, /* SaFari */
};
(this.debug) && console.log('agents',site.agents);
if (msie && site.agents.trident) {
/* add stub entry for setClasses() ie9: Trident/5, ie10: Trident/6, ie11: Trident/7 */
site.agents['is-ie'] =
site.agents['msie' + (document.documentMode||7)] = true;
} else if (!site.agents.gecko) {
site.agents.webkit = (!site.agents.edge && ('webkitAnimation' in body.style));
site.agents.chrome = (site.agents.blink && site.agents.webkit);
/* Old Edge on iOS and Android uses different engines
* @link https://blogs.windows.com/msedgedev/2017/10/05/microsoft-edge-ios-android-developer/ */
if (site.agents.webkit && (edg = ua.match("EdgiOS/(\\d+)", "g"))) {
site.agents['edge-ios'] = true;
} else if (site.agents.chrome && (edg = ua.match("EdgA/(\\d+)", "g"))) {
site.agents['edge-and'] = true;
}
}
/* keep what's true */
Object.keys(site.agents).forEach(function (key, index) {
if (!site.agents[key]) {
delete site.agents[key];
} else {
site.setFeature(key, this.root);
}
});
},
debug: false,
version: '1.3.3'
};
(function() {
Sleuth.debug = true; /* check console */
Sleuth.sniff();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment