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 + + + + 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/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 diff --git a/src/js/tracker.js b/src/js/tracker.js index e87f85e90..7189252ea 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. */ @@ -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 @@ -171,6 +172,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, @@ -458,11 +462,11 @@ */ function resetMaxScrolls() { var offsets = getPageOffsets(); - + var x = offsets[0]; minXOffset = x; maxXOffset = x; - + var y = offsets[1]; minYOffset = y; maxYOffset = y; @@ -473,7 +477,7 @@ */ function updateMaxScrolls() { var offsets = getPageOffsets(); - + var x = offsets[0]; if (x < minXOffset) { minXOffset = x; @@ -486,7 +490,7 @@ minYOffset = y; } else if (y > maxYOffset) { maxYOffset = y; - } + } } /* @@ -827,7 +831,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); @@ -843,7 +848,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) { refreshUrl(); core.trackPagePing( purify(configCustomUrl || locationHrefAlias), @@ -853,6 +858,7 @@ cleanOffset(maxXOffset), cleanOffset(minYOffset), cleanOffset(maxYOffset), + elapsedTime, addCommonContexts(context)); resetMaxScrolls(); } @@ -865,7 +871,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 @@ -895,7 +901,7 @@ * Browser prefix */ function prefixPropertyName(prefix, propertyName) { - + if (prefix !== '') { return prefix + propertyName.charAt(0).toUpperCase() + propertyName.slice(1); } @@ -1124,7 +1130,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.'); @@ -1167,7 +1173,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 @@ -1274,7 +1280,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) { @@ -1284,7 +1290,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) { @@ -1294,7 +1300,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) { @@ -1302,7 +1308,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 */ @@ -1314,7 +1320,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) { @@ -1511,25 +1517,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