Skip to content

Commit

Permalink
empty initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
snipebin committed Mar 14, 2018
0 parents commit ed2abec
Show file tree
Hide file tree
Showing 19 changed files with 8,780 additions and 0 deletions.
934 changes: 934 additions & 0 deletions .eslintrc

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
npm-debug.log
.DS_Store
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Google Analytics Task Manager

This guide explains what the `gaTaskManager` plugin is and how to integrate it into your `analytics.js` tracking implementation.

## Overview

Google Analytics [Tasks](https://developers.google.com/analytics/devguides/collection/analyticsjs/tasks) is "an advanced feature used to customize how analytics.js validates, constructs, and sends measurement protocol requests". `gaTaskManager` is a Google Analytics plugin which expands analytics.js Tasks, allowing the user to specify multiple functions to be run for each GA Task. Also provides utility functions to add common functions to tasks, such as setting Custom Dimensions. If you want to be able to run multiple functions for one or more Tasks included in the measurement protocol's request lifecycle, this is the plugin for you.

## Usage

To enable the `gaTaskManager` plugin, run the [`require`](https://developers.google.com/analytics/devguides/collection/analyticsjs/using-plugins) command, specify the plugin name `'gaTaskManager'`:

```js
ga('require', 'gaTaskManager');
```

When you require the `gaTaskManager` plugin, its constructor overrides the GA tracker's Tasks' function with its own, and sets the original Task function as the first function to be run for that respective Task. From an output perspective, nothing changes by requiring the plugin, it simply bootstraps the tracker so that you're able to use the methods listed below.

## Methods

The following table lists all methods for the `gaTaskManager` plugin:

<table>
<tr valign="top">
<th align="left">Name</th>
<th align="left">Description</th>
</tr>
<tr valign="top">
<td><code>addFunctionToTask</code></td>
<td>Adds a function to be executed at the specified GA Task. Can be used to add to any Task listed at https://developers.google.com/analytics/devguides/collection/analyticsjs/tasks</td>
</tr>
<tr valign="top">
<td><code>setCustomDimension</code></td>
<td>Adds a function which sets a GA Custom Dimension at the specified GA Task execution time. Defaults to execution on the <code>customTask</code> Task</td>
</tr>
<tr valign="top">
<td><code>remove</code></td>
<td>Removes the <code>gaTaskManager</code> plugin from the specified tracker, restoring all original functions set to each Task prior to the plugin being required.</td>
</tr>
</table>

For details on how `analytics.js` plugin methods work and how to invoke them, see [calling plugin methods](https://developers.google.com/analytics/devguides/collection/analyticsjs/using-plugins#calling_plugin_methods) in the `analytics.js` documentation.

## Examples

### Setting a Custom Dimension at every hit:

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

```js
var index = 1;
ga('gaTaskManager:setCustomDimension', index, 'foo');

```

### Setting Custom Dimension value just-in-time:

This example delegates the generation of the value for the Custom Dimension to a function that returns the current timestamp at every Hit:

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

```

### Executing an arbitrary function on a Task

This example adds an arbitrary function to be executed after sending the normal request to
request to www.google-analytics.com/collect.

```js
ga('gaTaskManager:addFunctionToTask', function(model) {
// Send a copy of the request to a local server
var xhr = new XMLHttpRequest();
xhr.open('POST', '/localhits', true);
xhr.send(model.get('hitPayload'));
});
```
112 changes: 112 additions & 0 deletions bin/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* Copyright 2017 Google 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.
*/


/* eslint-env node */
/* eslint require-jsdoc: "off" */


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');


module.exports = (output) => {
const entry = path.resolve(__dirname, '../lib/index.js');
const plugins = [nodeResolve()];
return new Promise((resolve, reject) => {
rollup({entry, plugins}).then((bundle) => {
try {
const rollupResult = bundle.generate({
format: 'es',
dest: output,
sourceMap: true,
});

const externsDir = path.resolve(__dirname, '../lib/externs');
const externs = glob.sync(path.join(externsDir, '*.js'))
.reduce((acc, cur) => acc + fs.readFileSync(cur, 'utf-8'), '');


const closureFlags = {
jsCode: [{
src: rollupResult.code,
path: path.basename(output),
}],
compilationLevel: 'ADVANCED',
useTypesForOptimization: true,
outputWrapper:
'(function(){%output%})();\n' +
`//# sourceMappingURL=${path.basename(output)}.map`,
assumeFunctionWrapper: true,
rewritePolyfills: false,
warningLevel: 'VERBOSE',
createSourceMap: true,
externs: [{src: externs}],
};

const closureResult = compile(closureFlags);
if (closureResult.errors.length || closureResult.warnings.length) {
const rollupMap = new SourceMapConsumer(rollupResult.map);

// Remap errors from the closure compiler output to the original
// files before rollup bundled them.
const remap = (type) => (item) => {
let {line, column, source} = rollupMap.originalPositionFor({
line: item.lineNo,
column: item.charNo,
});
source = path.relative('.', path.resolve(__dirname, '..', source));
return {type, line, column, source, desc: item.description};
};


reject({
errors: [
...closureResult.errors.map(remap('error')),
...closureResult.warnings.map(remap('warning')),
],
});
} else {
// Currently, closure compiler doesn't support applying its generated
// source map to an existing source map, so we do it manually.
const fromMap = JSON.parse(closureResult.sourceMap);
const toMap = rollupResult.map;

const generator = SourceMapGenerator.fromSourceMap(
new SourceMapConsumer(fromMap));

generator.applySourceMap(
new SourceMapConsumer(toMap), path.basename(output));

const sourceMap = generator.toString();

resolve({
code: closureResult.compiledCode,
map: sourceMap,
});
}
} catch(err) {
reject(err);
}
}).catch(reject);
});
};
36 changes: 36 additions & 0 deletions bin/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright 2017 Google 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.
*/


const chalk = require('chalk');


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


module.exports = (err) => {
if (err instanceof Error) {
log(`\n${err.stack}\n`);
} else {
log('\n');
for (let {source, line, column, desc, type} of err.errors) {
const color = chalk[type == 'error' ? 'red' : 'yellow'];

log(`${color(`[${type}]`)} ${desc}\n`);
log(chalk.gray(`${source} [${line}:${column}]\n\n`));
}
}
};
30 changes: 30 additions & 0 deletions bin/ga-task-manager
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env node
/* eslint no-console: "off" */


const chalk = require('chalk');
const fs = require('fs-extra');
const gzipSize = require('gzip-size');
const minimist = require('minimist');
const path = require('path');
const build = require('./build');
const logErrors = require('./errors');

const output = 'ga-task-manager.js';
const {cyan, gray, green, red} = chalk;

build(output)
.then(({code, map}) => {
fs.outputFileSync(output, code, 'utf-8');
fs.outputFileSync(`${output}.map`, map, 'utf-8');

const size = (gzipSize.sync(code) / 1000).toFixed(1);

console.log(green(`\nGreat success!\n`));
console.log(cyan('Built: ') +
`${output} ${gray(`(${size} Kb gzipped)`)}`);
console.log(cyan('Built: ') +
`${output}.map\n`);
})
.catch(logErrors)
.catch(console.error.bind(console));
42 changes: 42 additions & 0 deletions bin/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright 2016 Google 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.
*
* Modifications Copyright (C) 2017 Anki, Inc.
*/


import express from 'express';
import serveStatic from 'serve-static';


let server;


/**
* Starts the express log server.
* @param {Function} done A callback to invoke once the server is up.
*/
export function start(done) {
const app = express();
app.use(serveStatic('./'));
server = app.listen(8080, done);
}

/**
* Stops the log server and deletes the logs.
*/
export function stop() {
server.close();
}
4 changes: 4 additions & 0 deletions bin/usage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

Usage: ga-task-manager

Generates a minified, ga-task-manager file and source map.
38 changes: 38 additions & 0 deletions ga-task-manager.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!doctype html>
<html>
<head>
<script>
(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');

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

// Add a function to customTask
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){
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)

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

ga('send', 'pageview');
</script>
<script async src="/ga-task-manager.js"></script>
</head>
<body>
</body>
</html>
4 changes: 4 additions & 0 deletions ga-task-manager.js

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

Loading

0 comments on commit ed2abec

Please sign in to comment.