Skip to content

Commit

Permalink
Merge pull request #1 from anki/feature/WEB-1471-ga-visitor-data
Browse files Browse the repository at this point in the history
Initial Review
  • Loading branch information
snipebin authored Sep 27, 2018
2 parents ed2abec + 14c0e4d commit f2d41f9
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 47 deletions.
13 changes: 13 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright 2018 Anki, Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ For details on how `analytics.js` plugin methods work and how to invoke them, se

### Setting a Custom Dimension at every hit:

This example sets a Custom Dimension `dimension2` at every Hit:
This example sets a Custom Dimension `dimension2` at every Hit:

```js
var index = 1;
Expand All @@ -59,8 +59,8 @@ This example delegates the generation of the value for the Custom Dimension to a

```js
var index = 2;
ga('gaTaskManager:setCustomDimension', index, function(){
return Date.now() / 1000 | 0;
ga('gaTaskManager:setCustomDimension', index, function(){
return Date.now() / 1000 | 0;
});

```
Expand All @@ -71,7 +71,7 @@ This example adds an arbitrary function to be executed after sending the normal
request to www.google-analytics.com/collect.

```js
ga('gaTaskManager:addFunctionToTask', function(model) {
ga('gaTaskManager:addFunctionToTask', 'sendHitTask', 'sendHitToMyServer', function(model) {
// Send a copy of the request to a local server
var xhr = new XMLHttpRequest();
xhr.open('POST', '/localhits', true);
Expand Down
4 changes: 2 additions & 2 deletions bin/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Modifications Copyright (C) 2018 Anki, Inc.
*/


Expand All @@ -23,7 +25,6 @@ const fs = require('fs-extra');
const glob = require('glob');
const {compile}= require('google-closure-compiler-js');
const {rollup} = require('rollup');
const memory = require('rollup-plugin-memory');
const nodeResolve = require('rollup-plugin-node-resolve');
const path = require('path');
const {SourceMapGenerator, SourceMapConsumer} = require('source-map');
Expand All @@ -45,7 +46,6 @@ module.exports = (output) => {
const externs = glob.sync(path.join(externsDir, '*.js'))
.reduce((acc, cur) => acc + fs.readFileSync(cur, 'utf-8'), '');


const closureFlags = {
jsCode: [{
src: rollupResult.code,
Expand Down
5 changes: 3 additions & 2 deletions bin/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Modifications Copyright (C) 2018 Anki, Inc.
*
*/


const chalk = require('chalk');


const log = (msg) => process.stderr.write(msg);


Expand Down
39 changes: 26 additions & 13 deletions ga-task-manager.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,47 @@
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

// Anki Test Property
ga('create', 'UA-40941061-3', 'auto');

})(window,document,'script','https://www.google-analytics.com/analytics_debug.js','ga');

window.ga_debug = {trace: true};

ga('create', 'GA-PROPERTY-ID', 'auto');

// Require the plugin
ga('require', 'gaTaskManager');

// Add a function to customTask
ga('gaTaskManager:addFunctionToTask', 'customTask', 'logShinyString', function(model){
ga('GaTaskManager:addFunctionToTask', 'customTask', 'logShinyString', function(model){
console.log("Look at my shiny new random customTask");
});

// Add a second function to customTask
ga('gaTaskManager:addFunctionToTask', 'customTask', 'logShinyStringAgain', function(model){
ga('GaTaskManager:addFunctionToTask', 'customTask', 'logShinyStringAgain', function(model){
console.log("Look at my shiny second random customTask which will be executed after the first!");
});

// Set Dimensions with a static string value
ga('gaTaskManager:setCustomDimension', 1, 14)
ga('GaTaskManager:setCustomDimension', 1, 14)

// Set Dimensions with function to return the value at time of execution
ga('gaTaskManager:setCustomDimension', 2, function(){return Date.now() / 1000 | 0})
ga('GaTaskManager:setCustomDimension', 2, function(){return Date.now() / 1000 | 0})

ga('send', 'pageview');

// Remove logShinyStringAgain function
ga('GaTaskManager:removeFunctionFromTask', 'customTask', 'logShinyStringAgain');

// Unset the function setting the dimension at every hit which we added earlier
ga('GaTaskManager:unsetCustomDimension', 1);

// Trigger a second hit after 5 seconds
setTimeout(function () {
ga('send', 'pageview');
}, 5000);

</script>
<script async src="/ga-task-manager.js"></script>
</head>
<body>
</body>
</html>
</html>
7 changes: 4 additions & 3 deletions ga-task-manager.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ga-task-manager.js.map

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion lib/externs/ga-task-manager.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
/**
* @interface
*/
class GATaskManagerPublicInterface {
class GaTaskManagerPublicInterface {
/**
* @param {string} gaTaskName
* @param {string} userTaskName
* @param {function(!Model)} taskFunction
*/
addFunctionToTask(gaTaskName, userTaskName, taskFunction) {}
/**
* @param {string} gaTaskName
* @param {string} userTaskName
*/
removeFunctionFromTask(gaTaskName, userTaskName) {}
/**
* @param {string} index
* @param {string|number|function(): string|number} value
* @param {string} gaTaskName
*/
setCustomDimension(index, value, gaTaskName) {}
/**
* @param {string} index
* @param {string} gaTaskName
*/
unsetCustomDimension(index, gaTaskName) {}
remove() {}
}
80 changes: 62 additions & 18 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,125 @@ import provide from './provide';

/**
* Class for the `ga-task-manager` analytics.js plugin.
* @implements {GATaskManagerPublicInterface}
* @implements {GaTaskManagerPublicInterface}
*/
class gaTaskManager {
class GaTaskManager {
/**
* Registers declarative event tracking.
* @param {!Tracker} tracker Passed internally by analytics.js
*/
constructor(tracker) {
this.tracker = tracker;
this.registry = {};
this.gaTaskNames = ['customTask', 'sendHitTask'];
// @see https://developers.google.com/analytics/devguides/collection/analyticsjs/tasks
this.gaTaskNames = [
'customTask',
'previewTask',
'checkProtocolTask',
'validationTask',
'checkStorageTask',
'historyImportTask',
'samplerTask',
'buildHitTask',
'sendHitTask',
'timingTask',
'displayFeaturesTask'
];

// Bind methods.
this.addFunctionToTask = this.addFunctionToTask.bind(this);
this.removeFunctionFromTask = this.removeFunctionFromTask.bind(this);
this.setCustomDimension = this.setCustomDimension.bind(this);

this.unsetCustomDimension = this.unsetCustomDimension.bind(this);

this.gaTaskNames.forEach((gaTaskName) => {
this.registry[gaTaskName] = [];
this.registry[gaTaskName] = {};
// Grab a reference to the default function for this task and register it as the first task.
this.addFunctionToTask(gaTaskName, 'original', tracker.get(gaTaskName));

// Override the GA Tracker's task with our task manager executor.
// Override the GA Tracker's task with our task manager executor which calls all user tasks added to the registry.
this.tracker.set(gaTaskName, (model) => {
this.registry[gaTaskName].forEach((userTask) => {
userTask.func(model);
});
for (var userTask in this.registry[gaTaskName]) {
if (this.registry[gaTaskName].hasOwnProperty(userTask)) {
this.registry[gaTaskName][userTask](model);
}
}
});
});
}

/**
* Adds a function to be executed at the specified GA Task.
* Adds a function to be executed at the specified GA Task.
* @param {string} gaTaskName The name of the GA Task to add the taskFunction to.
* @param {string} userTaskName An arbitrary name for the task performed by the taskFunction.
* @param {function(!Model)} taskFunction The function to be added to the execution stack of the GA Task specified in gaTaskName.
* The GA Model Object (https://developers.google.com/analytics/devguides/collection/analyticsjs/model-object-reference) will be passed to it at time of execution.
*/
addFunctionToTask(gaTaskName, userTaskName, taskFunction) {
this.registry[gaTaskName].push({name: userTaskName, func: taskFunction});
if (this.registry[gaTaskName]) {
this.registry[gaTaskName][userTaskName] = taskFunction;
}
}

/**
* Adds a function which sets a GA Custom Dimension at the specified GA Task execution time. Defaults to execution on the customTask Task.
* Removes a function from the specified GA Task by the name given by the user.
* @param {string} gaTaskName The name of the GA Task to remove the task from.
* @param {string} userTaskName The arbitrary name for the task given to addFunctionToTask.
*/
removeFunctionFromTask(gaTaskName, userTaskName) {
if (this.registry[gaTaskName] && this.registry[gaTaskName][userTaskName]) {
delete this.registry[gaTaskName][userTaskName];
}
}

/**
* Adds a function which sets a GA Custom Dimension at the specified GA Task execution time. Defaults to execution on the customTask Task.
* @param {string} index The index of your dimension as defined in your Google Analytics property settings.
* @param {string|number|function(): string|number} value Arbitrary string, number or function to set value for the dimension.
* @param {string|number|function(): string|number} value Arbitrary string, number or function to set value for the dimension.
* If a function is given, it will be executed every time at the moment of task execution.
* If a number is given, it will be cast automatically to a string by calling Number.toString() method.
* Observe size limit for the string value https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs
* @param {string} gaTaskName The name of the GA Task to add the taskFunction to.
* @param {string} gaTaskName The name of the GA Task on which to set the Custom Dimension. Defaults to 'customTask'.
*/
setCustomDimension(index, value, gaTaskName = 'customTask') {
const userTaskName = 'customDimension' + index;
this.addFunctionToTask(gaTaskName, userTaskName, function(model){
let auxValue = value;
// If user provided a function to set value at execution time, then type check itsreturn value.
if (typeof value == 'function') {
auxValue = value();
const auxType = typeof auxValue;
if (auxType != 'string' && auxType != 'number') {
throw new Error('Function ' + value.name + ' must return a string or number. Got ' + auxType + 'instead.');
}
}

if (typeof auxValue === 'number') auxValue = auxValue.toString();

// Set the dimension value to be sent to GA.
model.set('dimension' + index, auxValue);
});
}

/**
* Removes a function added with setCustomDimension.
* @param {string} index The index of your dimension as defined in your Google Analytics property settings.
* @param {string} gaTaskName The name of the GA Task to add the taskFunction to.
*/
unsetCustomDimension(index, gaTaskName = 'customTask') {
const userTaskName = 'customDimension' + index;
this.removeFunctionFromTask(gaTaskName, userTaskName);
}

/**
* Resets all Tasks of the Tracker to the original function registered at the moment the plugin was required.
*/
remove() {
this.gaTaskNames.forEach((gaTaskName) => {
const originalTaskFunction = this.registry[gaTaskName].find((element) => {
return element.name == 'original';
});
const originalTaskFunction = this.registry[gaTaskName]['original'];
this.tracker.set(gaTaskName, originalTaskFunction);
});
}
}

provide('gaTaskManager', gaTaskManager);
provide('gaTaskManager', GaTaskManager);
2 changes: 2 additions & 0 deletions lib/provide.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Modifications Copyright (C) 2017 Anki, Inc
*/


Expand Down
4 changes: 2 additions & 2 deletions lib/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Modifications Copyright (C) 2017 Anki, Inc.
*
* Modifications Copyright (C) 2018 Anki, Inc.
*/

/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
],
"author": {
"name": "Vinnie Frietas",
"email": "[email protected]",
"email": "[email protected]"
},
"homepage": "https://github.com/anki/ga-task-manager#readme",
"dependencies": {
Expand Down

0 comments on commit f2d41f9

Please sign in to comment.