diff --git a/README.md b/README.md index 828c6c53..a839ca0d 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/docs/helpers.md b/docs/helpers.md index 4a719534..1cbd7e4e 100644 --- a/docs/helpers.md +++ b/docs/helpers.md @@ -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. diff --git a/lib/browser-auto-tracking.js b/lib/browser-auto-tracking.js index 4ada5c0d..42910cc9 100644 --- a/lib/browser-auto-tracking.js +++ b/lib/browser-auto-tracking.js @@ -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), @@ -339,4 +339,4 @@ function getSecondsSinceDate(date) { function getMiliSecondsSinceDate(date) { return new Date().getTime() - date.getTime(); -} +} \ No newline at end of file diff --git a/lib/browser.js b/lib/browser.js index ab8c2ef9..1ff7dd68 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -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'; @@ -64,6 +64,7 @@ extend(KeenCore.prototype, { // ------------------------ extend(KeenCore.helpers, { getBrowserProfile, + getBrowserProfilePromise, getDatetimeIndex, getDomainName, getDomNodePath, @@ -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; } diff --git a/lib/helpers/getBattery.js b/lib/helpers/getBattery.js new file mode 100644 index 00000000..5d8023ef --- /dev/null +++ b/lib/helpers/getBattery.js @@ -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)); +} diff --git a/lib/helpers/getBrowserProfile.js b/lib/helpers/getBrowserProfile.js index b2709a45..0602cb16 100644 --- a/lib/helpers/getBrowserProfile.js +++ b/lib/helpers/getBrowserProfile.js @@ -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; + }) +} \ No newline at end of file diff --git a/lib/helpers/getMediaDevices.js b/lib/helpers/getMediaDevices.js new file mode 100644 index 00000000..14dd1e19 --- /dev/null +++ b/lib/helpers/getMediaDevices.js @@ -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)) +} diff --git a/lib/index.js b/lib/index.js index 38008b67..b881d9d5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -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(); diff --git a/lib/record-events-browser.js b/lib/record-events-browser.js index d3a3fa90..7d2c4317 100644 --- a/lib/record-events-browser.js +++ b/lib/record-events-browser.js @@ -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 }); } @@ -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 }); } diff --git a/test/setupJest.js b/test/setupJest.js index fb37d4d1..d8e7fa01 100644 --- a/test/setupJest.js +++ b/test/setupJest.js @@ -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 = { diff --git a/test/unit/modules/helpers-spec.js b/test/unit/modules/helpers-spec.js index 931b1402..172fb75d 100644 --- a/test/unit/modules/helpers-spec.js +++ b/test/unit/modules/helpers-spec.js @@ -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'; @@ -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);