From 4e9fcca48038f2e83ad8493159216482c1157e4c Mon Sep 17 00:00:00 2001 From: Chip Tol Date: Fri, 6 Mar 2015 09:34:01 -0500 Subject: [PATCH 1/4] Adding elapsedTime support to pagepings --- core/lib/core.js | 30 ++++++++-------- src/js/tracker.js | 89 +++++++++++++++++++++++++---------------------- 2 files changed, 63 insertions(+), 56 deletions(-) diff --git a/core/lib/core.js b/core/lib/core.js index dafc77155..7fc8aea1d 100644 --- a/core/lib/core.js +++ b/core/lib/core.js @@ -1,6 +1,6 @@ /* * JavaScript tracker core for Snowplow: core.js - * + * * Copyright (c) 2014 Snowplow Analytics Ltd. All rights reserved. * * This program is licensed to you under the Apache License Version 2.0, @@ -79,7 +79,7 @@ function trackerCore(base64, callback) { /** * Gets called by every trackXXX method * Adds context and payloadPairs name-value pairs to the payload - * Applies the callback to the built payload + * Applies the callback to the built payload * * @param sb object Payload * @param array contexts Custom contexts relating to the event @@ -91,9 +91,9 @@ function trackerCore(base64, callback) { sb.add('eid', uuid.v4()); sb.add('dtm', tstamp || new Date().getTime()); if (context) { - sb.addJson('cx', 'co', completeContexts(context)); + sb.addJson('cx', 'co', completeContexts(context)); } - + if (typeof callback === 'function') { callback(sb); } @@ -106,7 +106,7 @@ function trackerCore(base64, callback) { * * @param object eventJson Contains the properties and schema location for the event * @param array context Custom contexts relating to the event - * @param number tstamp Timestamp of the event + * @param number tstamp Timestamp of the event * @return object Payload */ function trackUnstructEvent(properties, context, tstamp) { @@ -134,11 +134,11 @@ function trackerCore(base64, callback) { }, addPayloadPair: addPayloadPair, - + /** * Merges a dictionary into payloadPairs * - * @param object dict Dictionary to add + * @param object dict Dictionary to add */ addPayloadDict: function (dict) { for (var key in dict) { @@ -286,11 +286,12 @@ function trackerCore(base64, callback) { * @param maxXOffset Maximum page x offset seen in the last ping period * @param minYOffset Minimum page y offset seen in the last ping period * @param maxYOffset Maximum page y offset seen in the last ping period + * @param number elapsedTime Total Elapsed Time the user has spent engaging the page * @param array context Custom contexts relating to the event * @param number tstamp Timestamp of the event * @return object Payload */ - trackPagePing: function (pageUrl, pageTitle, referrer, minXOffset, maxXOffset, minYOffset, maxYOffset, context, tstamp) { + trackPagePing: function (pageUrl, pageTitle, referrer, minXOffset, maxXOffset, minYOffset, maxYOffset, elapsedTime, context, tstamp) { var sb = payload.payloadBuilder(base64); sb.add('e', 'pp'); // 'pp' for Page Ping sb.add('url', pageUrl); @@ -300,6 +301,7 @@ function trackerCore(base64, callback) { sb.add('pp_max', maxXOffset); sb.add('pp_miy', minYOffset); sb.add('pp_may', maxYOffset); + sb.add('pp_elapsed', elapsedTime); return track(sb, context, tstamp); }, @@ -436,7 +438,7 @@ function trackerCore(base64, callback) { * Track an ad being served * * @param string impressionId Identifier for a particular ad impression - * @param string costModel The cost model. 'cpa', 'cpc', or 'cpm' + * @param string costModel The cost model. 'cpa', 'cpc', or 'cpm' * @param number cost Cost * @param string bannerId Identifier for the ad banner displayed * @param string zoneId Identifier for the ad zone @@ -451,10 +453,10 @@ function trackerCore(base64, callback) { schema: 'iglu:com.snowplowanalytics.snowplow/ad_impression/jsonschema/1-0-0', data: removeEmptyProperties({ impressionId: impressionId, - costModel: costModel, + costModel: costModel, cost: cost, targetUrl: targetUrl, - bannerId: bannerId, + bannerId: bannerId, zoneId: zoneId, advertiserId: advertiserId, campaignId: campaignId @@ -468,7 +470,7 @@ function trackerCore(base64, callback) { * Track an ad being clicked * * @param string clickId Identifier for the ad click - * @param string costModel The cost model. 'cpa', 'cpc', or 'cpm' + * @param string costModel The cost model. 'cpa', 'cpc', or 'cpm' * @param number cost Cost * @param string targetUrl (required) The link's target URL * @param string bannerId Identifier for the ad banner displayed @@ -520,14 +522,14 @@ function trackerCore(base64, callback) { schema: 'iglu:com.snowplowanalytics.snowplow/ad_conversion/jsonschema/1-0-0', data: removeEmptyProperties({ conversionId: conversionId, - costModel: costModel, + costModel: costModel, cost: cost, category: category, action: action, property: property, initialValue: initialValue, advertiserId: advertiserId, - campaignId: campaignId + campaignId: campaignId }) }; diff --git a/src/js/tracker.js b/src/js/tracker.js index a624a7c40..7397ba705 100644 --- a/src/js/tracker.js +++ b/src/js/tracker.js @@ -1,34 +1,34 @@ /* * JavaScript tracker for Snowplow: tracker.js - * - * Significant portions copyright 2010 Anthon Pang. Remainder copyright - * 2012-2014 Snowplow Analytics Ltd. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * Significant portions copyright 2010 Anthon Pang. Remainder copyright + * 2012-2014 Snowplow Analytics Ltd. All rights reserved. * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * * * Neither the name of Anthon Pang nor Snowplow Analytics Ltd nor the * names of their contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. + * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -167,6 +167,9 @@ // Document character set documentCharset = documentAlias.characterSet || documentAlias.charset, + // Track the elapsed time a user actively engages on a page + elapsedTime = 0, + // This forces the tracker to be HTTPS even if the page is not secure forceSecureTracker = argmap.hasOwnProperty('forceSecureTracker') ? (argmap.forceSecureTracker === true) : false, @@ -400,11 +403,11 @@ */ function resetMaxScrolls() { var offsets = getPageOffsets(); - + var x = offsets[0]; minXOffset = x; maxXOffset = x; - + var y = offsets[1]; minYOffset = y; maxYOffset = y; @@ -415,7 +418,7 @@ */ function updateMaxScrolls() { var offsets = getPageOffsets(); - + var x = offsets[0]; if (x < minXOffset) { minXOffset = x; @@ -428,7 +431,7 @@ minYOffset = y; } else if (y > maxYOffset) { maxYOffset = y; - } + } } /* @@ -715,7 +718,8 @@ if ((lastActivityTime + configHeartBeatTimer) > now.getTime()) { // Send ping if minimum visit time has elapsed if (configMinimumVisitTime < now.getTime()) { - logPagePing(pageTitle, context); // Grab the min/max globals + elapsedTime += configHeartBeatTimer; + logPagePing(pageTitle, elapsedTime, context); // Grab the min/max globals } } }, configHeartBeatTimer); @@ -731,7 +735,7 @@ * @param string pageTitle The page title to attach to this page ping * @param object context Custom context relating to the event */ - function logPagePing(pageTitle, context) { + function logPagePing(pageTitle, elapsedTime, context) { core.trackPagePing( purify(configCustomUrl || locationHrefAlias), pageTitle, @@ -740,6 +744,7 @@ cleanOffset(maxXOffset), cleanOffset(minYOffset), cleanOffset(maxYOffset), + elapsedTime, addCommonContexts(context)); resetMaxScrolls(); } @@ -752,7 +757,7 @@ * @param string total * @param string tax * @param string shipping - * @param string city + * @param string city * @param string state * @param string country * @param string currency The currency the total/tax/shipping are expressed in @@ -782,7 +787,7 @@ * Browser prefix */ function prefixPropertyName(prefix, propertyName) { - + if (prefix !== '') { return prefix + propertyName.charAt(0).toUpperCase() + propertyName.slice(1); } @@ -1005,7 +1010,7 @@ * where tracking is: * 1) Sending events to a collector * 2) Setting first-party cookies - * @param bool enable If true and Do Not Track feature enabled, don't track. + * @param bool enable If true and Do Not Track feature enabled, don't track. */ respectDoNotTrack: function (enable) { helpers.warn('This usage of respectDoNotTrack is deprecated. Instead add a "respectDoNotTrack" field to the argmap argument of newTracker.'); @@ -1039,7 +1044,7 @@ * be "_self", "_top", or "_parent"). * * @see https://bugs.webkit.org/show_bug.cgi?id=54783 - * + * * @param object criterion Criterion by which it will be decided whether a link will be tracked * @param bool pseudoClicks If true, use pseudo click-handler (mousedown+mouseup) * @param bool trackContent Whether to track the innerHTML of the link element @@ -1146,7 +1151,7 @@ /** * Set the business-defined user ID for this user using the location querystring. - * + * * @param string queryName Name of a querystring name-value pair */ setUserIdFromLocation: function(querystringField) { @@ -1155,7 +1160,7 @@ /** * Set the business-defined user ID for this user using the referrer querystring. - * + * * @param string queryName Name of a querystring name-value pair */ setUserIdFromReferrer: function(querystringField) { @@ -1164,7 +1169,7 @@ /** * Set the business-defined user ID for this user to the value of a cookie. - * + * * @param string cookieName Name of the cookie whose value will be assigned to businessUserId */ setUserIdFromCookie: function(cookieName) { @@ -1172,7 +1177,7 @@ }, /** - * Configure this tracker to log to a CloudFront collector. + * Configure this tracker to log to a CloudFront collector. * * @param string distSubdomain The subdomain on your CloudFront collector's distribution */ @@ -1184,7 +1189,7 @@ * * Specify the Snowplow collector URL. No need to include HTTP * or HTTPS - we will add this. - * + * * @param string rawUrl The collector URL minus protocol and /i */ setCollectorUrl: function (rawUrl) { @@ -1381,25 +1386,25 @@ * Track an ad being served * * @param string impressionId Identifier for a particular ad impression - * @param string costModel The cost model. 'cpa', 'cpc', or 'cpm' + * @param string costModel The cost model. 'cpa', 'cpc', or 'cpm' * @param number cost Cost * @param string bannerId Identifier for the ad banner displayed * @param string zoneId Identifier for the ad zone * @param string advertiserId Identifier for the advertiser * @param string campaignId Identifier for the campaign which the banner belongs to * @param object Custom context relating to the event - */ + */ trackAdImpression: function(impressionId, costModel, cost, targetUrl, bannerId, zoneId, advertiserId, campaignId, context) { trackCallback(function () { core.trackAdImpression(impressionId, costModel, cost, targetUrl, bannerId, zoneId, advertiserId, campaignId, addCommonContexts(context)); }); }, - + /** * Track an ad being clicked * * @param string clickId Identifier for the ad click - * @param string costModel The cost model. 'cpa', 'cpc', or 'cpm' + * @param string costModel The cost model. 'cpa', 'cpc', or 'cpm' * @param number cost Cost * @param string targetUrl (required) The link's target URL * @param string bannerId Identifier for the ad banner displayed From 652436795c31a44b13fe70e529c8e1d3c80dc506 Mon Sep 17 00:00:00 2001 From: Chip Tol Date: Fri, 1 May 2015 13:45:31 -0400 Subject: [PATCH 2/4] Pointing to local core instead of NPM module --- src/js/tracker.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/tracker.js b/src/js/tracker.js index 7397ba705..bde769c21 100644 --- a/src/js/tracker.js +++ b/src/js/tracker.js @@ -45,7 +45,8 @@ links = require('./links'), forms = require('./forms'), requestQueue = require('./out_queue'), - coreConstructor = require('snowplow-tracker-core'), + //coreConstructor = require('snowplow-tracker-core'), + coreConstructor = require('./core') object = typeof exports !== 'undefined' ? exports : this; // For eventual node.js environment support From 71b0c85c8aae7a7762cfff8e5e2fbf2c0548bfdd Mon Sep 17 00:00:00 2001 From: Chip Tol Date: Fri, 1 May 2015 13:54:02 -0400 Subject: [PATCH 3/4] Adding symlink to core --- src/js/core | 1 + 1 file changed, 1 insertion(+) create mode 120000 src/js/core diff --git a/src/js/core b/src/js/core new file mode 120000 index 000000000..58377d59e --- /dev/null +++ b/src/js/core @@ -0,0 +1 @@ +../../core/ \ No newline at end of file From 3961dd744852a07b06a4ed3392bc676cda9a85e3 Mon Sep 17 00:00:00 2001 From: Chip Tol Date: Fri, 1 May 2015 14:07:32 -0400 Subject: [PATCH 4/4] Adding PERSONALIZATION to help explain how to keep in sync with upsteram. --- PERSONALIZATION.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 PERSONALIZATION.md diff --git a/PERSONALIZATION.md b/PERSONALIZATION.md new file mode 100644 index 000000000..953cf407e --- /dev/null +++ b/PERSONALIZATION.md @@ -0,0 +1,64 @@ +This is our fork of Snowplow's javscript collector. + +Our fork adds an "elased time" feature to the collector: + +The code keeps an elapsed time timer while the user is +actively engaging the page. This data is sent along +in the payload. + +To roll a new version: + +1. git checkout master (to ensure you are at master) +1. git branch +2. git checkout +3. git fetch upstream/master + +At this point, you have a new branch and the newest code +from upstream. You probably want to merge in a tagged +version of the upstream, so look it up: + +4.Find the tag you want to merge in> + +> git tag + +Our versions start with 'p' and Snowplow's don't. + +2.4.3 <== upsteam +p.2.4.3 <== local to this repo. + +5. Merge in the desired tag: + +git merge + +6. Fix any conflicts. + +7. Test + +> grunt test + +Fix any issues. + +8. + +> git commit + +9. Merge into master + +> git checkout master +> git merge + +10. Tag it using the same tag as upsteram, but prepend with 'p' + +> git tag p + +11. Push to origin. + +> git push origin + +12. Roll a new release + +> grunt + + + +