"
7 | ],
8 | "description": "Angular module to detect OS / Browser / Device",
9 | "main": "ng-device-detector.js",
10 | "keywords": [
11 | "angularjs",
12 | "user-agent",
13 | "browser",
14 | "os",
15 | "device",
16 | "detection"
17 | ],
18 | "license": "MIT",
19 | "ignore": [
20 | "**/.*",
21 | "node_modules",
22 | "bower_components",
23 | "test",
24 | "tests"
25 | ],
26 | "dependencies": {
27 | "re-tree": "^0.0.2",
28 | "ua-device-detector": "^1.0.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var uglify = require('gulp-uglify');
3 | var concat = require('gulp-concat');
4 | var git = require('gulp-git');
5 | var bump = require('gulp-bump');
6 | var filter = require('gulp-filter');
7 | var tag_version = require('gulp-tag-version');
8 | var Server = require('karma').Server;
9 |
10 | gulp.task('default', ["test"]);
11 |
12 | gulp.task('minify', function () {
13 | gulp.src('ng-device-detector.js')
14 | .pipe(uglify())
15 | .pipe(concat("ng-device-detector.min.js"))
16 | .pipe(gulp.dest('.'))
17 | });
18 |
19 | /**
20 | * Run test once and exit
21 | */
22 | gulp.task('test', function (done) {
23 | new Server({
24 | configFile: __dirname + '/karma.conf.js',
25 | singleRun: true
26 | }, done).start();
27 | });
28 |
29 | gulp.task('watch', [], function () {
30 | gulp.watch(["**/*.js"], ["test"]);
31 | });
32 |
33 | gulp.task('version', ["minify"], function () {
34 | gulp.src(['./package.json', './bower.json'])
35 | .pipe(bump())
36 | .pipe(gulp.dest('./'))
37 | .pipe(git.commit('bumps package version'))
38 | .pipe(filter('package.json'))
39 | .pipe(tag_version());
40 | });
41 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | require('re-tree');
2 | require('ua-device-detector');
3 | require('./ng-device-detector');
4 | module.exports = 'ng.deviceDetector';
5 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | config.set({
3 | // base path, that will be used to resolve files and exclude
4 | basePath: '',
5 |
6 | // testing framework to use (jasmine/mocha/qunit/...)
7 | frameworks: ['jasmine'],
8 |
9 | // list of files / patterns to load in the browser
10 | files: [
11 | 'node_modules/angular/angular.js',
12 | 'node_modules/angular-mocks/angular-mocks.js',
13 | 'node_modules/re-tree/re-tree.js',
14 | 'node_modules/ua-device-detector/ua-device-detector.js',
15 | 'ng-device-detector.js',
16 | 'test/*.js'
17 | ],
18 |
19 | // list of files / patterns to exclude
20 | exclude: [],
21 |
22 | // web server port
23 | port: 8888,
24 |
25 | // level of logging
26 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
27 | logLevel: config.LOG_INFO,
28 |
29 | // enable / disable watching file and executing tests whenever any file changes
30 | autoWatch: true,
31 |
32 | // Start these browsers, currently available:
33 | // - Chrome
34 | // - ChromeCanary
35 | // - Firefox
36 | // - Opera
37 | // - Safari (only Mac)
38 | // - PhantomJS
39 | // - IE (only Windows)
40 | browsers: ['PhantomJS'],
41 |
42 |
43 | // Continuous Integration mode
44 | // if true, it capture browsers, run tests and exit
45 | singleRun: false,
46 | plugins: [
47 | 'karma-jasmine',
48 | 'karma-nested-reporter',
49 | 'karma-phantomjs-launcher'
50 | ],
51 | reporters: ['nested'],
52 | nestedReporter: {
53 | color: {
54 | should: 'red',
55 | browser: 'yellow'
56 | },
57 | icon: {
58 | failure: '✘ ',
59 | indent: 'ட ',
60 | browser: ''
61 | }
62 | }
63 | });
64 | };
65 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) {{{year}}} {{{fullname}}}
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ng-device-detector.js:
--------------------------------------------------------------------------------
1 | (function (angular) {
2 | "use strict";
3 |
4 | angular.module("ng.deviceDetector", ["reTree", "uaDeviceDetector"])
5 | .service("detectUtils", ["deviceDetector", "uaDeviceDetector",
6 | function (deviceDetector, uaDeviceDetector) {
7 | var deviceInfo = deviceDetector;
8 |
9 | this.isMobile = function () {
10 | return deviceInfo.device !== 'unknown';
11 | };
12 |
13 | this.isAndroid = function () {
14 | return (deviceInfo.device === uaDeviceDetector.DEVICES.ANDROID || deviceInfo.OS === uaDeviceDetector.OS.ANDROID);
15 | };
16 |
17 | this.isIOS = function () {
18 | return (deviceInfo.os === uaDeviceDetector.OS.IOS || deviceInfo.device === uaDeviceDetector.DEVICES.I_POD ||
19 | deviceInfo.device === uaDeviceDetector.DEVICES.IPHONE);
20 | };
21 | }
22 | ])
23 | .provider("deviceDetector", function () {
24 | var customDetectors = [];
25 | this.addCustom = function (customDetectorName, customDetectorRE) {
26 | customDetectors.push({ name: customDetectorName, re: customDetectorRE });
27 | };
28 | this.$get = [
29 | "$window",
30 | "uaDeviceDetector",
31 | "reTree",
32 | function (
33 | $window,
34 | uaDeviceDetector,
35 | reTree
36 | ) {
37 | var ua = $window.navigator.userAgent;
38 | var platform = $window.navigator.platform;
39 | var maxTouchPoints= $window.navigator.maxTouchPoints;
40 | var deviceInfo = uaDeviceDetector.parseUserAgent(ua, customDetectors, platform, maxTouchPoints );
41 | deviceInfo.parseUserAgent = function (ua, platform, maxTouchPoints) { return uaDeviceDetector.parseUserAgent(ua, customDetectors, platform, maxTouchPoints) };
42 | return deviceInfo;
43 | }];
44 | }
45 | )
46 | .directive('deviceDetector', ["deviceDetector", function (deviceDetector) {
47 | function customClassName(name) {
48 | return 'is-' + name.toLowerCase().replace(/[^0-9a-z]+/g, '-');
49 | }
50 |
51 | return {
52 | restrict: "A",
53 | link: function (scope, elm/*, attrs*/) {
54 | elm.addClass('os-' + deviceDetector.os);
55 | elm.addClass('browser-' + deviceDetector.browser);
56 | elm.addClass('device-' + deviceDetector.device);
57 | elm.toggleClass('is-mobile', deviceDetector.isMobile());
58 | elm.toggleClass('is-tablet', deviceDetector.isTablet());
59 | elm.toggleClass('is-desktop', deviceDetector.isDesktop());
60 | Object.keys(deviceDetector.custom).forEach(function (customKey) {
61 | elm.toggleClass(customClassName(customKey), deviceDetector.custom[customKey]);
62 | });
63 | }
64 | };
65 | }]);
66 | })(angular);
67 |
--------------------------------------------------------------------------------
/ng-device-detector.min.js:
--------------------------------------------------------------------------------
1 | !function(e){"use strict";e.module("ng.deviceDetector",["reTree","uaDeviceDetector"]).service("detectUtils",["deviceDetector","uaDeviceDetector",function(e,t){var i=e;this.isMobile=function(){return"unknown"!==i.device},this.isAndroid=function(){return i.device===t.DEVICES.ANDROID||i.OS===t.OS.ANDROID},this.isIOS=function(){return i.os===t.OS.IOS||i.device===t.DEVICES.I_POD||i.device===t.DEVICES.IPHONE}}]).provider("deviceDetector",function(){var e=[];this.addCustom=function(t,i){e.push({name:t,re:i})},this.$get=["$window","uaDeviceDetector","reTree",function(e,t,i){var s=e.navigator.userAgent,r=t.parseUserAgent(s);return r.parseUserAgent=t.parseUserAgent,r}]}).directive("deviceDetector",["deviceDetector",function(e){function t(e){return"is-"+e.toLowerCase().replace(/[^0-9a-z]+/g,"-")}return{restrict:"A",link:function(i,s){s.addClass("os-"+e.os),s.addClass("browser-"+e.browser),s.addClass("device-"+e.device),s.toggleClass("is-mobile",e.isMobile()),s.toggleClass("is-tablet",e.isTablet()),s.toggleClass("is-desktop",e.isDesktop()),Object.keys(e.custom).forEach(function(i){s.toggleClass(t(i),e.custom[i])})}}}])}(angular);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-device-detector",
3 | "version": "5.1.4",
4 | "devDependencies": {
5 | "angular": "^1.6.7",
6 | "angular-mocks": "^1.6.7",
7 | "gulp": "~3.9.1",
8 | "gulp-bump": "~2.7.0",
9 | "gulp-concat": "~2.6.1",
10 | "gulp-filter": "^5.0.1",
11 | "gulp-git": "^2.0.1",
12 | "gulp-karma": "0.0.5",
13 | "gulp-tag-version": "~1.3.0",
14 | "gulp-uglify": "~2.0.1",
15 | "jasmine-core": "~2.5.2",
16 | "karma": "~1.5.0",
17 | "karma-jasmine": "^1.1.1",
18 | "karma-nested-reporter": "^0.1.6",
19 | "karma-phantomjs-launcher": "^1.0.4",
20 | "phantomjs": "~2.1.7",
21 | "standard-version": "^8.0.1",
22 | "uglify-js": "^2.8.29"
23 | },
24 | "scripts": {
25 | "test": "./node_modules/karma/bin/karma start --single-run --browsers PhantomJS",
26 | "release": "node_modules/standard-version/bin/cli.js",
27 | "publish-version": "(git push --follow-tags origin master) && (npm publish)"
28 | },
29 | "description": "Uses user-agent to set css classes or directly usable via JS.",
30 | "main": "index.js",
31 | "repository": {
32 | "type": "git",
33 | "url": "https://github.com/srfrnk/ng-device-detector.git"
34 | },
35 | "keywords": [
36 | "angularjs"
37 | ],
38 | "author": "srfrnk",
39 | "license": "MIT",
40 | "bugs": {
41 | "url": "https://github.com/srfrnk/ng-device-detector/issues"
42 | },
43 | "homepage": "https://github.com/srfrnk/ng-device-detector",
44 | "dependencies": {
45 | "re-tree": "^0.1.7",
46 | "ua-device-detector": "^1.1.1"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # ng-device-detector
2 | ##### Angular module to detect OS / Browser / Device
3 |
4 | [](https://travis-ci.org/srfrnk/ng-device-detector)
5 | [](https://github.com/srfrnk/ng-device-detector/issues)
6 | [](https://snyk.io/package/npm/ng-device-detector)
7 | [](https://www.codetriage.com/srfrnk/ng-device-detector)
8 |
9 | [](https://github.com/srfrnk/ng-device-detector/blob/master/license.txt)
10 | [](https://www.npmjs.com/package/ng-device-detector)
11 | [](https://www.npmjs.com/package/ng-device-detector)
12 | [](https://www.npmjs.com/package/ng-device-detector)
13 |
14 | [](http://commitizen.github.io/cz-cli/)
15 | [](http://semver.org/spec/v2.0.0.html)
16 |
17 | Uses user-agent to set CSS classes or directly usable via JS.
18 | See website: [http://srfrnk.github.io/ng-device-detector](http://srfrnk.github.io/ng-device-detector)
19 |
20 | ### Install
21 | NPM
22 | ```sh
23 | $ npm install ng-device-detector --save
24 | ```
25 | Bower
26 | ```sh
27 | $ bower install ng-device-detector --save
28 | ```
29 | Browser (Add scripts in HTML)
30 | ```sh
31 |
32 |
33 |
34 | ```
35 | Adding `'ng.deviceDetector'` to your app module dependencies
36 | ```js
37 | angular.module('app', ['ng.deviceDetector']);
38 | ```
39 | Injecting *DeviceDetector* service in controller
40 | ```js
41 | angular.module('app').controller('Home', function($scope, deviceDetector){
42 | // Awesome stuff
43 | });
44 | ```
45 |
46 | To add classes, add directive like: ``
47 |
48 | ### Setup
49 |
50 | You can set custom detectors at the provider object.
51 | The
52 | ```javascript
53 | angular.module('app', ["ng.deviceDetector"])
54 | .config(['deviceDetectorProvider', function(deviceDetectorProvider) {
55 | deviceDetectorProvider.addCustom("Custom_UA_Entry", {
56 | and:["\\bCustom_UA_Entry\\b", {
57 | not:"\\bChrome\\b"
58 | }]
59 | });
60 | }])
61 |
62 | .controller('Home', function($scope, deviceDetector) {
63 | // (true / false)
64 | $scope.customUAEntry = deviceDetector.custom["Custom_UA_Entry"];
65 | });
66 | ```
67 |
68 | > Custom detectors will also be added as CSS classes with 'is-' prefix and encoded into css class name casing.
69 |
70 | ### deviceDetector service
71 | Holds the following properties:
72 | * raw : object : contains the raw values... for internal use mostly.
73 | * os : string : name of current OS
74 | * browser : string : name of current browser
75 | * device : string : name of current device
76 |
77 | ### Support
78 | At first I added just major browser, OS, device support.
79 | With help from mariendries, javierprovecho and crisandretta more support was added.
80 | [The current list of supported browser, OS, device can be easily viewed in here](https://github.com/srfrnk/ng-device-detector/blob/master/ng-device-detector.js).
81 |
82 | Pull-requests with new stuff will be highly appreciated :)
83 |
84 | ### Example
85 |
86 | See [plunker](http://plnkr.co/edit/urqMI1?p=preview)
87 |
88 | ### License
89 |
90 | [MIT License](//github.com/srfrnk/ng-device-detector/blob/master/license.txt)
91 |
--------------------------------------------------------------------------------
/test/detectUtilsService.js:
--------------------------------------------------------------------------------
1 | describe("detectUtils", function () {
2 | var deviceDetector,
3 | osConstant,
4 | browserConstant,
5 | deviceConstant,
6 | util;
7 |
8 | function loadDetector(userAgent) {
9 | module("ng.deviceDetector");
10 | inject(["$window", function ($window) {
11 | var __originalNavigator = $window.navigator;
12 | $window.navigator = {};
13 | if ($window.navigator !== __originalNavigator) {
14 | $window.navigator.__proto__ = __originalNavigator;
15 | }
16 | $window.navigator.__defineGetter__('userAgent', function () {
17 | return userAgent;
18 | });
19 | }]);
20 | inject(["deviceDetector", "detectUtils", "DEVICES", "BROWSERS", "OS", function (deviceDetectorI, detectUtils, DEVICES, BROWSERS, OS) {
21 | deviceDetector = deviceDetectorI;
22 | osConstant = OS;
23 | browserConstant = BROWSERS;
24 | deviceConstant = DEVICES;
25 | util = detectUtils;
26 | }]);
27 | }
28 |
29 | describe("with ios user-agent", function () {
30 | function describeUserAgent(userAgent, os, browser, device) {
31 | describe(userAgent, function () {
32 | beforeEach(function () {
33 | loadDetector(userAgent);
34 | });
35 |
36 | it("should return true for iOS", function () {
37 | expect(util.isIOS()).toBeTruthy();
38 | });
39 |
40 | it("should return true for isMobile ", function () {
41 | expect(util.isMobile()).toBeTruthy();
42 | });
43 |
44 | it("should return false for isAndroid ", function () {
45 | expect(util.isAndroid()).toBeFalsy();
46 | });
47 | });
48 | }
49 |
50 | // Safari
51 | describeUserAgent("Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25",
52 | "ios", "safari", "ipad");
53 | });
54 |
55 | describe("with andriod user-agent", function () {
56 | function describeUserAgent(userAgent, os, browser, device) {
57 | describe(userAgent, function () {
58 | beforeEach(function () {
59 | loadDetector(userAgent);
60 | });
61 |
62 | it("should return false for iOS", function () {
63 | expect(util.isIOS()).toBeFalsy();
64 | });
65 |
66 | it("should return true for isMobile ", function () {
67 | expect(util.isMobile()).toBeTruthy();
68 | });
69 |
70 | it("should return true for isAndroid ", function () {
71 | expect(util.isAndroid()).toBeTruthy();
72 | });
73 | });
74 | }
75 |
76 | // Android
77 | describeUserAgent("Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
78 | "android", "chrome", "android");
79 | });
80 |
81 | describe("with desktop chrome user-agent", function () {
82 | function describeUserAgent(userAgent, os, browser, device) {
83 | describe(userAgent, function () {
84 | beforeEach(function () {
85 | loadDetector(userAgent);
86 | });
87 |
88 | it("should return false for iOS", function () {
89 | expect(util.isIOS()).toBeFalsy();
90 | });
91 |
92 | it("should return false for isMobile ", function () {
93 | expect(util.isMobile()).toBeFalsy();
94 | });
95 |
96 | it("should return false for isAndroid ", function () {
97 | expect(util.isAndroid()).toBeFalsy();
98 | });
99 | });
100 | }
101 |
102 | // Chrome
103 | describeUserAgent("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36",
104 | "windows", "chrome", "unknown");
105 | });
106 |
107 | describe("with windows phone user-agent", function () {
108 | function describeUserAgent(userAgent, os, browser, device) {
109 | describe(userAgent, function () {
110 | beforeEach(function () {
111 | loadDetector(userAgent);
112 | });
113 |
114 | it("should return false for iOS", function () {
115 | expect(util.isIOS()).toBeFalsy();
116 | });
117 |
118 | it("should return true for isMobile ", function () {
119 | expect(util.isMobile()).toBeTruthy();
120 | });
121 |
122 | it("should return false for isAndroid ", function () {
123 | expect(util.isAndroid()).toBeFalsy();
124 | });
125 | });
126 | }
127 |
128 | // Windows phone
129 | describeUserAgent("Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 930) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537",
130 | "windows-phone", "ie", "windows-phone");
131 | });
132 | });
133 |
--------------------------------------------------------------------------------
/test/unit.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("ng-device-detector", function () {
4 | var deviceDetector;
5 |
6 | function loadDetector(userAgent, setup) {
7 | module("ng.deviceDetector", function (deviceDetectorProvider) {
8 | if (!!setup) {
9 | setup.apply(null, [deviceDetectorProvider]);
10 | }
11 | });
12 | inject(["$window", function ($window) {
13 | var __originalNavigator = $window.navigator;
14 | $window.navigator = {};
15 | if ($window.navigator !== __originalNavigator) {
16 | $window.navigator.__proto__ = __originalNavigator;
17 | }
18 | $window.navigator.__defineGetter__('userAgent', function () {
19 | return userAgent;
20 | });
21 | }]);
22 | inject(["deviceDetector", function (deviceDetectorI) {
23 | deviceDetector = deviceDetectorI;
24 | }]);
25 | }
26 |
27 | it("should load", function () {
28 | loadDetector("");
29 | expect(deviceDetector).not.toBeNull();
30 | });
31 |
32 | describe("with empty user-agent", function () {
33 | beforeEach(function () {
34 | loadDetector("");
35 | });
36 |
37 | it("should read empty", function () {
38 | expect(deviceDetector.raw.userAgent).toBe("");
39 | });
40 | });
41 |
42 | describe("with dummy user-agent", function () {
43 | beforeEach(function () {
44 | loadDetector("dummy");
45 | });
46 |
47 | it("should read dummy", function () {
48 | expect(deviceDetector.raw.userAgent).toBe("dummy");
49 | });
50 | });
51 |
52 | // Issue 72
53 | describe("Test custom UA string parsing", function () {
54 | it("Should parse the specified UA", function () {
55 | loadDetector("Custom", function(deviceDetectorProvider) {
56 | deviceDetectorProvider.addCustom('MY_CUSTOM', {
57 | and: ['\\bCustom\\b']
58 | });
59 | });
60 | var deviceInfo = deviceDetector.parseUserAgent("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 Custom");
61 | expect(deviceDetector.browser).toBe("unknown");
62 | expect(deviceDetector.os).toBe("unknown");
63 | expect(deviceDetector.device).toBe("unknown");
64 | expect(deviceInfo.os).toBe("windows");
65 | expect(deviceInfo.os_version).toBe("windows-8-1");
66 | expect(deviceInfo.browser).toBe("chrome");
67 | expect(deviceInfo.browser_version).toBe("37.0.2049.0");
68 | expect(deviceInfo.device).toBe("unknown");
69 | expect(deviceInfo.isMobile()).toBeFalsy();
70 | expect(deviceInfo.isTablet()).toBeFalsy();
71 | expect(deviceInfo.isDesktop()).toBeTruthy();
72 | expect(deviceDetector.custom.MY_CUSTOM).toBeTruthy();
73 | });
74 | });
75 | });
76 |
--------------------------------------------------------------------------------