Skip to content

Commit

Permalink
refs #4996 more tests and bugfixes, improved API, some work on crossb…
Browse files Browse the repository at this point in the history
…rowser support
  • Loading branch information
tsteur committed Sep 9, 2014
1 parent 774611a commit 36b6aed
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 155 deletions.
184 changes: 99 additions & 85 deletions js/piwik.js
Expand Up @@ -447,12 +447,12 @@ if (typeof JSON2 !== 'object') {
getQuery, getContent, getContentImpressionsRequestsFromNodes, buildContentInteractionTrackingRedirectUrl,
buildContentInteractionRequestNode, buildContentInteractionRequest, buildContentImpressionRequest,
appendContentInteractionToRequestIfPossible, setupInteractionsTracking, trackContentImpressionClickInteraction,
internalIsNodeVisible, clearTrackedContentImpressions, getTrackerUrl, trackContentImpressions,
internalIsNodeVisible, clearTrackedContentImpressions, getTrackerUrl, trackAllContentImpressions,
getTrackedContentImpressions, getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet,
contentInteractionTrackingSetupDone, contains, match, pathname, piece, trackContentInteractionNode,
trackContentInteractionNode, trackContentImpressionsWithinNode, trackContentImpression,
enableTrackOnlyVisibleContent, trackContentInteraction, clearEnableTrackOnlyVisibleContent,
trackVisibleContentImpressions
trackVisibleContentImpressions, isTrackOnlyVisibleContentEnabled
*/
/*global _paq:true */
/*members push */
Expand Down Expand Up @@ -1812,7 +1812,13 @@ if (typeof Piwik !== 'object') {
},
getLocation: function ()
{
return this.location || windowAlias.location;
var locationAlias = this.location || windowAlias.location;

if (!locationAlias.origin) {
locationAlias.origin = locationAlias.protocol + "//" + locationAlias.hostname + (locationAlias.port ? ':' + locationAlias.port: '');
}

return locationAlias;
},
toAbsoluteUrl: function (url)
{
Expand Down Expand Up @@ -2110,7 +2116,7 @@ if (typeof Piwik !== 'object') {

// Keeps track of previously tracked content impressions
trackedContentImpressions = [],
enableTrackOnlyVisibleContent = false,
isTrackOnlyVisibleContentEnabled = false,

// Guard against installing the link tracker more than once per Tracker instance
linkTrackingInstalled = false,
Expand Down Expand Up @@ -3500,6 +3506,72 @@ if (typeof Piwik !== 'object') {
}
}
}
function enableTrackOnlyVisibleContent (checkOnSroll, timeIntervalInMs, tracker) {

if (isTrackOnlyVisibleContentEnabled) {
// already enabled, do not register intervals again
return true;
}

isTrackOnlyVisibleContentEnabled = true;

var didScroll = false;
var events, index;

function setDidScroll() { didScroll = true; }

trackCallbackOnLoad(function () {

function checkContent(intervalInMs) {
setTimeout(function () {
if (!isTrackOnlyVisibleContentEnabled) {
return; // the tests stopped tracking only visible content
}
didScroll = false;
tracker.trackVisibleContentImpressions();
checkContent(intervalInMs);
}, intervalInMs);
}

function checkContentIfDidScroll(intervalInMs) {

setTimeout(function () {
if (!isTrackOnlyVisibleContentEnabled) {
return; // the tests stopped tracking only visible content
}

if (didScroll) {
didScroll = false;
tracker.trackVisibleContentImpressions();
}

checkContentIfDidScroll(intervalInMs);
}, intervalInMs);
}

if (checkOnSroll) {

// scroll event is executed after each pixel, so we make sure not to
// execute event too often. otherwise FPS goes down a lot!
events = ['scroll', 'resize'];
for (index = 0; index < events.length; index++) {
if (documentAlias.addEventListener) {
documentAlias.addEventListener(events[index], setDidScroll);
} else {
windowAlias.attachEvent('on' + events[index], setDidScroll);
}
}

checkContentIfDidScroll(100);
}

if (timeIntervalInMs && timeIntervalInMs > 0) {
timeIntervalInMs = parseInt(timeIntervalInMs, 10);
checkContent(timeIntervalInMs);
}

});
}

/*
* Browser features (plugins, resolution, cookies)
Expand Down Expand Up @@ -3636,6 +3708,9 @@ if (typeof Piwik !== 'object') {
setupInteractionsTracking: setupInteractionsTracking,
trackContentImpressionClickInteraction: trackContentImpressionClickInteraction,
internalIsNodeVisible: isVisible,
enableTrackOnlyVisibleContent: function (checkOnScroll, timeIntervalInMs) {
return enableTrackOnlyVisibleContent(checkOnScroll, timeIntervalInMs, this);
},
clearTrackedContentImpressions: function () {
trackedContentImpressions = [];
},
Expand All @@ -3646,9 +3721,8 @@ if (typeof Piwik !== 'object') {
return configTrackerUrl;
},
clearEnableTrackOnlyVisibleContent: function () {
enableTrackOnlyVisibleContent = false;
isTrackOnlyVisibleContentEnabled = false;
},

/*</DEBUG>*/

/**
Expand Down Expand Up @@ -4360,18 +4434,19 @@ if (typeof Piwik !== 'object') {
}
},

enableTrackOnlyVisibleContent: function (checkOnSroll, timeIntervalInMs) {
var self = this;
var didScroll = false;

function setDidScroll() { didScroll = true; }
trackAllContentImpressions: function () {
trackCallback(function () {
trackCallbackOnReady(function () {
// we have to wait till DOM ready
var contentNodes = content.findContentNodes();

if (enableTrackOnlyVisibleContent) {
// already enabled, do not register intervals again
return true;
}
var requests = getContentImpressionsRequestsFromNodes(contentNodes);
sendBulkRequest(requests, configTrackerPause);
});
});
},

enableTrackOnlyVisibleContent = true;
trackVisibleContentImpressions: function (checkOnSroll, timeIntervalInMs) {

if (!isDefined(checkOnSroll)) {
checkOnSroll = true;
Expand All @@ -4381,79 +4456,19 @@ if (typeof Piwik !== 'object') {
timeIntervalInMs = 750;
}

trackCallbackOnLoad(function () {
var events, index;

function checkContent(intervalInMs) {
setTimeout(function () {
didScroll = false;
self.trackContentImpressions();
checkContent(intervalInMs);
}, intervalInMs);
}

function checkContentIfDidScroll(intervalInMs) {
setTimeout(function () {
if (didScroll) {
didScroll = false;
self.trackContentImpressions();
}

checkContentIfDidScroll(intervalInMs);
}, intervalInMs);
}

if (checkOnSroll) {

// scroll event is executed after each pixel, so we make sure not to
// execute event too often. otherwise FPS goes down a lot!
events = ['scroll', 'resize'];
for (index = 0; index < events.length; index++) {
if (windowAlias.addEventListener) {
windowAlias.addEventListener(events[index], setDidScroll);
} else {
windowAlias.attachEvent('on' + events[index], setDidScroll);
}
}

checkContentIfDidScroll(100);
}

if (timeIntervalInMs && timeIntervalInMs > 0) {
timeIntervalInMs = parseInt(timeIntervalInMs, 10);
checkContent(timeIntervalInMs);
}
enableTrackOnlyVisibleContent(checkOnSroll, timeIntervalInMs, this);

});
},

trackContentImpressions: function () {
trackCallback(function () {
if (enableTrackOnlyVisibleContent) {
trackCallbackOnLoad(function () {
// we have to wait till CSS parsed and applied
var contentNodes = content.findContentNodes();

var requests = getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet(contentNodes);
sendBulkRequest(requests, configTrackerPause);
});
} else {
trackCallbackOnReady(function () {
// we have to wait till DOM ready
var contentNodes = content.findContentNodes();
trackCallbackOnLoad(function () {
// we have to wait till CSS parsed and applied
var contentNodes = content.findContentNodes();

var requests = getContentImpressionsRequestsFromNodes(contentNodes);
sendBulkRequest(requests, configTrackerPause);
});
}
var requests = getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet(contentNodes);
sendBulkRequest(requests, configTrackerPause);
});
});
},

trackVisibleContentImpressions: function (checkOnSroll, timeIntervalInMs) {
this.enableTrackOnlyVisibleContent(checkOnSroll, timeIntervalInMs);
this.trackContentImpressions();
},

// it must be a node that is set to .piwikTrackContent or [data-track-content] or one of its parents nodes
trackContentImpression: function (contentName, contentPiece, contentTarget) {
if (!contentName) {
Expand All @@ -4472,7 +4487,7 @@ if (typeof Piwik !== 'object') {
// we might remove this method again
trackContentImpressionsWithinNode: function (domNode) {
trackCallback(function () {
if (enableTrackOnlyVisibleContent) {
if (isTrackOnlyVisibleContentEnabled) {
trackCallbackOnLoad(function () {
// we have to wait till CSS parsed and applied
var contentNodes = content.findContentNodesWithinNode(domNode);
Expand Down Expand Up @@ -4541,7 +4556,6 @@ if (typeof Piwik !== 'object') {
});
},


/**
* Used to record that the current page view is an item (product) page view, or a Ecommerce Category page view.
* This must be called before trackPageView() on the product/category page.
Expand Down
47 changes: 25 additions & 22 deletions misc/internal-docs/content-tracking.md
Expand Up @@ -302,31 +302,15 @@ A typical example for a content block that displays a text ad.

There are several ways to track a content impression and/or interaction manually, semi-automatically and automatically. Please be aware that content impressions will be tracked using bulk tracking which will always send a `POST` request, even if `GET` is configured which is the default.

#### `trackContentImpressions()`
#### `trackAllContentImpressions()`

You can use this method to scan the entire DOM for content blocks.
For each content block we will track a content impression immediately unless you enable `enableTrackOnlyVisibleContent` see below.
For each content block we will track a content impression immediately unless you have called `trackVisibleContentImpressions()` before see below.

Note: We will not send an impression of the same content block twice if you call this method multiple times unless `trackPageView()` is called meanwhile. This is useful for single page applications. The "same" content blocks means if a content block has the identical name, piece and target as an already tracked one.
Note: At this stage we do not exeute this method automatically along with a trackPageView(), we can do this later once we know it works

#### `trackContentImpressionsWithinNode(domNode, contentTarget)`

You can use this method if you, for instance, dynamically add an element using JavaScript to your DOM after the we have tracked the initial impressions. Calling this method will make sure an impression will be tracked for all content blocks contained within this node.

Example
```
var div = $('<div>...<div data-track-content>...</div>...<div data-track-content>...</div></div>');
$('#id').append(div);
_paq.push(['trackContentImpressionsWithinNode', div[0]]);
```

We would detect two new content blocks in this example.

Please note: In case you have enabled to only track visible content blocks we will respect this. In case it contains a content block that was already tracked we will not track it again.

#### `enableTrackOnlyVisibleContent(checkOnSroll, timeIntervalInMs)`
#### `trackVisibleContentImpressions(checkOnSroll, timeIntervalInMs)`
If you enable to track only visible content we will only track an impression if a content block is actually visible. With visible we mean the content block has been in the view port, it is actually in the DOM and is not hidden via CSS (opacity, visibility, display, ...).

* Optionally you can tell us to rescan the DOM automatically after each scroll event by passing `checkOnSroll=true`. We will then check whether the previously hidden content blocks are visible now and if so track the impression.
Expand All @@ -338,14 +322,33 @@ If you enable to track only visible content we will only track an impression if
* Rescanning the entire DOM and detecting the visible state of content blocks can take a while depending on the browser and amount of content
* We do not really rescan every X milliseconds. We will schedule the next rescan after a previous scan has finished. So if it takes 20ms to scan the DOM and you tell us to rescan every 50ms it can actually take 70ms.
* In case your frames per second goes down you might want to increase this value
* If you do not want us to perform any checks you can either call `trackContentImpressions()` manually at any time to rescan the entire DOM or `trackContentImpressionsWithinNode()` to check only a specific part of the DOM for visible content blocks.
* If you do want to only track visible content but not want us to perform any checks automatically you can either call `trackVisibleContentImpressions()` manually at any time to rescan the entire DOM or `trackContentImpressionsWithinNode()` to check only a specific part of the DOM for visible content blocks.
* Call `trackVisibleContentImpressions(false, 0)` to initially track only visible content impressions
* Call `trackVisibleContentImpressions()` at any time again to rescan the entire DOM for newly visible content blocks or
* Call `trackContentImpressionsWithinNode(node)` at any time to rescan only a part of the DOM for newly visible content blocks

It is recommended to call this method once before any call to `trackContentImpressions` or `trackContentImpressionsWithinNode()`. If you call this method more than once it does not have any effect.
Note: You can not change the `checkOnScroll` or `timeIntervalInMs` after this method was called the first time.

#### `trackVisibleContentImpressions(checkOnSroll, timeIntervalInMs)`
#### `(checkOnSroll, timeIntervalInMs)`

Is a shorthand for calling `enableTrackOnlyVisibleContent()` and `trackContentImpressions()`.

#### `trackContentImpressionsWithinNode(domNode, contentTarget)`

You can use this method if you, for instance, dynamically add an element using JavaScript to your DOM after the we have tracked the initial impressions. Calling this method will make sure an impression will be tracked for all content blocks contained within this node.

Example
```
var div = $('<div>...<div data-track-content>...</div>...<div data-track-content>...</div></div>');
$('#id').append(div);
_paq.push(['trackContentImpressionsWithinNode', div[0]]);
```

We would detect two new content blocks in this example.

Please note: In case you have enabled to only track visible content blocks we will respect this. In case it contains a content block that was already tracked we will not track it again.

#### trackContentInteractionNode(domNode, contentInteraction)

By default we track interactions depending on a click and sometimes we cannot track interactions automatically add all. See "How do we track an interaction automatically?". In case you want to track an interaction manually for instance on a double click or on a form submit you can do this as following:
Expand Down
21 changes: 21 additions & 0 deletions tests/javascript/content-fixtures/trackingContent.html
@@ -0,0 +1,21 @@
<div>
<img src="img1-en.jpg" data-track-content/>
<img src="img1-en.jpg" class="piwikTrackContent"/> <!-- same as before should be ignored -->
<div id="block1" style="display: none;">
<a href="http://img2.example.com" data-track-content><img src="img2-en.jpg" data-content-piece="img.jpg"/></a>
<a href="http://img3.example.com" data-track-content><img src="img3-en.jpg" data-content-piece/></a>
<a href="http://img4.example.com" data-track-content><p data-content-piece="My content 4">Lorem ipsum</p></a>
</div>
<div id="block2">
<div id="ex5" data-track-content data-content-name="My Ad 5">
<img src="http://img5.example.com/path/xyz.jpg" data-content-piece />
<a href="/anylink5" data-content-target>Lorem ipsum</a>
</div>
<a href="http://img6.example.com" data-track-content>
<img src="http://www.example.com/path/xyz.jpg" data-content-piece />
</a>
<a href="http://img7.example.com" data-track-content data-content-name="My Ad 7" style="visibility:hidden;">
Lorem ipsum
</a>
</div>
</div>

0 comments on commit 36b6aed

Please sign in to comment.