Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorThomas Steur <thomas.steur@googlemail.com>2014-09-02 20:30:31 +0400
committerThomas Steur <thomas.steur@googlemail.com>2014-09-02 20:30:31 +0400
commit053f6c19be177621bce18b623a99b5549dcda22b (patch)
tree4d48fe4eccdcf8da25a7c7008e9600d6ed51335f /js
parenta6474178f9a770d56754636345082a3cc698eeb8 (diff)
add possbility to track only visible content impressions and to detect them automatically
Diffstat (limited to 'js')
-rw-r--r--js/piwik.js186
1 files changed, 177 insertions, 9 deletions
diff --git a/js/piwik.js b/js/piwik.js
index 0f1e14149c..624b52327f 100644
--- a/js/piwik.js
+++ b/js/piwik.js
@@ -1076,8 +1076,8 @@ if (typeof Piwik !== 'object') {
//-- Cross browser method to get style properties:
function _getStyle(el, property) {
- if ( window.getComputedStyle ) {
- return document.defaultView.getComputedStyle(el,null)[property];
+ if ( windowAlias.getComputedStyle ) {
+ return documentAlias.defaultView.getComputedStyle(el,null)[property];
}
if ( el.currentStyle ) {
return el.currentStyle[property];
@@ -1086,7 +1086,7 @@ if (typeof Piwik !== 'object') {
function _elementInDocument(element) {
while (element = element.parentNode) {
- if (element == document) {
+ if (element == documentAlias) {
return true;
}
}
@@ -1519,13 +1519,13 @@ if (typeof Piwik !== 'object') {
}
var rect = node.getBoundingClientRect();
- var html = document.documentElement ? document.documentElement : {};
+ var html = documentAlias.documentElement ? documentAlias.documentElement : {};
return (
rect.bottom > 0 &&
rect.right > 0 &&
- rect.left < (window.innerWidth || html.clientWidth) &&
- rect.top < (window.innerHeight || html.clientHeight)
+ rect.left < (windowAlias.innerWidth || html.clientWidth) &&
+ rect.top < (windowAlias.innerHeight || html.clientHeight)
);
},
isNodeVisible: function (node) {
@@ -1876,6 +1876,9 @@ if (typeof Piwik !== 'object') {
// Browser features via client-side data collection
browserFeatures = {},
+ // Keeps track of previously tracked content impressions
+ trackedContentImpressions = [],
+
// Guard against installing the link tracker more than once per Tracker instance
linkTrackingInstalled = false,
@@ -2739,14 +2742,83 @@ if (typeof Piwik !== 'object') {
return configTrackerUrl + (configTrackerUrl.indexOf('?') < 0 ? '?' : '&') + request;
}
+ function wasContentImpressionAlreadyTracked(content)
+ {
+ if (!trackedContentImpressions || !trackedContentImpressions.length) {
+ return false;
+ }
+
+ var index;
+
+ for (index = 0; index < trackedContentImpressions.length; index++) {
+ var trackedContent = trackedContentImpressions[index];
+
+ if (!trackedContent) {
+ continue;
+ }
+
+ if (trackedContent.name === content.name &&
+ trackedContent.piece === content.piece &&
+ trackedContent.target === content.target) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ * Log currently visible content pieces
+ */
+ function logCurrentlyVisibleContentImpressionsIfNotTrackedYet() {
+
+ var contentNodes = content.findContentNodes();
+
+ if (!contentNodes || !contentNodes.length) {
+ return;
+ }
+
+ var index;
+
+ for (index = 0; index < contentNodes.length; index++) {
+ if (!content.isNodeVisible(contentNodes[index])) {
+ contentNodes.splice(index, 1);
+ }
+ }
+
+ if (!contentNodes || !contentNodes.length) {
+ return;
+ }
+
+ var contents = content.collectContent(contentNodes);
+
+ for (index = 0; index < contents; index++) {
+ // TODO !!! it could happen that a content was tracked while this check is performed if they run both at the same time, maybe we need some kind of lock?
+ if (wasContentImpressionAlreadyTracked(contents[index])) {
+ contents.splice(index, 1);
+ } else {
+ trackedContentImpressions.push(contents[index]);
+ }
+ }
+
+ logContentImpressions(contents, contentNodes);
+ }
+
/*
* Log all content pieces
*/
- function logContentImpressions() {
+ function logAllContentImpressions() {
var contentNodes = content.findContentNodes();
var contents = content.collectContent(contentNodes);
+ logContentImpressions(contents, contentNodes);
+ }
+
+ /*
+ * Log all content pieces
+ */
+ function logContentImpressions(contents, contentNodes) {
if (!contents || !contents.length) {
return;
}
@@ -2956,6 +3028,37 @@ if (typeof Piwik !== 'object') {
callback();
}
+ function trackCallbackOnLoad(callback)
+ {
+ if (documentAlias.readyState === 'complete') {
+ setTimeout(callback, 1); // Handle async to allow delaying ready
+ } else if (windowAlias.addEventListener) {
+ windowAlias.addEventListener('load', callback);
+ } else if (windowAlias.attachEvent) {
+ windowAlias.attachEvent('onLoad', callback);
+ }
+ }
+
+ function trackCallbackOnReady(callback)
+ {
+ // TODO we might also need a poll similar to https://github.com/dperini/ContentLoaded/blob/master/src/contentloaded.js
+ var loaded = false;
+
+ if (documentAlias.attachEvent) {
+ loaded = documentAlias.readyState === "complete";
+ } else {
+ loaded = documentAlias.readyState !== "loading";
+ }
+
+ if (loaded) {
+ setTimeout(callback, 1); // Handle async to allow delaying ready
+ } else if (documentAlias.addEventListener) {
+ documentAlias.addEventListener('DOMContentLoaded', callback);
+ } else if (documentAlias.attachEvent) {
+ documentAlias.attachEvent('onreadystatechange', callback);
+ }
+ }
+
/*
* Construct regular expression of classes
*/
@@ -3920,9 +4023,74 @@ if (typeof Piwik !== 'object') {
}
},
- trackContentImpressions: function () {
+ trackAllContentImpressions: function () {
+ trackCallback(function () {
+ trackCallbackOnReady(function () {
+ logAllContentImpressions();
+ });
+ });
+ },
+
+ enableDetectContentImpressionsAutomatically: function (checkOnSroll, timeIntervalInMs) {
+ var self = this;
+ var didScroll = false;
+
+ trackCallbackOnLoad(function () {
+
+ function checkContent(intervalInMs) {
+ setTimeout(function () {
+ didScroll = false;
+ self.trackCurrentlyVisibleContentImpressionsIfNotTrackedYet();
+ checkBanners(intervalInMs);
+ }, intervalInMs);
+ }
+
+ function checkContentIfDidScroll(intervalInMs) {
+ setTimeout(function () {
+ if (didScroll) {
+ didScroll = false;
+ self.trackCurrentlyVisibleContentImpressionsIfNotTrackedYet();
+ }
+
+ 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!
+ // todo test if need to listen to all of the following events:
+ // 'ondrop', 'touchend', 'mousewheel', 'DOMMouseScroll'
+ var events = ['scroll', 'resize'];
+ for (var index = 0; index < events.length; index++) {
+ if (windowAlias.addEventListener) {
+ windowAlias.addEventListener(events[index], function () {
+ didScroll = true;
+ });
+ } else {
+ windowAlias.attachEvent('on' + events[index], function () {
+ didScroll = true;
+ });
+ }
+ }
+
+ checkContentIfDidScroll(100);
+ }
+
+ if (timeIntervalInMs && timeIntervalInMs > 0) {
+ timeIntervalInMs = parseInt(timeIntervalInMs, 10);
+ // TODO provide possibility to cancel interval? or if called again cancel previous one?
+ checkBanners(timeIntervalInMs);
+ }
+
+ });
+ },
+
+ trackCurrentlyVisibleContentImpressionsIfNotTrackedYet: function () {
trackCallback(function () {
- logContentImpressions();
+ trackCallbackOnLoad(function () {
+ logCurrentlyVisibleContentImpressionsIfNotTrackedYet();
+ });
});
},