├── .gitignore ├── package.json ├── LICENSE ├── README.md ├── test └── index.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sniffer", 3 | "version": "2.0.3", 4 | "description": "A browser sniffing util", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "browserify test/index.js | tap-closer | smokestack | faucet", 11 | "test-debug": "budo test/index.js --live" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+ssh://git@github.com/watsondg/sniffer.git" 16 | }, 17 | "keywords": [ 18 | "browser", 19 | "sniffing", 20 | "detect", 21 | "device", 22 | "os" 23 | ], 24 | "author": "Florian Morel", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/watsondg/sniffer/issues" 28 | }, 29 | "homepage": "https://github.com/watsondg/sniffer#readme", 30 | "devDependencies": { 31 | "faucet": "0.0.1", 32 | "smokestack": "^3.4.1", 33 | "tap-closer": "^1.0.0", 34 | "tape": "^4.5.1" 35 | }, 36 | "dependencies": { 37 | "dashify": "^0.2.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Watson Design Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sniffer.js 2 | === 3 | 4 | A browser sniffing util. 5 | 6 | ## Install 7 | 8 | ``` 9 | npm install watsondg/sniffer -S 10 | ``` 11 | 12 | ## Usage 13 | 14 | ``` 15 | var sniffer = require('sniffer'); 16 | 17 | console.log(sniffer.isDesktop); // true 18 | 19 | // If using any app singleton, you can do something like 20 | _.extend(App, sniffer.getInfos()); 21 | console.log(App.isDesktop); // true 22 | 23 | sniffer.addClasses(document.documentElement); 24 | console.log(document.documentElement.className); // is-desktop 25 | ``` 26 | 27 | ## Instance Methods 28 | 29 | ### addClasses(el) 30 | 31 | Add dashed-case sniffing classes to the given element, i.e. `is-ios`, `is-firefox`. 32 | * `el` - the element to add classes to. 33 | 34 | 35 | ### getInfos() 36 | 37 | Return an object containing all the sniffing properties. 38 | 39 | ## Instance Properties 40 | 41 | ### isEdge 42 | ### isIE 43 | ### isIE11 44 | 45 | ### isDroid 46 | ### isDroidTablet 47 | ### isDroidPhone 48 | 49 | ### isIos 50 | ### isIpad 51 | 52 | ### isTablet 53 | ### isPhone 54 | ### isDevice `(isPhone && isTablet)` 55 | 56 | ### isDesktop 57 | ### isFirefox 58 | ### isSafari 59 | ### isOpera 60 | ### isChrome 61 | 62 | ## License 63 | MIT. 64 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var sniffer = require('../index.js'); 5 | 6 | test('Desktop test', function(assert) { 7 | assert.ok(sniffer.isDesktop, 'isDesktop should be true.'); 8 | assert.end(); 9 | }); 10 | 11 | test('Browsers test', function(assert) { 12 | assert.notOk(sniffer.isDesktop === sniffer.isPhone, 'Shouldnt be both desktop and phone.'); 13 | 14 | assert.notOk((sniffer.isChrome || sniffer.isSafari) && sniffer.isChrome === sniffer.isSafari, 'Shouldnt be both chrome and safari.'); 15 | 16 | assert.notOk((sniffer.isChrome || sniffer.isFirefox) && sniffer.isChrome === sniffer.isFirefox, 'Shouldnt be both chrome and firefox.'); 17 | 18 | assert.notOk((sniffer.isIE || sniffer.isFirefox) && sniffer.isIE === sniffer.isFirefox, 'Shouldnt be both IE and firefox.'); 19 | 20 | assert.notOk(sniffer.isIos && sniffer.isWindowsPhone, 'Shouldnt be both iOS and Windows Mobile.'); 21 | assert.notOk(sniffer.isDroid && sniffer.isWindowsPhone, 'Shouldnt be both Android and Windows Mobile.'); 22 | 23 | assert.end(); 24 | }); 25 | 26 | test('Freeze test', function(assert) { 27 | var isDesktop = sniffer.isDesktop; 28 | 29 | try { 30 | sniffer.isDesktop = Math.random(); 31 | } catch(e) {} 32 | 33 | assert.ok(isDesktop == sniffer.isDesktop, 'Properties should be immutables.'); 34 | assert.end(); 35 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var dashify = require('dashify'); 4 | 5 | module.exports = new Sniffer(); 6 | 7 | function Sniffer() { 8 | var ua = navigator.userAgent.toLowerCase(); 9 | var av = navigator.appVersion.toLowerCase(); 10 | 11 | var isWindowsPhone = /windows phone|iemobile|wpdesktop/.test(ua); 12 | 13 | var isDroidPhone = !isWindowsPhone && /android.*mobile/.test(ua); 14 | var isDroidTablet = !isWindowsPhone && !isDroidPhone && (/android/i).test(ua); 15 | var isDroid = isDroidPhone || isDroidTablet; 16 | 17 | var isIos = !isWindowsPhone && (/ip(hone|od|ad)/i).test(ua) && !window.MSStream; 18 | var isIpad = !isWindowsPhone && (/ipad/i).test(ua) && isIos; 19 | 20 | var isTablet = isDroidTablet || isIpad; 21 | var isPhone = isDroidPhone || (isIos && !isIpad) || isWindowsPhone; 22 | var isDevice = isPhone || isTablet; 23 | 24 | var isFirefox = ua.indexOf('firefox') > -1; 25 | var isSafari = !!ua.match(/version\/[\d\.]+.*safari/); 26 | var isOpera = ua.indexOf('opr') > -1; 27 | var isIE11 = !(window.ActiveXObject) && "ActiveXObject" in window; 28 | var isIE = av.indexOf('msie') > -1 || isIE11 || av.indexOf('edge') > -1; 29 | var isEdge = ua.indexOf('edge') > -1; 30 | var isChrome = window.chrome !== null && window.chrome !== undefined && navigator.vendor.toLowerCase() == 'google inc.' && !isOpera && !isEdge; 31 | 32 | this.infos = { 33 | isDroid: isDroid, 34 | isDroidPhone: isDroidPhone, 35 | isDroidTablet: isDroidTablet, 36 | isWindowsPhone: isWindowsPhone, 37 | isIos: isIos, 38 | isIpad: isIpad, 39 | isDevice: isDevice, 40 | isEdge: isEdge, 41 | isIE: isIE, 42 | isIE11: isIE11, 43 | isPhone: isPhone, 44 | isTablet: isTablet, 45 | isFirefox: isFirefox, 46 | isSafari: isSafari, 47 | isOpera: isOpera, 48 | isChrome: isChrome, 49 | isDesktop: !isPhone && !isTablet 50 | }; 51 | 52 | Object.keys(this.infos).forEach(function(info) { 53 | Object.defineProperty(this, info, { 54 | get: function () { 55 | return this.infos[info]; 56 | } 57 | }); 58 | }, this); 59 | 60 | Object.freeze(this); 61 | 62 | // TODO: add getVersion() to get IE/Safari/... version 63 | } 64 | 65 | Sniffer.prototype.addClasses = function(el) { 66 | Object.keys(this.infos).forEach(function(info) { 67 | if (this.infos[info]) addClass(el, dashify(info)); 68 | }, this); 69 | }; 70 | 71 | Sniffer.prototype.getInfos = function() { 72 | return clone(this.infos); 73 | }; 74 | 75 | function addClass(el, className) { 76 | if (el.addClass) el.addClass(className); 77 | else if (el.classList) el.classList.add(className); 78 | else el.className += ' ' + className; 79 | } 80 | 81 | function clone(source) { 82 | return JSON.parse(JSON.stringify(source)); 83 | } --------------------------------------------------------------------------------