UNPKG

@ra5mus/eb-comscore

Version:

Convenience wrapper for comscore functionality

725 lines (665 loc) 25.7 kB
/** * 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));