Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile device info #176

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,18 @@ const client = new KeenTracking({

---

### Vibration

If your device supports [Vibration API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vibrate) you can turn on vibration once an event is recorded
```javascript
const client = new KeenTracking({
projectId: 'PROJECT_ID',
writeKey: 'WRITE_KEY',
vibration: true
});
```
---

### Contributing

This is an open source project and we love involvement from the community! Hit us up with pull requests and issues.
Expand Down
8 changes: 8 additions & 0 deletions docs/helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ const browserProfile = KeenTracking.helpers.getBrowserProfile();
*/
```

If the browser supports new properties like [`battery`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getBattery) and [`mediaDevice`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/mediaDevices) use `getBrowserProfilePromise()` but you'll get a Promise instead of a plain object.

```javascript
Keen.helpers.getBrowserProfilePromise().then((data) => {
client.extendEvents({ browser: data });
});
```

### Scroll State

`KeenTracking.helpers.getScrollState()` returns an object of properties profiling the current scroll state. This lets you measure how much of a page a user has viewed before taking a recorded action.
Expand Down
4 changes: 2 additions & 2 deletions lib/browser-auto-tracking.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export function initAutoTrackingCore(lib) {
});

if (options.recordClicks === true) {
utils.listener('a, a *').on('click', function(e) {
utils.listener('a, a *, button').on('click', function(e) {
const el = e.target;
let event = {
element: helpers.getDomNodeProfile(el),
Expand Down Expand Up @@ -339,4 +339,4 @@ function getSecondsSinceDate(date) {

function getMiliSecondsSinceDate(date) {
return new Date().getTime() - date.getTime();
}
}
7 changes: 4 additions & 3 deletions lib/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from './defer-events';
import { extendEvent, extendEvents } from './extend-events';
import { initAutoTrackingCore } from './browser-auto-tracking';
import { getBrowserProfile } from './helpers/getBrowserProfile';
import { getBrowserProfile, getBrowserProfilePromise } from './helpers/getBrowserProfile';
import { getDatetimeIndex } from './helpers/getDatetimeIndex';
import { getDomainName } from './helpers/getDomainName';
import { getDomNodePath } from './helpers/getDomNodePath';
Expand Down Expand Up @@ -64,6 +64,7 @@ extend(KeenCore.prototype, {
// ------------------------
extend(KeenCore.helpers, {
getBrowserProfile,
getBrowserProfilePromise,
getDatetimeIndex,
getDomainName,
getDomNodePath,
Expand Down Expand Up @@ -105,8 +106,8 @@ if (localStorage && localStorage.getItem('optout')) {
KeenCore.optedOut = true;
}

if (getBrowserProfile().doNotTrack === '1'
|| getBrowserProfile().doNotTrack === 'yes') {
if (navigator.doNotTrack === '1'
|| navigator.doNotTrack === 'yes') {
KeenCore.doNotTrack = true;
}

Expand Down
15 changes: 15 additions & 0 deletions lib/helpers/getBattery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function getBattery() {
if (!('getBattery' in navigator || 'battery' in navigator)) return;

let batteryPromise;

if ('getBattery' in navigator) {
batteryPromise = navigator.getBattery();
} else {
batteryPromise = Promise.resolve(navigator.battery);
}

return batteryPromise
.then(battery => battery)
.catch(err => console.error(err));
}
47 changes: 39 additions & 8 deletions lib/helpers/getBrowserProfile.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,58 @@
import 'promise-polyfill/src/polyfill';

import { getBattery } from './getBattery';
import { getMediaDevices } from './getMediaDevices';
import { getScreenProfile } from './getScreenProfile';
import { getWindowProfile } from './getWindowProfile';

function getDocumentDescription() {
var el;
if (document && typeof document.querySelector === 'function') {
el = document.querySelector('meta[name="description"]');
}
return el ? el.content : '';
}

function getConnection() {
return navigator.connection || navigator.mozConnection ||
navigator.webkitConnection || navigator.msConnection;
}

export function getBrowserProfile() {
return {
'activeVRDisplays' : navigator.activeVRDisplays,
'connection' : getConnection(),
'cookies' : ('undefined' !== typeof navigator.cookieEnabled) ? navigator.cookieEnabled : false,
'codeName' : navigator.appCodeName,
'description': getDocumentDescription(),
'deviceMemory' : navigator.deviceMemory,
'doNotTrack' : navigator.doNotTrack,
'hardwareConcurrency' : navigator.hardwareConcurrency,
'language' : navigator.language,
'maxTouchPoints': navigator.maxTouchPoints,
'name' : navigator.appName,
'online' : navigator.onLine,
'platform' : navigator.platform,
'useragent' : navigator.userAgent,
'version' : navigator.appVersion,
'doNotTrack' : navigator.doNotTrack,
'screen' : getScreenProfile(),
'window' : getWindowProfile()
}
}

function getDocumentDescription() {
var el;
if (document && typeof document.querySelector === 'function') {
el = document.querySelector('meta[name="description"]');
}
return el ? el.content : '';
}
export function getBrowserProfilePromise() {
return Promise.all([getBattery(), getMediaDevices()])
.then(data => {
const [ battery, mediaDevices ] = data;
const browserProfile = getBrowserProfile();

if (battery) {
browserProfile.battery = battery;
}

if (mediaDevices) {
browserProfile.mediaDevices = mediaDevices;
}
return browserProfile;
})
}
7 changes: 7 additions & 0 deletions lib/helpers/getMediaDevices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function getMediaDevices() {
if (!(navigator.mediaDevices && navigator.mediaDevices.enumerateDevices)) return;

return navigator.mediaDevices.enumerateDevices()
.then(devices => devices)
.catch(err => console.error(err))
}
4 changes: 4 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ KeenCore.on('client', function(client){
this.optedOut = client.config.optOut;
}

if (client.config.vibration) {
this.vibration = client.config.vibration
}

client.queue = queue(client.config.queue);
client.queue.on('flush', function(){
client.recordDeferredEvents();
Expand Down
8 changes: 8 additions & 0 deletions lib/record-events-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export function recordEvent(eventCollectionOrConfigObject, eventBody, callback){
message: 'Keen.doNotTrack is set to true.'
})
}

if (Keen.vibration && navigator && navigator.vibrate) {
navigator.vibrate(200);
}

return send.call(this, { url, extendedEventsHash, callback, configObject, eventCollection });
}
Expand Down Expand Up @@ -157,6 +161,10 @@ export function recordEvents(eventsHash, callback){
})
}

if (Keen.vibration && navigator && navigator.vibrate) {
navigator.vibrate(200);
}

return send.call(this, { url, extendedEventsHash, callback });
}

Expand Down
6 changes: 5 additions & 1 deletion test/setupJest.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ global.IntersectionObserver.prototype.simulate = function(elements){
this.callback(elements);
};
global.navigator = {
sendBeacon: jest.mock()
sendBeacon: jest.mock(),
mediaDevices: {
enumerateDevices: jest.mock(),
},
getBattery: jest.mock(),
};
const mockStorage = {};
const localStorage = {
Expand Down
13 changes: 12 additions & 1 deletion test/unit/modules/helpers-spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getBrowserProfile } from '../../../lib/helpers/getBrowserProfile';
import { getBrowserProfile, getBrowserProfilePromise } from '../../../lib/helpers/getBrowserProfile';
import { getDatetimeIndex } from '../../../lib/helpers/getDatetimeIndex';
import { getDomainName } from '../../../lib/helpers/getDomainName';
import { getDomNodePath } from '../../../lib/helpers/getDomNodePath';
Expand Down Expand Up @@ -78,6 +78,17 @@ describe('Keen.helpers', () => {
});
});

describe('#getBrowserProfilePromise', () => {
it('should return a promise', () => {
expect(getBrowserProfilePromise()).toBeInstanceOf(Promise);
});
it('it should return an object from promise when resolved', () => {
getBrowserProfilePromise().then((data) => {
expect(data).toBeInstanceOf(Object);
});
});
});

describe('#getScreenProfile', () => {
it('should return an object of screen properties', () => {
expect(getScreenProfile()).toBeInstanceOf(Object);
Expand Down