@ra5mus/eb-comscore
Version:
Convenience wrapper for comscore functionality
725 lines (665 loc) • 25.7 kB
JavaScript
/**
* Javascript for EB comscore tracking.
*
* Also see original documentation here: http://wiki.rootdom.dk/index.php/Ekstrabladet_comscore_trackings
*
* These are the main steps to perform:
*
* 1. (mandatory) Initialize and configure comscore on page load.
* Normally this is done via the <ebComscore:init /> taglib, which add a number of common parameters
* to the ebComscore object, including comscoreId, comscoreSite, comscoreVsite, comscoreSubVsite and comscoreString.
* Alternatively, call ebComscore.initializeComscore() and supply the needed parameters.
*
* 2. (optional) Track page view
* Often done via the <ebComscore:trackPageView /> taglib function, which generates both javascript
* and a <noscript><img> for browsers with javascript disabled.
* Alternatively, call ebComscore.sendComscorePageview() directly, and remember to handle browsers with javascript disabled.
*
* 3. (optional) Track event
* Can be done via the <ebComscore:jsForTrackingEvent /> taglib function, which generates javascript to put
* in ex. an onclick/onsubmit event handler.
* Alternatively, call one of the comscoreEvent() or ebComscore.sendComscoreNonPageview() javascript functions directly.
* Both options offer the opportunity to send along custom parameters to comscore.
*
* 4. (mandatory) Include comscore js before </body>
* Normally this is done via the <ebComscore:bottom /> taglig, right before the end </body> tag.
* Alternatively, manually include http://b.scorecardresearch.com/c2/16785783/cs.js at the end of the body.
*/
(function(window) {
/**
* The udm_ function is the communication crux of comscore – don't mess with it unless you have
* special permission from the lords on high.
*/
var udm_ = function(e){var t="comScore=",n=document,r=n.cookie,i="",s="indexOf",o="substring",u="length",a=2048,f,l="&ns_",c="&",h,p,d,v,m=window,g=m.encodeURIComponent||escape;if(r[s](t)+1)for(d=0,p=r.split(";"),v=p[u];d<v;d++)h=p[d][s](t),h+1&&(i=c+unescape(p[d][o](h+t[u])));e+=l+"_t="+ +(new Date)+l+"c="+(n.characterSet||n.defaultCharset||"")+"&c8="+g(n.title)+i+"&c7="+g(n.URL)+"&c9="+g(n.referrer),e[u]>a&&e[s](c)>0&&(f=e[o](0,a-8).lastIndexOf(c),e=(e[o](0,f)+l+"cut="+g(e[o](f+1)))[o](0,a)),n.images?(h=new Image,m.ns_p||(ns_p=h),h.src=e):n.write("<","p","><",'img src="',e,'" height="1" width="1" alt="*"',"><","/p",">")};
/**
* Holds the comscore configuration, and common comscore parameters that should be included
* on all events (in comscoreString).
* Must be initialized before sending any events.
*/
var ebComscore = ebComscore || {
comscoreString : '',
onclickString : '',
eventSent : false,
eventsAdded : false,
didScroll : false,
staybeating : false,
scrollListenerTime : 150,
pulseTime : 2000,
flatline : 180000
};
if (typeof ebComscore.initializeComscore === 'undefined') {
/**
* Configured comscore with the 4 required parameters
* @param comscoreId
* @param comscoreSite
* @param comscoreVSite
* @param comscoreSubVSite
* @param params An object/map of additional parameters that should be sent to comscore on all events
*/
ebComscore.initializeComscore = function(comscoreId, comscoreSite, comscoreVSite, comscoreSubVSite, params) {
try {
ebComscore.comscoreString += "c1" + '=' + encodeURIComponent( "2") + '&';
ebComscore.comscoreString += "c2" + '=' + encodeURIComponent( comscoreId) + '&';
ebComscore.comscoreString += "ns_site" + '=' + encodeURIComponent(comscoreSite) + '&';
ebComscore.comscoreString += "ns_vsite" + '=' + encodeURIComponent(comscoreVSite) + '&';
ebComscore.comscoreString += "ns_sub_vsite" + '=' + encodeURIComponent(comscoreSubVSite) + '&';
if (typeof params !== 'undefined') {
for (var key in params) {
if(params[key] !== '') {
ebComscore.comscoreString += key + '=' + encodeURIComponent(params[key]) + '&';
}
}
}
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.sendComscorePageview === 'undefined') {
/**
* This method will send a pageview event to comscore.
* It accepts a map of key-value parameters to send along.
*
* Additionally, any data previously stored in ebComscore.comscoreString will
* be appended.
*
* Remember to include 'name' and 'category' parameters, if they haven't already been set
* upon initialization.
*/
ebComscore.sendComscorePageview = function(params) {
try {
var comscoreStr = '';
if (typeof params !== 'undefined') {
for (var key in params) {
if(params[key] !== '') {
comscoreStr += key + '=' + encodeURIComponent(params[key]) + '&';
}
}
}
ebComscore.sendComscore(comscoreStr);
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.sendComscoreNonPageview === 'undefined') {
/**
* This method will send a non-pageview event (ns_type=hidden) to comscore.
* It accepts a map of key-value parameters to send along.
*
* Additionally, any data previously stored in ebComscore.comscoreString will
* be appended.
*
* Remember to include 'name' and 'category' parameters, if they haven't already been set
* upon initialization.
*
* Note that this (simple) method doesn't properly handle events that navigates away from
* the current page on mobile browsers. Use comscoreEvent() instead.
*/
ebComscore.sendComscoreNonPageview = function(params) {
try {
var comscoreStr = 'ns_type=hidden&';
if (typeof params !== 'undefined') {
for (var key in params) {
if(params[key] !== '') {
comscoreStr += key + '=' + encodeURIComponent(params[key]) + '&';
}
}
}
ebComscore.sendComscore(comscoreStr);
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.comscoreBaseUrl === 'undefined') {
ebComscore.comscoreBaseUrl = function() {
return 'http'+(document.location.href.charAt(4)=='s'?'s://sb':'://b')+'.scorecardresearch.com/b?';
}
}
if (typeof ebComscore.sendComscore === 'undefined') {
/**
* The main method for sending data to comscore.
* It accepts a URL encoded parameter string of keys/values to send, and will
* also append any data previously stored in ebComscore.comscoreString (see the comscoreEvent() function)
*
* @param str A parameter string to send to comscore (format: &key1=val1&key2=val2)
*/
ebComscore.sendComscore = function(str) {
try {
var finalStr = str || '';
finalStr += ebComscore.comscoreString;
//console.log(':sendComscore__ ',finalStr);
//udm_('http'+(document.location.href.charAt(4)==='s'?'s://sb':'://b')+'.scorecardresearch.com/b?' + finalStr);
var firefox = /Firefox[\/\s](\d+)/.test(navigator.userAgent);
if ( firefox ) {
setTimeout(function () {
udm_( ebComscore.comscoreBaseUrl() + finalStr);
}, 0);
} else {
udm_( ebComscore.comscoreBaseUrl() + finalStr);
}
localStorage.setItem('ebComscoreString', finalStr);
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.addEvent === 'undefined') {
ebComscore.addEvent = function(elem, event, fn) {
try {
if (elem.addEventListener) {
elem.addEventListener(event, fn, false);
} else {
elem.attachEvent("on" + event, function() {
try {
// set the this pointer same as addEventListener when fn is called
return(fn.call(elem, window.event));
} catch (err) {
console.error(err);
}
});
}
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.getBannerInfo === 'undefined') {
ebComscore.getBannerInfo = function(e) {
try {
var eventString = '';
var b = ebComscore.banners,
bl = ebComscore.banners.length;
for (var i = bl;i--;) {
var obj = b[i];
var inViewTime = (obj.beating === true) ? (new Date().getTime() - obj.firstSign) : obj.ebInViewTime;
eventString += (obj.ebScrollDepth !== 0) ? 'l_scrdep_' + obj.campaignId + '=' + obj.ebScrollDepth + '&' : '';
eventString += (inViewTime !== 0) ? 'l_rdtime_' + obj.campaignId + '=' + inViewTime + '&' : '';
eventString += (inViewTime !== 0 || obj.ebScrollDepth !== 0) ? 'l_bheight_' + obj.campaignId + '=' + obj.bannerheight + '&' : '';
}
return eventString;
} catch (err) {
return '';
console.error(err);
}
}
}
if (typeof ebComscore.setCookie === 'undefined') {
ebComscore.setCookie = function(cname, cvalue, exdays) {
console.log(cname, cvalue, exdays);
try {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+d.toUTCString();
document.cookie = cname + "=" + cvalue + "; " + expires;
} catch (err) {
console.log(err);
}
}
ebComscore.getCookie = function(cname) {
try {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) != -1) return c.substring(name.length,c.length);
}
return "";
} catch (err) {
console.log(err);
}
}
}
if (typeof ebComscore.finalAssembly === 'undefined') {
/**
* This method is normally called on the beforeunload/pagehide/unload page lifecycle events,
* to send any deferred events right before navigating away from the current page.
*
* It uses anything stored in ebComscore.onclickString, and sends it to comscore (see the comscoreEvent() function).
* Additional parameters stored in ebComscore.banners are also appended.
*
* The ebComscore.eventSent flag is used to avoid transmitting the same event data multiple times.
*/
ebComscore.finalAssembly = function(e, str) {
try {
var assembledStr = str || ebComscore.onclickString; //ebComscoreOnclickString;
//e.preventDefault();
//console.log('ebComscore.finalAssembly starting with : ', str);
//console.log(assembledStr);
if (ebComscore.eventSent === false) {
var shouldSend = ((e.type === 'beforeunload' || e.type === 'pagehide' || e.type === 'unload') && assembledStr !== '') || assembledStr !== '';
if (typeof ebComscore.banners !== 'undefined') {
var bannerstring = ebComscore.getBannerInfo();
if (bannerstring !== '') {
assembledStr += bannerstring;
assembledStr += 'ns_type=hidden&';
shouldSend = true;
}
}
//console.log('shouldSend ',shouldSend);
if (shouldSend) {
ebComscore.eventSent = true;
assembledStr += 'l_event=' + e.type + '&';
ebComscore.sendComscore(assembledStr);
}
}
} catch (err) {
console.error(err);
}
}
}
/** inview handling */
if (typeof ebComscore.scriller === 'undefined') {
ebComscore.scriller = function(e) {
try {
ebComscore.didScroll = true;
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.listener === 'undefined') {
ebComscore.listener = function() {
try {
ebComscore.resetBanners();
ebComscore.scrollListener = setInterval(function () {
try {
if (typeof ebComscore.banners !== 'undefined') {
if (ebComscore.didScroll === true) {
ebComscore.didScroll = false;
ebComscore.shouldListen();
}
}
} catch (err) {
console.error(err);
}
}, ebComscore.scrollListenerTime);
} catch (err) {
console.error(err);
}
}
ebComscore.shouldListen = function() {
try {
for (var i = ebComscore.banners.length;i--;) {
var item = ebComscore.banners[i],
el = item.banner,
rectTop = el.getBoundingClientRect().top;
if (rectTop < item.startPos && rectTop > item.endPos) {
if (item.beating === false) {
ebComscore.heartBeat(item, 'start');
}
} else {
if (item.beating === true) {
ebComscore.heartBeat(item, 'stop');
}
}
if (rectTop < ebComscore.viewPort && item.drowned === false) {
ebComscore.setScrollDepth(item, rectTop);
}
}
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.resetBanners === 'undefined') {
ebComscore.resizer = function() {
try {
ebComscore.didResize = true;
} catch (err) {
console.error(err);
}
}
ebComscore.resizeListener = setInterval(function () {
try {
if (typeof ebComscore.banners !== 'undefined') {
if (ebComscore.didResize === true) {
ebComscore.didResize = false;
ebComscore.resetBanners();
}
} else {
clearInterval(ebComscore.resizeListener);
}
} catch (err) {
console.error(err);
}
}, ebComscore.scrollListenerTime);
ebComscore.resetBanners = function() {
try {
if (typeof ebComscore.banners !== 'undefined') {
var b = ebComscore.banners,
bl = ebComscore.banners.length;
for (var i = bl;i--;) {
var item = b[i];
item.bannerheight = item.banner.clientHeight;
item.whereYouWere = item.banner.getBoundingClientRect().top;
item.testForPos = (item.whereYouWere <= -item.bannerheight);
item.startPos = Math.round(ebComscore.viewPort - (item.bannerheight / 2));
item.endPos = Math.round(-(item.bannerheight / 2));
}
ebComscore.shouldListen();
}
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.setScrollDepth === 'undefined') {
ebComscore.setScrollDepth = function(obj, curTop) {
try {
var level = (ebComscore.viewPort - Math.round(curTop)),
score = (level/obj.bannerheight) * 100;
if (obj.testForPos === false) {
if (score > 25 && score < 50) {
depth = 25;
} else if (score > 50 && score < 75) {
depth = 50;
} else if (score > 75 && score < 100) {
depth = 75;
} else if (score >= 100) {
depth = 100;
obj.drowned = true;
} else {
depth = 0;
}
//console.log('depth ', depth);
obj.ebScrollDepth = depth;
} else {
//console.log('should be safe now');
obj.testForPos = false;
}
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.heartBeat === 'undefined') {
ebComscore.heartBeat = function(obj, life) {
try {
if (life === 'start') {
//console.log('start');
if (obj.resurrect === true) {
obj.firstView = obj.ebInViewTime;
//console.log('... as zombie ', obj.ebInViewTime);
}
obj.firstSign = new Date().getTime();
pulse = 0;
if (ebComscore.staybeating) {
obj.beat = setInterval(function() {
try {
//obj.ebInViewTime = new Date().getTime() - obj.firstSign;
obj.ebInViewTime = obj.ebInViewTime + ebComscore.pulseTime;
pulse++;
//console.log('we got pulse', obj.ebInViewTime);
if ((pulse*2000) === ebComscore.flatline) {
//console.log('and we are clear ', obj.ebInViewTime);
clearInterval(obj.beat);
}
} catch (err) {
console.error(err);
}
}, ebComscore.pulseTime);
}
obj.beating = true;
} else if (life === 'stop') {
if (ebComscore.staybeating) {
clearInterval(obj.beat);
}
rightNow = new Date().getTime() - obj.firstSign;
obj.ebInViewTime = (obj.resurrect) ? obj.firstView + rightNow : rightNow;
//console.log('cutoff ', obj.ebInViewTime, rightNow);
obj.beating = false;
obj.resurrect = true;
}
} catch (err) {
console.error(err);
}
}
}
ebComscore.addToComscoreScroll = function(options) {
//console.log('addToComscoreScroll', options);
try {
this.init(options);
} catch (err) {
console.error(err);
}
};
ebComscore.addToComscoreScroll.prototype = {
init : function(options) {
try {
var self = this;
ebComscore.banners = ebComscore.banners || [];
ebComscore.viewPort = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
options.banner = document.getElementById(options.elid);
options.scrollDepth = 0;
options.inViewTime = 0;
options.ebScrollDepth = 0;
options.drowned = false;
options.ebInViewTime = 0;
options.beating = false;
self.obj = options;
ebComscore.banners.push(options);
} catch (err) {
console.error(err);
}
}
}
/** /inview handling */
/** public functions */
if (typeof comscoreEvent === 'undefined') {
/**
* This method can be used to send event data to comscore.
* The parameter ns_type=hidden will be added automatically, to differentiate events from page-views.
* If the inpage argument is true, the event data will be transmitted to comscore
* immediately. If not, the parameters will be instead be saved in ebComscore.onclickString,
* so that it can be sent on the beforeunload/pagehide/unload lifecycle events. This is typically
* used on mobile browsers, since it appears to be more reliable way of tracking events on those.
*
*
* @param e The event that triggered the comscore tracking
* @param el ??
* @param obj A parameter object (map) with additional key/value data to send to comscore
* @param inpage Determines whether data will send immidiately, or attached to window lifecycle events
* @param custom An additional parameter string to append to the comscore call. URL format: &key1=val1&key2=val2
* @param setClickedExtraHead If true, a clickedEkstraHead cookie is set to true
* */
var comscoreEvent = function(e, el, obj, inpage, custom, setClickedExtraHead) {
try {
//e.preventDefault();
var comscoreStr = '';
comscoreStr += 'ns_type=hidden&';
if (typeof obj !== 'undefined') {
for (var key in obj) {
if(obj[key] !== '') {
comscoreStr += key + '=' + encodeURIComponent(obj[key]) + '&';
}
}
}
if (typeof custom !== 'undefined' && custom !== '') {
comscoreStr += encodeURIComponent(custom);
}
if (setClickedExtraHead === true) {
ebComscore.setCookie('clickedEkstraHead','true', 2);
}
if (inpage === false) {
comscoreStr += 'l_event_type=' + e.type + '&';
ebComscore.onclickString = comscoreStr;
//console.log(ebComscore.onclickString);
} else if (inpage === true) {
comscoreStr += 'l_event=' + e.type + '&';
comscoreStr += 'l_inpage=true&';
ebComscore.sendComscore(comscoreStr);
}
} catch (err) {
console.error(err);
}
}
var comscoreDrFrontEvent = function(e, el, obj) {
try {
//e.preventDefault();
var comscoreStr = '';
comscoreStr += 'ns_type=hidden&';
var found = false,
targetEl = e.target,
parEl = e.target.parentElement,
count = 0;
if (targetEl.className.indexOf('df-container') === -1 && targetEl.className.indexOf('articles') === -1 && targetEl.className.indexOf('EBDrFrontScalingInner') === -1) {
while (found === false) {
//console.log(parEl.id);
if (parEl.id === '') {
parEl = parEl.parentElement;
} else {
found = true;
}
count++;
if (count === 7) {
found = true;
}
}
var dfclass = parEl.className.split(' '),
dfcl = dfclass.length,
j = 0;
for (var i = dfcl; i--;) {
if (dfclass[i] !== '' && dfclass[i] !== 'article-extract' && dfclass[i] !== 'right') {
comscoreStr += 'le_df_class' + j + '=' + dfclass[i] + '&';
j++;
}
}
if (typeof obj !== 'undefined') {
for (var key in obj) {
if(obj[key] !== '') {
comscoreStr += key + '=' + encodeURIComponent(obj[key]) + '&';
}
}
}
//console.log(comscoreStr);
//ebComscore.finalAssembly(e, comscoreStr);
ebComscore.onclickString = comscoreStr;
}
} catch (err) {
console.error(err);
}
}
}
if (typeof searchComscore === 'undefined') {
var searchComscore = function(e, el, le_name, inputId, le_pos) {
try {
var comscoreStr = '',
inputEl = document.getElementById(inputId),
inputVal = inputEl.value;
comscoreStr += 'ns_type=hidden&';
comscoreStr += 'le_type=form&';
comscoreStr += 'le_name=' + encodeURIComponent(le_name) + '&';
comscoreStr += 'le_label=' + encodeURIComponent(inputVal) + '&';
if (typeof le_pos !== 'undefined' && le_pos !== '') {
comscoreStr += 'le_pos=' + encodeURIComponent(le_pos) + '&';
}
//ebComscore.finalAssembly(e, comscoreStr);
ebComscore.onclickString = comscoreStr;
} catch (err) {
console.error(err);
}
}
}
/** /public functions */
if (typeof ebComscore.attachLinkListeners === 'undefined') {
/**
* Use this method to attach event handlers to all hyperlinks with the 'trackclick' css class,
* to have them tracked via comscore.
*
* Events will be sent to comscore, with the hyperlink as the le_label
*
* Note that this will NOT work on links with target=_blank
*
* @param inpage If you are on a mobile browser, you should set inpage to false, to avoid problems with onclick handlers
*/
ebComscore.attachLinkListeners = function(inpage) {
try {
var links = document.querySelectorAll('a.trackclick');
var i=0;
for (i = 0; i < links.length; ++i) {
var elem = links[i];
ebComscore.addEvent(elem, "click", function(event) {
console.log("Link clicked: " + elem.href);
comscoreEvent(event, elem, { "le_label" : elem.href }, inpage);
});
console.log("attached click handler to " + elem);
}
} catch (err) {
console.error(err);
}
}
}
if (typeof ebComscore.menuTracking === 'undefined') {
ebComscore.menuClick = function(event) {
try {
var elem = event.target,
comscoreName = elem.getAttribute('data-comscorename'),
comscoreLabel = elem.innerHTML;
comscoreEvent(event, elem, { "le_name" : comscoreName, "le_label" : comscoreLabel }, false);
} catch (err) {
console.error('ebComscore.menuClick ', err);
}
}
ebComscore.menuTracking = function() {
try {
var mainnav = document.getElementById('mainnav'),
flyouts = document.getElementById('flyouts'),
fnSubWrapper = document.getElementById('fnSubWrapper');
if (mainnav !== null) {
ebComscore.addEvent(mainnav, "click", function(event) {
ebComscore.menuClick(event);
});
}
if (flyouts !== null) {
ebComscore.addEvent(flyouts, "click", function(event) {
ebComscore.menuClick(event);
});
}
if (fnSubWrapper !== null) {
ebComscore.addEvent(fnSubWrapper, "click", function(event) {
ebComscore.menuClick(event);
});
}
} catch (err) {
console.error('ebComscore.menuTracking ', err);
}
}
}
/**
* Adds event listeners for tracking various events.
* The beforeunload/pagehide/unload are used for (reliably) tracking events on mobile browsers.
*/
if (ebComscore.eventsAdded === false) {
ebComscore.addEvent(window, 'load', ebComscore.listener);
ebComscore.addEvent(window, 'scroll', ebComscore.scriller);
ebComscore.addEvent(window, 'resize', ebComscore.resizer);
ebComscore.addEvent(window, 'beforeunload', ebComscore.finalAssembly);
ebComscore.addEvent(window, 'pagehide', ebComscore.finalAssembly);
ebComscore.addEvent(window, 'unload', ebComscore.finalAssembly);
ebComscore.eventsAdded = true;
}
window.ebComscore = ebComscore;
}(window));