Skip to content

Commit

Permalink
Added support for per-event vars. (#2928)
Browse files Browse the repository at this point in the history
This allows specification of vars like `veritcalScrollBoundary` etc.
These vars only make sense when particular types of triggers fire and
the valuer is based on the trigger config.

Fixes #2031. Also helps #1297
  • Loading branch information
avimehta committed Apr 27, 2016
1 parent 9968af7 commit 2e8fbe3
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 27 deletions.
2 changes: 1 addition & 1 deletion examples/analytics.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"requests": {
"base": "https://example.com/?domain=${canonicalHost}&path=${canonicalPath}&title=${title}&time=${timestamp}&tz=${timezone}&pid=${pageViewId}&_=${random}",
"pageview": "${base}&name=${eventName}&type=${eventId}&screenSize=${screenWidth}x${screenHeight}",
"event": "${base}&name=${eventName}&scrollY=${scrollTop}&scrollX=${scrollLeft}&height=${availableScreenHeight}&width=${availableScreenWidth}"
"event": "${base}&name=${eventName}&scrollY=${scrollTop}&scrollX=${scrollLeft}&height=${availableScreenHeight}&width=${availableScreenWidth}&scrollBoundV=${verticalScrollBoundary}&scrollBoundH=${horizontalScrollBoundary}"
},
"vars": {
"title": "Example Request"
Expand Down
9 changes: 5 additions & 4 deletions extensions/amp-analytics/0.1/amp-analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,10 @@ export class AmpAnalytics extends AMP.BaseElement {
* method generates the request and sends the request out.
*
* @param {!JSONObject} trigger JSON config block that resulted in this event.
* @param {!Object} unusedEvent Object with details about the event.
* @param {!Object} event Object with details about the event.
* @private
*/
handleEvent_(trigger, unusedEvent) {
handleEvent_(trigger, event) {
let request = this.requests_[trigger['request']];
if (!request) {
user.error(this.getName_(), 'Ignoring event. Request string ' +
Expand All @@ -334,13 +334,14 @@ export class AmpAnalytics extends AMP.BaseElement {
this.config_['vars']['requestCount']++;

// Replace placeholders with URI encoded values.
// Precedence is trigger.vars > config.vars.
// Precedence is event.vars > trigger.vars > config.vars.
// Nested expansion not supported.
request = expandTemplate(request, key => {
const match = key.match(/([^(]*)(\([^)]*\))?/);
const name = match[1];
const argList = match[2] || '';
const raw = (trigger['vars'] && trigger['vars'][name] ||
const raw = event.vars[name] ||
(trigger['vars'] && trigger['vars'][name] ||
this.config_['vars'] && this.config_['vars'][name]);
const val = this.encodeVars_(raw != null ? raw : '', name);
return val + argList;
Expand Down
35 changes: 24 additions & 11 deletions extensions/amp-analytics/0.1/instrumentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,17 @@ const MIN_TIMER_INTERVAL_SECONDS_ = 0.5;
/** @private @const {number} */
const DEFAULT_MAX_TIMER_LENGTH_SECONDS_ = 7200;

/** @private {number} */
/** @private @const {number} */
const SCROLL_PRECISION_PERCENT = 5;

/** @private @const {string} */
const VAR_H_SCROLL_BOUNDARY = 'horizontalScrollBoundary';

/** @private @const {string} */
const VAR_V_SCROLL_BOUNDARY = 'verticalScrollBoundary';

/**
* This type signifies a callback that gets called when an analytics event that
* the listener subscribed to fires.
* Type to define a callback that is called when an instrumented event fires.
* @typedef {function(!AnalyticsEvent)}
*/
let AnalyticsEventListenerDef;
Expand Down Expand Up @@ -69,9 +74,11 @@ class AnalyticsEvent {

/**
* @param {!AnalyticsEventType} type The type of event.
* @param {!Object<string, string>} A map of vars and their values.
*/
constructor(type) {
constructor(type, vars) {
this.type = type;
this.vars = vars || Object.create(null);
}
}

Expand Down Expand Up @@ -401,11 +408,13 @@ export class InstrumentationService {
}

/**
* @param {!Object.<number, boolean>} bounds.
* @param {!Object<number, boolean>} bounds.
* @param {number} scrollPos Number representing the current scroll
* @param {string} varName variable name to assign to the bound that
* triggers the event
* position.
*/
const triggerScrollEvents = function(bounds, scrollPos) {
const triggerScrollEvents = function(bounds, scrollPos, varName) {
if (!scrollPos) {
return;
}
Expand All @@ -416,7 +425,9 @@ export class InstrumentationService {
continue;
}
bounds[b] = true;
listener(new AnalyticsEvent(AnalyticsEventType.SCROLL));
const vars = Object.create(null);
vars[varName] = b;
listener(new AnalyticsEvent(AnalyticsEventType.SCROLL, vars));
}
};

Expand All @@ -426,9 +437,11 @@ export class InstrumentationService {
// Calculates percentage scrolled by adding screen height/width to
// top/left and dividing by the total scroll height/width.
triggerScrollEvents(boundsV,
(e.top + e.height) * 100 / this.viewport_.getScrollHeight());
(e.top + e.height) * 100 / this.viewport_.getScrollHeight(),
VAR_V_SCROLL_BOUNDARY);
triggerScrollEvents(boundsH,
(e.left + e.width) * 100 / this.viewport_.getScrollWidth());
(e.left + e.width) * 100 / this.viewport_.getScrollWidth(),
VAR_H_SCROLL_BOUNDARY);
});
}

Expand All @@ -437,8 +450,8 @@ export class InstrumentationService {
* SCROLL_PRECISION_PERCENT and returns an object with normalized boundaries
* as keys and false as values.
*
* @param {!Array.<number>} bounds array of bounds.
* @return {!Object.<number,boolean>} Object with normalized bounds as keys
* @param {!Array<number>} bounds array of bounds.
* @return {!Object<number,boolean>} Object with normalized bounds as keys
* and false as value.
* @private
*/
Expand Down
2 changes: 2 additions & 0 deletions extensions/amp-analytics/0.1/test/test-amp-analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ describe('amp-analytics', function() {
return analytics.layoutCallback().then(() => {
return analytics.handleEvent_({
request: name,
}, {
vars: Object.create(null),
}).then(url => {
const val = VENDOR_REQUESTS[vendor][name];
if (val == null) {
Expand Down
18 changes: 16 additions & 2 deletions extensions/amp-analytics/0.1/test/test-instrumentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,17 @@ describe('amp-analytics.instrumentation', function() {
}},
fn1);
ins.addListener({'on': 'scroll', 'scrollSpec': {
'verticalBoundaries': [90], 'horizontalBoundaries': [90]}}, fn2);

'verticalBoundaries': [92], 'horizontalBoundaries': [92]}}, fn2);

function matcher(expected) {
return actual => {
return actual.vars.horizontalScrollBoundary === String(expected) ||
actual.vars.verticalScrollBoundary === String(expected);
};
}
expect(fn1.callCount).to.equal(2);
expect(fn1.getCall(0).calledWithMatch(sinon.match(matcher(0)))).to.be.true;
expect(fn1.getCall(1).calledWithMatch(sinon.match(matcher(0)))).to.be.true;
expect(fn2.callCount).to.equal(0);

// Scroll Down
Expand All @@ -337,7 +345,13 @@ describe('amp-analytics.instrumentation', function() {
ins.onScroll_({top: 500, left: 500, height: 250, width: 250});

expect(fn1.callCount).to.equal(4);
expect(fn1.getCall(2).calledWithMatch(sinon.match(matcher(100)))).to.be
.true;
expect(fn1.getCall(3).calledWithMatch(sinon.match(matcher(100)))).to.be
.true;
expect(fn2.callCount).to.equal(2);
expect(fn2.getCall(0).calledWithMatch(sinon.match(matcher(90)))).to.be.true;
expect(fn2.getCall(1).calledWithMatch(sinon.match(matcher(90)))).to.be.true;
});

it('does not fire duplicates on scroll', () => {
Expand Down
2 changes: 1 addition & 1 deletion extensions/amp-analytics/amp-analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ Use this configuration to fire a request when a specified element is clicked. Us

#### Scroll trigger (`"on": "scroll"`)
Use this configuration to fire a request under certain conditions when the page is scrolled. Use `scrollSpec` to control when this will fire:
- `scrollSpec` This object can contain `verticalBoundaries` and `horizontalBoundaries`. At least one of the two properties is required for a scroll event to fire. The values for both of the properties should be arrays of numbers containing the boundaries on which a scroll event is generated. For instance, in the following code snippet, the scroll event will be fired when page is scrolled vertically by 25%, 50% and 90%. Additionally, the event will also fire when the page is horizontally scrolled to 90% of scroll width.
- `scrollSpec` This object can contain `verticalBoundaries` and `horizontalBoundaries`. At least one of the two properties is required for a scroll event to fire. The values for both of the properties should be arrays of numbers containing the boundaries on which a scroll event is generated. For instance, in the following code snippet, the scroll event will be fired when page is scrolled vertically by 25%, 50% and 90%. Additionally, the event will also fire when the page is horizontally scrolled to 90% of scroll width. To keep the page performant, the scroll boundaries are rounded to the nearest multiple of `5`.


```javascript
Expand Down
32 changes: 24 additions & 8 deletions extensions/amp-analytics/analytics-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,30 @@ Provides the time it took for HTTP connection to be setup. The duration includes
Example value `10`
## Interaction
### horizontalScrollBoundary
Provides the horizontal scroll boundary that triggered a scroll event. This var is
only available in a `trigger` of type `scroll`. The value of the boundary may be
rounded based on the precision supported by the extension. For example, a
boundary with value `1` and precision of `5` will result in value of var to be 0.
### totalEngagedTime
Provides the total time (in seconds) the user has been engaged with the page since the page
first became visible in the viewport. Total engaged time will be 0 until the
page first becomes visible.
Example value: `36`
### verticalScrollBoundary
Provides the vertical scroll boundary that triggered a scroll event. This var is
only available in a `trigger` of type `scroll`. The value of the boundary may be
rounded based on the precision supported by the extension. For example, a
boundary with value `1` and precision of `5` will result in value of var to be 0.
## Miscellaneous
### clientId
Expand Down Expand Up @@ -329,11 +353,3 @@ Provides the number of seconds that have elapsed since 1970. (Epoch time)
Example value: `1452710304312`
### totalEngagedTime
Provides the total time (in seconds) the user has been engaged with the page since the page
first became visible in the viewport. Total engaged time will be 0 until the
page first becomes visible.
Example value: `36`

0 comments on commit 2e8fbe3

Please sign in to comment.