RBX.extend(RBX.utils, { throttle: function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : new Date().getTime(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = new Date().getTime(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; } }); RBX.extend(RBX.utils, (function() { function isBlockInView(top, left, bottom, right) { var viewportHeight = $window.height(); var viewportWidth = $window.width(); var viewportTop = $window.scrollTop(); var viewportBottom = viewportTop + viewportHeight; var viewportLeft = $(window).scrollLeft(); var viewportRight = viewportLeft + viewportWidth; var viewportThirdHeight = Math.ceil(viewportHeight * .33); var elementHeight = bottom - top; var elementWidth = right - left; var isViewableVerticle = false; var isViewableHorizontal = false; if (top >= viewportTop && bottom <= viewportBottom) { /* is in full view */ isViewableVerticle = true; } else if (top <= viewportTop && bottom >= viewportBottom) { /* bigger than viewport (can't see top or bottom) */ isViewableVerticle = true; } else if (top >= viewportTop && top <= (viewportBottom - elementHeight / 2)) { /* is in partial view (can see top half) */ isViewableVerticle = true; } else if (bottom <= viewportBottom && bottom >= (viewportTop + elementHeight / 2)) { /* is in partial view (can see bottom half) */ isViewableVerticle = true; } else if ( elementHeight >= viewportHeight ) { /* bigger than viewport */ if (bottom >= viewportBottom && top <= viewportBottom - viewportThirdHeight) { /* can see top */ isViewableVerticle = true; } else if (top <= viewportTop && bottom >= viewportTop + viewportThirdHeight) { /* can see bottom */ isViewableVerticle = true; } } if (isViewableVerticle) { if (left >= viewportLeft && right <= viewportRight) { /* is in full view */ isViewableHorizontal = true; } else if (left <= viewportLeft && right >= viewportRight) { /* bigger than viewport */ isViewableHorizontal = true; } else if (left >= viewportLeft && left <= (viewportRight - Math.ceil(elementWidth / 2))) { /* is in partial view (can see half width of content) */ isViewableVerticle = true; } else if (right <= viewportRight && right >= (viewportLeft + Math.ceil(elementWidth / 2))) { /* is in partial view (can see half width of content) */ isViewableVerticle = true; } } return (isViewableVerticle && isViewableHorizontal); } function isBlockInFullView(top, left, bottom, right) { var viewportHeight = $window.height(); var viewportWidth = $window.width(); var viewportTop = $window.scrollTop(); var viewportBottom = viewportTop + viewportHeight; var viewportLeft = $(window).scrollLeft(); var viewportRight = viewportLeft + viewportWidth; var viewportThirdHeight = Math.ceil(viewportHeight * .33); var elementWidth = right - left; var isViewableVerticle = false; var isViewableHorizontal = false; if (top >= viewportTop && bottom <= viewportBottom && left >= viewportLeft && right <= viewportRight) { /* is in full view */ return true; } } function isElementInView(elem) { var $elem = $(elem); var top = $elem.offset().top; var left = $elem.offset().left; var bottom = top + $elem.outerHeight(); var right = left + $elem.outerWidth(); return isBlockInView(top, left, bottom, right); } function isElementInFullView(elem) { var $elem = $(elem); var top = $elem.offset().top; var left = $elem.offset().left; var bottom = top + $elem.outerHeight(); var right = left + $elem.outerWidth(); return isBlockInFullView(top, left, bottom, right); } return { isElementInView: isElementInView, isElementInFullView: isElementInFullView, isBlockInView: isBlockInView, isBlockInFullView: isBlockInFullView }; })()); RBX.extend(RBX.utils,{lzstring:function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;ne;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;ie;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}()}); /* START: Objec.assign() polyfill */ if (typeof Object.assign != 'function') { Object.assign = function(target, varArgs) { // .length of function is 2 'use strict'; if (target == null) { // TypeError if undefined or null throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource != null) { // Skip over if undefined or null for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }; } /* END: Objec.assign() polyfill */ /* global googletag d*/ /* Copyright 2013 Michael Countis MIT License: http://opensource.org/licenses/MIT */ (function() { "use strict"; window.googletag = window.googletag || {}; window.googletag.cmd = window.googletag.cmd || []; googletag.cmd.push(function() { if ( googletag.hasOwnProperty("on") || googletag.hasOwnProperty("off") || googletag.hasOwnProperty("trigger") || googletag.hasOwnProperty("events") ) { return; } var old_log = googletag.debug_log.log, events = [], addEvent = function(name, id, match) { events.push({ name: name, id: id, match: match }); }; addEvent("gpt-google_js_loaded", 8, /Google service JS loaded/ig); addEvent("gpt-gpt_fetch", 46, /Fetching GPT implementation/ig); addEvent("gpt-gpt_fetched", 48, /GPT implementation fetched\./ig); addEvent("gpt-page_load_complete", 1, /Page load complete/ig); addEvent("gpt-queue_start", 31, /^Invoked queued function/ig); addEvent("gpt-service_add_slot", 40, /Associated ([\w]*) service with slot ([\/\w]*)/ig); addEvent("gpt-service_add_targeting", 88, /Setting targeting attribute ([\w]*) with value ([\w\W]*) for service ([\w]*)/ig); addEvent("gpt-service_collapse_containers_enable", 78, /Enabling collapsing of containers when there is no ad content/ig); addEvent("gpt-service_create", 35, /Created service: ([\w]*)/ig); addEvent("gpt-service_single_request_mode_enable", 63, /Using single request mode to fetch ads/ig); addEvent("gpt-slot_create", 2, /Created slot: ([\/\w]*)/ig); addEvent("gpt-slot_add_targeting", 17, /Setting targeting attribute ([\w]*) with value ([\w\W]*) for slot ([\/\w]*)/ig); addEvent("gpt-slot_fill", 50, /Calling fillslot/ig); addEvent("gpt-slot_fetch", 3, /Fetching ad for slot ([\/\w]*)/ig); addEvent("gpt-slot_receiving", 4, /Receiving ad for slot ([\/\w]*)/ig); addEvent("gpt-slot_render_delay", 53, /Delaying rendering of ad slot ([\/\w]*) pending loading of the GPT implementation/ig); addEvent("gpt-slot_rendering", 5, /^Rendering ad for slot ([\/\w]*)/ig); addEvent("gpt-slot_rendered", 6, /Completed rendering ad for slot ([\/\w]*)/ig); googletag.events = googletag.events || {}; googletag.on = function(events, op_arg0 /* data*/, op_arg1 /* callback*/) { if (!op_arg0) { return this; } events = events.split(" "); var data = op_arg1 ? op_arg0 : undefined, callback = op_arg1 || op_arg0, ei = 0, e = ""; callback.data = data; for (e = events[(ei = 0)]; ei < events.length; e = events[++ei]) { (this.events[e] = this.events[e] || []).push(callback); } return this; }; googletag.off = function(events, handler) { events = events.split(" "); var ei = 0, e = "", fi = 0, f = function() {}; for (e = events[ei]; ei < events.length; e = events[++ei]) { if (!this.events.hasOwnProperty(e)) { continue; } if (!handler) { delete this.events[e]; continue; } fi = this.events[e].length - 1; for (f = this.events[e][fi]; fi >= 0; f = this.events[e][--fi]) { if (f === handler) { this.events[e].splice(fi, 1); } } if (this.events[e].length === 0) { delete this.events[e]; } } return this; }; googletag.trigger = function(event, parameters) { if (!this.events[event] || this.events[event].length === 0) { return this; } var parameters = parameters || [], fi = 0, f = this.events[event][fi]; for (fi, f; fi < this.events[event].length; f = this.events[event][++fi]) { if (f.apply(this, [{ data: f.data }].concat(parameters)) === false) { break; } } return this; }; googletag.debug_log.log = function(level, message, service, slot, reference) { if (message && message.getMessageId && typeof message.getMessageId() === "number") { var args = Array.prototype.slice.call(arguments), e = 0; for (e; e < events.length; e++) { if (events[e].id === message.getMessageId()) { googletag.trigger(events[e].name, args); } } } return old_log.apply(this, arguments); }; }); })(); /* START: performance.now() polyfill */ (function() { if ("performance" in window === false) { window.performance = {}; } Date.now = Date.now || function() { /* thanks IE8 */ return new Date().getTime(); }; if ("now" in window.performance == false) { var nowOffset = Date.now(); if (performance.timing && performance.timing.navigationStart) { nowOffset = performance.timing.navigationStart; } window.performance.now = function now() { return Date.now() - nowOffset; }; } })(); /* END: performance.now() polyfill */ /* START: googletag event listeners */ (function() { var timers = { fetched: {}, rendered: {} }; googletag.cmd.push(function() { var $window = $(window); var lastScrollTop = $window.scrollTop(); var scrollingDown = true; function updateScrollStatus(){ var currentScrollTop = $window.scrollTop(); if(currentScrollTop !== lastScrollTop) { var scrollOffset = currentScrollTop - lastScrollTop; if (scrollOffset >= 100 || scrollOffset <= -100) { if (currentScrollTop < lastScrollTop) { scrollingDown = false; } else { scrollingDown = true; } lastScrollTop = currentScrollTop; } } } setInterval(updateScrollStatus, 5000); googletag.on("gpt-slot_fetch", function(data, level, message, service, slot, reference) { updateScrollStatus(); RBX.debug("ad.dfp: gpt-slot_fetch", slot.getSlotElementId()); timers.fetched[slot.getSlotElementId()] = performance.now(); delete timers.rendered[slot.getSlotElementId()]; if (!slot.custom) return; slot.custom.getModule().removeClass("ad--cleared"); slot.custom.getModule().addClass("ad--fetched"); slot.custom.fetched = performance.now(); }); googletag.on("gpt-slot_rendered", function(data, level, message, service, slot, reference) { updateScrollStatus(); timers.rendered[slot.getSlotElementId()] = performance.now(); var $iframe = $(document.getElementById(slot.getSlotElementId())).find("iframe[height]"); if ($iframe.attr("height")) { $iframe.height($iframe.attr("height")); } }); googletag.pubads().addEventListener("slotRenderEnded", function(event) { RBX.debug( "ad.dfp: gpt-slotRenderEnded:", event.slot.getSlotElementId(), (performance.now() - timers.fetched[event.slot.getSlotElementId()]) / 1000 ); if (!event.slot.custom) return; event.slot.custom.rendered = performance.now(); event.slot.custom.renderedDirection = scrollingDown ? 1 : -1; event.slot.custom.nofill = false; if (scrollingDown && $window.scrollTop() > event.slot.custom.getModule().offset().top) { event.slot.custom.renderedLate = true; } else if (!scrollingDown && event.slot.custom.getModule().offset().top > $window.scrollTop() + $window.height() - (event.slot.custom.getModule().height() / 2)) { event.slot.custom.renderedLate = true; } else { event.slot.custom.renderedLate = false; } event.slot.custom.getModule().addClass("ad--rendered"); event.slot.custom.getModule().find(" > [id]:first").height(""); var isNofill = false; var nofillLineItems = [4359300433]; if ( event.slot.getResponseInformation() && event.slot.getResponseInformation().advertiserId === 38883126 /* multiply */ && nofillLineItems.indexOf(event.slot.getResponseInformation().lineItemId) !== -1 ) { isNofill = true; } if (!event.slot.getResponseInformation() || isNofill) { event.slot.custom.nofill = true; event.slot.custom.getModule().addClass("ad--nofill"); if (event.slot.custom.getModule().offset().top >= $window.scrollTop() + ($window.height() * .8)) { event.slot.custom.getModule().addClass("ad--nofill-collapse"); } } RBX.modules.ad.Manager.logStats('rendered', event.slot); if (event.slot.custom.refreshed) { RBX.modules.ad.Manager.logStats('refreshed'); } }); googletag.pubads().addEventListener("impressionViewable", function(event) { updateScrollStatus(); RBX.debug( "ad.dfp: gpt-impressionViewable:", event.slot.getSlotElementId(), (performance.now() - timers.rendered[event.slot.getSlotElementId()]) / 1000 ); if (!event.slot.custom) return; event.slot.custom.viewed = performance.now(); event.slot.custom.getModule().addClass("ad--viewed"); event.slot.custom.viewedDirection = scrollingDown ? 1 : -1; RBX.modules.ad.Manager.logStats('viewed', event.slot); }); }); })(); /* END: googletag event listeners */ var AppContentData = AppContentData || (function() { var moduleName = 'js_app_contentdata-0'; var ContentDataController = function() { var pageData = __page_data__ || {}; var data = pageData[moduleName]; var actionHandlers = function() { return { 'contentData::getCategories': this.getCategories, 'contentData::getTitle': this.getTitle }; }.bind(this); var bindActionHandlers = function() { actionHandlers = actionHandlers(); var keys = Object.keys(actionHandlers); for (var i = 0; i < keys.length; i++) { var action = keys[i]; var handler = actionHandlers[keys[i]]; $(document).on(action, handler); } }; this.initialize = function() { bindActionHandlers(); }; // Returns Wiki style categories for all content. this.getCategories = function(event, payload) { payload = payload || {}; if (!data.categories) { return undefined; } var categories = { category: data.categories.category, category_id: data.categories.category_id, subcategory: data.categories.subcategory, subcategory_id: data.categories.subcategory_id }; if (!!payload.done) { payload.done.call(payload.done, categories); } return categories; }.bind(this); this.getTitle = function(event, payload) { payload = payload || {}; if (!data.title) { return undefined; } if (!!payload.done) { payload.done.call(payload.done, data.title); } return data.title; }.bind(this); this.getCategoryIDArray = function(event, payload) { payload = payload || {}; var categoryArray = []; if (!!payload.done) { payload.done.call(payload.done, categories); } // Wiki questions have page_var with all categories, not just top two if (data.categories.question_categories) { return data.categories.question_categories.split(',').map(Number); } if (data.categories.category_id) { categoryArray.push(Number(data.categories.category_id)); } if (data.categories.subcategory_id) { categoryArray.push(Number(data.categories.subcategory_id)); } return categoryArray; }.bind(this); }; var controller = new ContentDataController(); controller.initialize(); return { getCategories: function(payload) { return controller.getCategories(null, payload); }, getCategoryIDArray: function(payload) { return controller.getCategoryIDArray(null, payload); }, getTitle: function(payload) { return controller.getTitle(null, payload); } }; })(); (function() { $window = $(window); var config = RBX.modules.ad.config; googletag.cmd.push(function() { /* capture out of process ads */ googletag.on("gpt-slot_create", function(data, level, message, service, slot, reference) { if (slot && slot.getSlotElementId && !RBX.modules.ad.Manager.get(slot.getSlotElementId())) { RBX.modules.ad.Manager.add(slot); } }); if (config.enable_sandboxing && ! (config.force_safeframe && config.force_sandboxing)) { googletag.on("gpt-slot_receiving", function(data, level, message, service, slot, reference) { try{ var isPrebid = false; for (var k in slot) { try{ if (slot[k]._advertiser_ids_) { /* 29989806 = DFP company id for Prebid */ if (slot[k]._advertiser_ids_.indexOf(29989806) !== -1) { isPrebid = true; break; } } }catch(e){} } if (isPrebid) { slot.setSafeFrameConfig({sandbox:true}); } else { slot.setSafeFrameConfig({}); } }catch(e){} }); } if (config.force_safeframe) { googletag.pubads().setForceSafeFrame(true); if (config.force_sandboxing) { googletag.pubads().setSafeFrameConfig({ sandbox: true }); } } try { googletag.pubads().setTargeting("guteref", "1"); } catch (e) {} try { googletag.pubads().setTargeting("is_holiday", AppContentData.hasHolidayCategory() ? "true" : "false"); } catch (e) {} try { if (AppContentData.getCategoryIDArray().length) { googletag.pubads().setTargeting("category", AppContentData.getCategoryIDArray()); } } catch (e) {} }); (function() { var screenHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; var pageHeight = screenHeight * 3; var $window = $(window); var lastPageTracked = Math.floor($window.scrollTop() / pageHeight) + 1; googletag.cmd.push(function() { googletag.pubads().setTargeting("guteref", String(lastPageTracked)); }); var onScroll = RBX.utils.throttle( function() { var page = Math.floor($window.scrollTop() / pageHeight) + 1; var bottomPage = Math.floor(($window.scrollTop() + screenHeight * 0.75) / pageHeight) + 1; if (page === bottomPage && page !== lastPageTracked) { RBX.modules.ad.changeCorrelator = true; lastPageTracked = page; RBX.debug("adtech: ", "page: " + page); googletag.cmd.push(function() { googletag.pubads().setTargeting("guteref", String(page)); }); try { if (typeof pv !== "undefined") { pv.t(); } } catch (e) {} } }, 1000, { leading: true, trailing: true } ); $(document).on("scroll", onScroll); })(); if(config.dock_short_ads === 'yes-fixed' || config.dock_short_ads === 'yes-translate') { var dockDistance = $window.height() * .2; if(config.dock_short_ads === 'yes-fixed') { // remove translate3d hack on #content-wrap, it breaks position:fixed $(function(){ $('#content-wrap').css({'transform':'none'}); }); } $window.scroll(RBX.utils.throttle( function() { var $header = $('header:first'); var topPos = $window.scrollTop() + $header.outerHeight(); var $ads = $('.module.content_body .module.ad_unit'); $ads.css({'z-index':5}); $ads.each(function() { var $ad = $(this); var $adTarget = $ad.find('.ad_unit_target'); if ($ad.is('.ad--rendered') && !$ad.is('.ad--nofill') && $adTarget.height() <= 90 && $ad.offset().top < topPos && $ad.offset().top > topPos - dockDistance) { if(!$ad.is('.ad--viewed') || ($adTarget.attr('style') || '').match(/transform/)) { $ad.addClass('js--docked'); if(config.dock_short_ads === 'yes-fixed') { $ad.height($ad.height()); $adTarget.stop(true).css({'transition':'none','background':'white','transform':'translate(0,0)','position':'fixed','top':($header.outerHeight() - 5),'left':0,'right':0}); } else if(config.dock_short_ads === 'yes-translate') { $adTarget.stop(true).css({'transition':'none','background':'white','transform':'translate3d(0, '+(topPos - $ad.offset().top - 5)+'px, 0)'}); } return; } } if ($ad.is('.js--docked')) { if(config.dock_short_ads === 'yes-fixed'){ $adTarget.stop(true).animate({'top':-$adTarget.height()}, 1000, function(){ $adTarget.stop(true).css({'transform':'','position':'','top':'','left':'','right':''}); }); }else{ $adTarget.stop(true).css({'transition':'all 2s','transform':'','position':'','top':'','left':'','right':''}); } $ad.removeClass('js--docked'); } }); }, 100, { leading: true, trailing: true } )); } })(); RBX.modules.ad_unit_injector = (function() { window._gaq = window._gaq || []; var defaults = { adunit: "", sizes: [] }; var baseTemplate = '
'; function AdUnitInjector(instance_config) { var config = $.extend({}, defaults, instance_config || {}); var template = baseTemplate; try{ if (config.config_override_js_var) { var config_override = window[config.config_override_js_var]; config.overrides = {}; for(var k in config_override){ config.overrides[k] = { original: config[k], override: config_override[k] }; config[k] = config_override[k]; } } }catch(e){} var screenHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; if (typeof config.ad_distance !== "number") { if (parseInt(config.ad_distance)) { if (config.ad_distance.search && config.ad_distance.search("vh") !== -1) { config.ad_distance = screenHeight * (parseInt(config.ad_distance) / 100); } else { config.ad_distance = parseInt(config.ad_distance); } } else { /* default */ config.ad_distance = screenHeight * 0.75; } } config.ad_distance = Math.floor(config.ad_distance); if(config.ad_distance_mutator && config.ad_distance_mutator.multiplier){ config.ad_distance_mutator.enabled = true; config.ad_distance_mutator.ad_distance = config.ad_distance; config.ad_distance = config.ad_distance * config.ad_distance_mutator.multiplier; config.ad_distance_mutator.increment = Math.floor((config.ad_distance_mutator.ad_distance - config.ad_distance) / config.ad_distance_mutator.ad_count); }else{ config.ad_distance_mutator.enabled = false; } RBX.debug("ad.unit.injector: config", config); if (!config.adunit || !config.sizes.length) { RBX.error("ad_unit_injector missing adunit or sizes", config); return false; } var $parents = $(config.parent_selector).not(".js--ad-injector-root"); $parents.addClass('js--ad-injector-root js--injecting'); if (!$parents.length) { if (!config.queued) { RBX.warn("ad.unit.injector: parent not found, queueing init for DOMContentLoaded.", "(" + config.parent_selector + ")"); setTimeout(function() { $(function() { config.queued = true; new AdUnitInjector(config); }); }, 10); } else { RBX.warn("ad.unit.injector: parent not found.", "(" + config.parent_selector + ")"); } return; } $parents.each(function() { var $parent = $(this); var adMetrics = $parent.attr('data-rbx-am'); var $sections = $parent.find(config.section_selector); addClasses($parent, $sections); splitLongText($parent, $sections); $sections = $parent.find(".js--section"); if (!config.use_viewability_metrics || !adMetrics) { injectAds($parent, $sections); } else { adMetrics = JSON.parse(adMetrics); var adCount = 0; var $sectionsWithMetrics = $sections.filter('[data-rbx-am]'); if ($sectionsWithMetrics.length) { var sectionMetrics = []; var metricsTotal = 0; $sectionsWithMetrics.each(function(){ var metrics = $(this).attr('data-rbx-am'); if (metrics) { metrics = JSON.parse(metrics); if (metrics.v) { var v = parseInt(metrics.v); metricsTotal += v; v = v - (v % 5); sectionMetrics.push(v); } } }); var eventData = { "has_metrics": parseInt($sectionsWithMetrics.length / $sections.length * 100) / 100, "avg_metric": parseInt(metricsTotal / $sectionsWithMetrics.length) / 100 }; _gaq.push(['_trackEvent', 'Ad Tech Experiments', 'used_viewability_metrics', JSON.stringify(eventData)]); RBX.debug('ad.unit.injector: Ad Tech Experiments, used_viewability_metrics', eventData); var viewabilityPercent = 95; while (viewabilityPercent >= 50 && RBX.modules.ad.Manager.getAllIds().length < adMetrics.acs) { if (sectionMetrics.indexOf(viewabilityPercent) !== -1) { injectAds($parent, $sections, viewabilityPercent); var newSectionMetrics = []; for(var i in sectionMetrics) { if (sectionMetrics[i] !== viewabilityPercent) { newSectionMetrics.push(sectionMetrics[i]); } } sectionMetrics = newSectionMetrics; } viewabilityPercent = viewabilityPercent - 5; } } if (adCount < adMetrics.acs) { injectAds($parent, $sections); } } }); $parents.removeClass('js--injecting'); function addClasses($parent, $sections) { $parent.addClass("js--ad-injector-root"); $sections.addClass("js--section"); $sections.has('.ecommad').addClass('ecom_ad'); config.section_is_media_selector && $sections.filter(config.section_is_media_selector).addClass("js--section-media"); config.section_is_heading_selector && $sections.filter(config.section_is_heading_selector).not(".js--section-media").addClass("js--section-heading"); config.section_is_text_selector && $sections.filter(config.section_is_text_selector).not(".js--section-media, .js--section-heading").addClass("js--section-text"); } function injectAds($parent, $sections, viewabilityPercent) { var validSectionClasses = ".js--section-" + config.enabled_section_types_for_injection.join(", .js--section-"); var sectionCount = 0; var adCount = 0; var distance = 0; var $lastSection = $sections.filter(":last"); var adIDs = []; $sections.filter('.js--section-media').each(function() { $section = $(this); var sectionHeight = $section.outerHeight(); // if media and smaller than 16:5 ratio, default to 16:9 height if (sectionHeight < parseInt($section.width() * 0.31)) { sectionHeight = parseInt($section.width() * 0.56); if (config.default_img_height_override) { sectionHeight = parseInt(Math.min(sectionHeight, config.ad_distance * .75)); } $section.css({minHeight:sectionHeight}); $section.addClass('js--reset-height'); } }); $sections.each(function() { sectionCount++; var $section = $(this); if ($section.is('.ad_unit, .ecom_ad')) { distance = 0; sectionCount = 0; return; } var adMetrics = $section.attr('data-rbx-am'); if (viewabilityPercent) { if (!adMetrics) { return; } else { adMetrics = JSON.parse(adMetrics); if (!adMetrics.v || adMetrics.v < viewabilityPercent) { return; } } } var sectionHeight = $section.outerHeight(); var maxMargin = Math.max(parseInt($section.css("marginTop")) || 0, parseInt($section.css("marginBottom")) || 0); if (isNaN(maxMargin)) { maxMargin = 0; } sectionHeight = sectionHeight + maxMargin; var sectionBottom = $section.offset().top + sectionHeight; var $prevAd = $section.prevAll('.ad_unit, .ecom_ad').first(); var $nextAd = $section.nextAll('.ad_unit, .ecom_ad').first(); var start = $parent.offset().top; var distanceFromAd = 999999; if ($prevAd.length) { start = $prevAd.offset().top + $prevAd.outerHeight() + Math.max(parseInt($prevAd.css("marginBottom")) || 0); distanceFromAd = sectionBottom - start; } distance = sectionBottom - start; if ($nextAd.length) { distanceFromAd = Math.max(0, Math.min($nextAd.offset().top - sectionBottom, distanceFromAd)); } var isValidSection = $section.is(validSectionClasses) || config.enabled_section_types_for_injection.indexOf("all") !== -1; if (isValidSection && config.invalid_for_injection_selector) { isValidSection = $section.is(config.invalid_for_injection_selector); } if (viewabilityPercent && $section.attr('data-rbx-am')) { isValidSection = true; } if (isValidSection) { if (adCount === 0 && config.sections_before_ads > 0 && sectionCount >= config.sections_before_ads) { isValidSection = true; } else if ((adCount > 0 || config.sections_before_ads === 0) && distance >= config.ad_distance) { isValidSection = true; }else{ isValidSection = false; } } if (isValidSection) { if (($prevAd.length || $nextAd.length) && distanceFromAd < config.ad_distance) { isValidSection = false; } } if (isValidSection) { var $module = $(template); var $ad = $module.find(".ad_unit_target"); $ad.addClass(config.size_class); $section.after($module); $ad.attr( "id", RBX.modules.ad.Manager.create(config.adunit, config.sizes, function(slot) { try { slot.custom.getModule(); } catch (e) {} }, config.slot_config) ); adIDs.push($ad.attr('id')); adCount++; distance = 0; sectionCount = 0; if (viewabilityPercent) { RBX.debug('ad.unit.injector: Optimized Placement >='+viewabilityPercent+'%', $ad[0]); } if(config.ad_distance_mutator.enabled){ RBX.debug('ad.unit.injector: ad_distance mutated', config.ad_distance, config.ad_distance_mutator); if(adCount < config.ad_distance_mutator.ad_count) { config.ad_distance += config.ad_distance_mutator.increment; }else{ config.ad_distance_mutator.enabled = false; config.ad_distance = config.ad_distance_mutator.ad_distance; RBX.debug('ad.unit.injector: ad_distance mutation done', config.ad_distance); } } } }); $sections.filter('.js--reset-height') .css({minHeight:''}) .removeClass('js--reset-height'); if(adIDs.length && !RBX.modules.ad.config.lazyload){ RBX.modules.ad.Manager.runAuction(adIDs, true); } } function splitLongText($parent, $sections) { if (config.split_long_text) { $sections.filter(".js--section-text").each(function() { var $section = $(this); var elHeight = $section.outerHeight(); if (elHeight > screenHeight * 0.33) { /* it's too big, is it simple enough to split? */ var children = $section.children(); if ($section.children().length === 1 && $section.find(" > p").length) { var $p = $section.find(" > p"); /* let's avoid splitting text with dom elements in them for now (links, spans, etc) */ if ($p.text() === $p.html()) { /* it is simple enough! so attempting to split */ var text = $.trim($p.text()); /* regex attempts to avoid bad sentence endings like ( 1. | Mr. | a. ) */ text = text.replace(/(\b[a-z]{2,}[\.\?\!]+)\s+/g, "$1"); var sentences = text.split(//g); var blocks = []; if (sentences.length >= 2) { var html = ""; /* split into two pieces, can make this smarter if we need to */ blocks.push(sentences.slice(0, Math.floor(sentences.length / 2))); blocks.push(sentences.slice(Math.floor(sentences.length / 2))); for (var i in blocks) { if (blocks[i].join(" ").length < 100) { /* split will be too small, skip this section */ return; } var $newSection = $section.clone(); $newSection.addClass("js--text-split"); var $newP = $("

").text(blocks[i].join(" ")); $newSection.html($newP); html += $newSection[0].outerHTML; } $section.replaceWith(html); } } } } }); } } } return { init: function(config) { new AdUnitInjector(config); } }; })();