diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b9bce91 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md index 8d08434..24191eb 100644 --- a/README.md +++ b/README.md @@ -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; @@ -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; }); ``` @@ -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); diff --git a/bin/build.js b/bin/build.js index fba6ebd..f07fe6d 100644 --- a/bin/build.js +++ b/bin/build.js @@ -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. */ @@ -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'); @@ -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, diff --git a/bin/errors.js b/bin/errors.js index d60ce63..3e3a40c 100644 --- a/bin/errors.js +++ b/bin/errors.js @@ -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); diff --git a/ga-task-manager.html b/ga-task-manager.html index b871397..d703644 100644 --- a/ga-task-manager.html +++ b/ga-task-manager.html @@ -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); + - \ No newline at end of file + diff --git a/ga-task-manager.js b/ga-task-manager.js index 4ef599c..c829b78 100644 --- a/ga-task-manager.js +++ b/ga-task-manager.js @@ -1,4 +1,5 @@ -(function(){function e(b){var c=this;this.c=b;this.a={};this.b=["customTask","sendHitTask"];this.addFunctionToTask=this.addFunctionToTask.bind(this);this.setCustomDimension=this.setCustomDimension.bind(this);this.b.forEach(function(a){c.a[a]=[];c.addFunctionToTask(a,"original",b.get(a));c.c.set(a,function(b){c.a[a].forEach(function(a){a.f(b)})})})}e.prototype.addFunctionToTask=function(b,c,a){this.a[b].push({name:c,f:a})}; -e.prototype.setCustomDimension=function(b,c,a){this.addFunctionToTask(void 0===a?"customTask":a,"customDimension"+b,function(a){var f=c;if("function"==typeof c){var f=c(),d=typeof f;if("string"!=d&&"number"!=d)throw Error("Function "+c.name+" must return a string or number. Got "+d+"instead.");}a.set("dimension"+b,f)})};e.prototype.remove=function(){var b=this;this.b.forEach(function(c){var a=b.a[c].find(function(a){return"original"==a.name});b.c.set(c,a)})}; -(function(b,c){var a=window.GoogleAnalyticsObject||"ga";window[a]=window[a]||function(c){for(var b=[],d=0;d {\n this.registry[gaTaskName] = [];\n // Grab a reference to the default function for this task and register it as the first task.\n this.addFunctionToTask(gaTaskName, 'original', tracker.get(gaTaskName));\n\n // Override the GA Tracker's task with our task manager executor.\n this.tracker.set(gaTaskName, (model) => {\n this.registry[gaTaskName].forEach((userTask) => {\n userTask.func(model);\n });\n });\n });\n }\n\n /**\n * Adds a function to be executed at the specified GA Task. \n * @param {string} gaTaskName The name of the GA Task to add the taskFunction to.\n * @param {string} userTaskName An arbitrary name for the task performed by the taskFunction.\n * @param {function(!Model)} taskFunction The function to be added to the execution stack of the GA Task specified in gaTaskName.\n * The GA Model Object (https://developers.google.com/analytics/devguides/collection/analyticsjs/model-object-reference) will be passed to it at time of execution.\n */\n addFunctionToTask(gaTaskName, userTaskName, taskFunction) {\n this.registry[gaTaskName].push({name: userTaskName, func: taskFunction});\n }\n\n /**\n * Adds a function which sets a GA Custom Dimension at the specified GA Task execution time. Defaults to execution on the customTask Task. \n * @param {string} index The index of your dimension as defined in your Google Analytics property settings.\n * @param {string|number|function(): string|number} value Arbitrary string, number or function to set value for the dimension. \n * If a function is given, it will be executed every time at the moment of task execution.\n * Observe size limit for the string value https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs\n * @param {string} gaTaskName The name of the GA Task to add the taskFunction to. \n */\n setCustomDimension(index, value, gaTaskName = 'customTask') {\n const userTaskName = 'customDimension' + index;\n this.addFunctionToTask(gaTaskName, userTaskName, function(model){\n let auxValue = value;\n if (typeof value == 'function') {\n auxValue = value();\n const auxType = typeof auxValue;\n if (auxType != 'string' && auxType != 'number') {\n throw new Error('Function ' + value.name + ' must return a string or number. Got ' + auxType + 'instead.');\n }\n }\n model.set('dimension' + index, auxValue);\n });\n }\n\n /**\n * Resets all Tasks of the Tracker to the original function registered at the moment the plugin was required.\n */\n remove() {\n this.gaTaskNames.forEach((gaTaskName) => {\n const originalTaskFunction = this.registry[gaTaskName].find((element) => {\n return element.name == 'original';\n });\n this.tracker.set(gaTaskName, originalTaskFunction);\n });\n }\n}\n\nprovide('gaTaskManager', gaTaskManager);\n","/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\nimport {capitalize} from './utilities';\n\n\n/**\n * Provides a plugin for use with analytics.js, accounting for the possibility\n * that the global command queue has been renamed or not yet defined.\n * @param {string} pluginName The plugin name identifier.\n * @param {Function} pluginConstructor The plugin constructor function.\n */\nexport default function provide(pluginName, pluginConstructor) {\n const gaAlias = window.GoogleAnalyticsObject || 'ga';\n window[gaAlias] = window[gaAlias] || function(...args) {\n (window[gaAlias].q = window[gaAlias].q || []).push(args);\n };\n\n // Formally provides the plugin for use with analytics.js.\n window[gaAlias]('provide', pluginName, pluginConstructor);\n\n // Registers the plugin on the global gaplugins object.\n window.gaplugins = window.gaplugins || {};\n window.gaplugins[capitalize(pluginName)] = pluginConstructor;\n}\n","/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * \n * Modifications Copyright (C) 2017 Anki, Inc.\n */\n\n/**\n * A small shim of Object.assign that aims for brevity over spec-compliant\n * handling all the edge cases.\n * @param {!Object} target The target object to assign to.\n * @param {...?Object} sources Additional objects who properties should be\n * assigned to target. Non-objects are converted to objects.\n * @return {!Object} The modified target object.\n */\nexport const assign = Object.assign || function(target, ...sources) {\n for (let i = 0, len = sources.length; i < len; i++) {\n const source = Object(sources[i]);\n for (let key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n};\n\n\n/**\n * Capitalizes the first letter of a string.\n * @param {string} str The input string.\n * @return {string} The capitalized string\n */\nexport function capitalize(str) {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n"]} \ No newline at end of file +{"version":3,"sources":["lib/index.js","lib/provide.js","lib/utilities.js"],"names":["constructor","GaTaskManager","tracker","registry","gaTaskNames","addFunctionToTask","bind","removeFunctionFromTask","setCustomDimension","unsetCustomDimension","forEach","gaTaskName","get","set","model","userTask","hasOwnProperty","userTaskName","taskFunction","index","value","auxValue","auxType","Error","name","toString","remove","originalTaskFunction","provide","pluginName","pluginConstructor","gaAlias","window","GoogleAnalyticsObject","push","q","args","gaplugins","charAt","toUpperCase","slice"],"mappings":"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","file":"","sourcesContent":["import provide from './provide';\n\n/**\n * Class for the `ga-task-manager` analytics.js plugin.\n * @implements {GaTaskManagerPublicInterface}\n */\nclass GaTaskManager {\n /**\n * Registers declarative event tracking.\n * @param {!Tracker} tracker Passed internally by analytics.js\n */\n constructor(tracker) {\n this.tracker = tracker;\n this.registry = {};\n // @see https://developers.google.com/analytics/devguides/collection/analyticsjs/tasks\n this.gaTaskNames = [\n 'customTask',\n 'previewTask',\n 'checkProtocolTask',\n 'validationTask',\n 'checkStorageTask',\n 'historyImportTask',\n 'samplerTask',\n 'buildHitTask',\n 'sendHitTask',\n 'timingTask',\n 'displayFeaturesTask'\n ];\n\n // Bind methods.\n this.addFunctionToTask = this.addFunctionToTask.bind(this);\n this.removeFunctionFromTask = this.removeFunctionFromTask.bind(this);\n this.setCustomDimension = this.setCustomDimension.bind(this);\n this.unsetCustomDimension = this.unsetCustomDimension.bind(this);\n\n this.gaTaskNames.forEach((gaTaskName) => {\n this.registry[gaTaskName] = {};\n // Grab a reference to the default function for this task and register it as the first task.\n this.addFunctionToTask(gaTaskName, 'original', tracker.get(gaTaskName));\n\n // Override the GA Tracker's task with our task manager executor which calls all user tasks added to the registry.\n this.tracker.set(gaTaskName, (model) => {\n for (var userTask in this.registry[gaTaskName]) {\n if (this.registry[gaTaskName].hasOwnProperty(userTask)) {\n this.registry[gaTaskName][userTask](model);\n }\n }\n });\n });\n }\n\n /**\n * Adds a function to be executed at the specified GA Task.\n * @param {string} gaTaskName The name of the GA Task to add the taskFunction to.\n * @param {string} userTaskName An arbitrary name for the task performed by the taskFunction.\n * @param {function(!Model)} taskFunction The function to be added to the execution stack of the GA Task specified in gaTaskName.\n * The GA Model Object (https://developers.google.com/analytics/devguides/collection/analyticsjs/model-object-reference) will be passed to it at time of execution.\n */\n addFunctionToTask(gaTaskName, userTaskName, taskFunction) {\n if (this.registry[gaTaskName]) {\n this.registry[gaTaskName][userTaskName] = taskFunction;\n }\n }\n\n /**\n * Removes a function from the specified GA Task by the name given by the user.\n * @param {string} gaTaskName The name of the GA Task to remove the task from.\n * @param {string} userTaskName The arbitrary name for the task given to addFunctionToTask.\n */\n removeFunctionFromTask(gaTaskName, userTaskName) {\n if (this.registry[gaTaskName] && this.registry[gaTaskName][userTaskName]) {\n delete this.registry[gaTaskName][userTaskName];\n }\n }\n\n /**\n * Adds a function which sets a GA Custom Dimension at the specified GA Task execution time. Defaults to execution on the customTask Task.\n * @param {string} index The index of your dimension as defined in your Google Analytics property settings.\n * @param {string|number|function(): string|number} value Arbitrary string, number or function to set value for the dimension.\n * If a function is given, it will be executed every time at the moment of task execution.\n * If a number is given, it will be cast automatically to a string by calling Number.toString() method.\n * Observe size limit for the string value https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs\n * @param {string} gaTaskName The name of the GA Task on which to set the Custom Dimension. Defaults to 'customTask'.\n */\n setCustomDimension(index, value, gaTaskName = 'customTask') {\n const userTaskName = 'customDimension' + index;\n this.addFunctionToTask(gaTaskName, userTaskName, function(model){\n let auxValue = value;\n // If user provided a function to set value at execution time, then type check itsreturn value.\n if (typeof value == 'function') {\n auxValue = value();\n const auxType = typeof auxValue;\n if (auxType != 'string' && auxType != 'number') {\n throw new Error('Function ' + value.name + ' must return a string or number. Got ' + auxType + 'instead.');\n }\n }\n\n if (typeof auxValue === 'number') auxValue = auxValue.toString();\n\n // Set the dimension value to be sent to GA.\n model.set('dimension' + index, auxValue);\n });\n }\n\n /**\n * Removes a function added with setCustomDimension.\n * @param {string} index The index of your dimension as defined in your Google Analytics property settings.\n * @param {string} gaTaskName The name of the GA Task to add the taskFunction to.\n */\n unsetCustomDimension(index, gaTaskName = 'customTask') {\n const userTaskName = 'customDimension' + index;\n this.removeFunctionFromTask(gaTaskName, userTaskName);\n }\n\n /**\n * Resets all Tasks of the Tracker to the original function registered at the moment the plugin was required.\n */\n remove() {\n this.gaTaskNames.forEach((gaTaskName) => {\n const originalTaskFunction = this.registry[gaTaskName]['original'];\n this.tracker.set(gaTaskName, originalTaskFunction);\n });\n }\n}\n\nprovide('gaTaskManager', GaTaskManager);\n","/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Modifications Copyright (C) 2017 Anki, Inc\n */\n\n\nimport {capitalize} from './utilities';\n\n\n/**\n * Provides a plugin for use with analytics.js, accounting for the possibility\n * that the global command queue has been renamed or not yet defined.\n * @param {string} pluginName The plugin name identifier.\n * @param {Function} pluginConstructor The plugin constructor function.\n */\nexport default function provide(pluginName, pluginConstructor) {\n const gaAlias = window.GoogleAnalyticsObject || 'ga';\n window[gaAlias] = window[gaAlias] || function(...args) {\n (window[gaAlias].q = window[gaAlias].q || []).push(args);\n };\n\n // Formally provides the plugin for use with analytics.js.\n window[gaAlias]('provide', pluginName, pluginConstructor);\n\n // Registers the plugin on the global gaplugins object.\n window.gaplugins = window.gaplugins || {};\n window.gaplugins[capitalize(pluginName)] = pluginConstructor;\n}\n","/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Modifications Copyright (C) 2018 Anki, Inc.\n */\n\n/**\n * A small shim of Object.assign that aims for brevity over spec-compliant\n * handling all the edge cases.\n * @param {!Object} target The target object to assign to.\n * @param {...?Object} sources Additional objects who properties should be\n * assigned to target. Non-objects are converted to objects.\n * @return {!Object} The modified target object.\n */\nexport const assign = Object.assign || function(target, ...sources) {\n for (let i = 0, len = sources.length; i < len; i++) {\n const source = Object(sources[i]);\n for (let key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n};\n\n\n/**\n * Capitalizes the first letter of a string.\n * @param {string} str The input string.\n * @return {string} The capitalized string\n */\nexport function capitalize(str) {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n"]} \ No newline at end of file diff --git a/lib/externs/ga-task-manager.js b/lib/externs/ga-task-manager.js index 56c4d2b..a02e207 100644 --- a/lib/externs/ga-task-manager.js +++ b/lib/externs/ga-task-manager.js @@ -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() {} } diff --git a/lib/index.js b/lib/index.js index 49c378d..50cdad7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,9 +2,9 @@ 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 @@ -12,49 +12,81 @@ class gaTaskManager { 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; @@ -62,21 +94,33 @@ class gaTaskManager { 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); diff --git a/lib/provide.js b/lib/provide.js index f5da89a..19d51a1 100644 --- a/lib/provide.js +++ b/lib/provide.js @@ -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 */ diff --git a/lib/utilities.js b/lib/utilities.js index 8d93e9f..2a6ab55 100644 --- a/lib/utilities.js +++ b/lib/utilities.js @@ -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. */ /** diff --git a/package.json b/package.json index 07ae223..bb81c39 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ ], "author": { "name": "Vinnie Frietas", - "email": "vinnie.freitas@gmail.com", + "email": "vinnie.freitas@gmail.com" }, "homepage": "https://github.com/anki/ga-task-manager#readme", "dependencies": {