Fire a pixel for UI flow tracking of sub/unsubscribe.

This will allow us to collect information about how users found
subreddits that they subscribe to. We can use this information to test
the effectiveness of new ways of discovering and subscribing to
subreddits.

Specifically, when the subscribe button is clicked, for the current page
and previous page: the URL, referrer URL, and the type of UI element
clicked are sent. We'll use this to answer questions like:

 * "did clicking on gizmo A lead to users subscribing to subreddit B?"
 * "why did we see a spike in subscriptions to subreddit X today?"
This commit is contained in:
Max Goodman
2012-07-25 18:43:21 -07:00
parent 39e3a81c0a
commit 482e8644d8
7 changed files with 131 additions and 2 deletions

View File

@@ -172,6 +172,8 @@ adtracker_url = /static/pixel.png
adframetracker_url = /static/pixel.png
# open redirector to bounce clicks off of on sponsored links for tracking
clicktracker_url = /static/pixel.png
# url to request to track interaction statistics
uitracker_url = /static/pixel.png
# new pixel
newtracker_url =

View File

@@ -145,6 +145,7 @@ def js_config():
"tracking_domain": g.tracking_domain,
"adtracker_url": g.adtracker_url,
"clicktracker_url": g.clicktracker_url,
"uitracker_url": g.uitracker_url,
"static_root": static(''),
}
return config

View File

@@ -95,5 +95,97 @@ r.analytics = {
thumb.attr('href', click_url)
thing.data('trackerFired', true)
},
fireUITrackingPixel: function(action, srname) {
var pixel = new Image()
pixel.src = r.config.uitracker_url + '?' + $.param(
_.extend(
{
'act': action,
'sr': srname,
'r': Math.round(Math.random() * 2147483647) // cachebuster
},
r.analytics.breadcrumbs.toParams()
)
)
}
}
r.analytics.breadcrumbs = {
hasSessionStorage: 'sessionStorage' in window,
selector: '.thing, .side, .sr-list, .srdrop, .tagline, .md, .organic-listing, .gadget, a, button, input',
init: function() {
this.data = this._load()
var refreshed = this.data[0] && this.data[0]['url'] == window.location
if (!refreshed) {
this._storeBreadcrumb()
}
$(document).delegate('a, button', 'click', $.proxy(function(ev) {
this.storeLastClick($(ev.target))
}, this))
},
_load: function() {
if (!this.hasSessionStorage) {
return [{stored: false}]
}
var data
try {
data = JSON.parse(sessionStorage['breadcrumbs'])
} catch (e) {
data = []
}
if (!_.isArray(data)) {
data = []
}
return data
},
store: function(data) {
if (this.hasSessionStorage) {
sessionStorage['breadcrumbs'] = JSON.stringify(this.data)
}
},
_storeBreadcrumb: function() {
var cur = {
'url': location.toString()
}
if ('referrer' in document) {
var referrerExternal = !document.referrer.match('^' + r.config.currentOrigin),
referrerUnexpected = this.data[0] && document.referrer != this.data[0]['url']
if (referrerExternal || referrerUnexpected) {
cur['ref'] = document.referrer
}
}
this.data.unshift(cur)
this.data = this.data.slice(0, 2)
this.store()
},
storeLastClick: function(el) {
this.data[0]['click'] =
r.utils.querySelectorFromEl(el, this.selector)
this.store()
},
toParams: function() {
params = []
for (var i = 0; i < this.data.length; i++) {
_.each(this.data[i], function(v, k) {
params['c'+i+'_'+k] = v
})
}
return params
}
}

View File

@@ -4,6 +4,9 @@ r.setup = function(config) {
r.config = config
// Set the legacy config global
reddit = config
r.config.currentOrigin = location.protocol+'//'+location.host
r.analytics.breadcrumbs.init()
}
$(function() {

View File

@@ -12,7 +12,7 @@ r.login = {
endpoint = r.config.https_endpoint || ('http://'+r.config.ajax_domain),
apiTarget = endpoint+'/api/'+action+'/'+username
if (this.currentOrigin == endpoint || $.support.cors) {
if (r.config.currentOrigin == endpoint || $.support.cors) {
var params = form.serialize()
params.push({name:'api_type', value:'json'})
$.ajax({
@@ -192,7 +192,7 @@ r.ui.LoginForm.prototype = $.extend(new r.ui.Form(), {
_handleNetError: function(result, err, xhr) {
r.ui.Form.prototype._handleNetError.apply(this, arguments)
if (xhr.status == 0 && r.login.currentOrigin != r.config.https_endpoint) {
if (xhr.status == 0 && r.config.currentOrigin != r.config.https_endpoint) {
$('<p>').append(
$('<a>')
.text(r.strings.login_fallback_msg)

View File

@@ -230,6 +230,8 @@ function toggle_label (elem, callback, cancelback) {
}
function toggle(elem, callback, cancelback) {
r.analytics.breadcrumbs.storeLastClick(elem)
var self = $(elem).parent().andSelf().filter(".option");
var sibling = self.removeClass("active")
.siblings().addClass("active").get(0);
@@ -332,6 +334,7 @@ function subscribe(reddit_name) {
}
$.things(reddit_name).find(".entry").addClass("likes");
$.request("subscribe", {sr: reddit_name, action: "sub"});
r.analytics.fireUITrackingPixel("sub", reddit_name)
}
};
};
@@ -344,6 +347,7 @@ function unsubscribe(reddit_name) {
}
$.things(reddit_name).find(".entry").removeClass("likes");
$.request("subscribe", {sr: reddit_name, action: "unsub"});
r.analytics.fireUITrackingPixel("unsub", reddit_name)
}
};
};

View File

@@ -1,5 +1,32 @@
r.utils = {
staticURL: function (item) {
return r.config.static_root + '/' + item
},
querySelectorFromEl: function(targetEl, selector) {
return $(targetEl).parents().andSelf()
.filter(selector || '*')
.map(function(idx, el) {
var parts = [],
$el = $(el),
elFullname = $el.data('fullname'),
elId = $el.attr('id'),
elClass = $el.attr('class')
parts.push(el.nodeName.toLowerCase())
if (elFullname) {
parts.push('[data-fullname="' + elFullname + '"]')
} else {
if (elId) {
parts.push('#' + elId)
} else if (elClass) {
parts.push('.' + _.compact(elClass.split(/\s+/)).join('.'))
}
}
return parts.join('')
})
.toArray().join(' ')
}
}