1/*! 2 * @preserve 3 * jquery.scrolldepth.js | v0.9.1 4 * Copyright (c) 2016 Rob Flaherty (@robflaherty) 5 * Licensed under the MIT and GPL licenses. 6 */ 7 8/* Universal module definition */ 9/* jshint ignore:start */ 10(function(factory) { 11 if (typeof define === 'function' && define.amd) { 12 // AMD 13 define(['jquery'], factory); 14 } else if (typeof module === 'object' && module.exports) { 15 // CommonJS 16 module.exports = factory(require('jquery')); 17 } else { 18 // Browser globals 19 factory(jQuery); 20 } 21}(function($) { 22 23/* Scroll Depth */ 24 25 "use strict"; 26 27 var defaults = { 28 minHeight: 0, 29 elements: [], 30 percentage: true, 31 userTiming: false, 32 pixelDepth: true, 33 nonInteraction: true, 34 gaGlobal: false, 35 gtmOverride: false 36 }; 37 38 var $window = $(window), 39 cache = [], 40 scrollEventBound = false, 41 lastPixelDepth = 0, 42 universalGA, 43 classicGA, 44 gaGlobal, 45 standardEventHandler; 46 47 /* 48 * Plugin 49 */ 50 51 $.scrollDepth = function(options) { 52 53 var startTime = +new Date; 54 55 options = $.extend({}, defaults, options); 56 57 // Return early if document height is too small 58 if ( $(document).height() < options.minHeight ) { 59 return; 60 } 61 62 /* 63 * Determine which version of GA is being used 64 * "ga", "__gaTracker", _gaq", and "dataLayer" are the possible globals 65 */ 66 67 if (options.gaGlobal) { 68 universalGA = true; 69 gaGlobal = options.gaGlobal; 70 } else if (typeof ga === "function") { 71 universalGA = true; 72 gaGlobal = 'ga'; 73 } else if (typeof __gaTracker === "function") { 74 universalGA = true; 75 gaGlobal = '__gaTracker'; 76 } 77 78 if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") { 79 classicGA = true; 80 } 81 82 if (typeof options.eventHandler === "function") { 83 standardEventHandler = options.eventHandler; 84 } else if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function" && !options.gtmOverride) { 85 86 standardEventHandler = function(data) { 87 dataLayer.push(data); 88 }; 89 } 90 91 /* 92 * Functions 93 */ 94 95 function sendEvent(action, label, scrollDistance, timing) { 96 97 if (standardEventHandler) { 98 99 standardEventHandler({'event': 'ScrollDistance', 'eventCategory': 'Scroll Depth', 'eventAction': action, 'eventLabel': label, 'eventValue': 1, 'eventNonInteraction': options.nonInteraction, 'eventTiming': timing}); 100 101 if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) { 102 lastPixelDepth = scrollDistance; 103 standardEventHandler({'event': 'ScrollDistance', 'eventCategory': 'Scroll Depth', 'eventAction': 'Pixel Depth', 'eventLabel': rounded(scrollDistance), 'eventValue': 1, 'eventNonInteraction': options.nonInteraction, 'eventTiming': timing}); 104 } 105 106 if (options.userTiming && arguments.length > 3) { 107 standardEventHandler({'event': 'ScrollTiming', 'eventCategory': 'Scroll Depth', 'eventAction': action, 'eventLabel': label, 'eventTiming': timing}); 108 } 109 110 } else { 111 112 if (universalGA) { 113 114 window[gaGlobal]('send', 'event', 'Scroll Depth', action, label, 1, {'nonInteraction': options.nonInteraction}); 115 116 if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) { 117 lastPixelDepth = scrollDistance; 118 window[gaGlobal]('send', 'event', 'Scroll Depth', 'Pixel Depth', rounded(scrollDistance), 1, {'nonInteraction': options.nonInteraction}); 119 } 120 121 if (options.userTiming && arguments.length > 3) { 122 window[gaGlobal]('send', 'timing', 'Scroll Depth', action, timing, label); 123 } 124 125 } 126 127 if (classicGA) { 128 129 _gaq.push(['_trackEvent', 'Scroll Depth', action, label, 1, options.nonInteraction]); 130 131 if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) { 132 lastPixelDepth = scrollDistance;
vendor: 4,508 bytes, lines 133-292
133 _gaq.push(['_trackEvent', 'Scroll Depth', 'Pixel Depth', rounded(scrollDistance), 1, options.nonInteraction]); 134 } 135 136 if (options.userTiming && arguments.length > 3) { 137 _gaq.push(['_trackTiming', 'Scroll Depth', action, timing, label, 100]); 138 } 139 140 } 141 142 } 143 144 } 145 146 function calculateMarks(docHeight) { 147 return { 148 '25%' : parseInt(docHeight * 0.25, 10), 149 '50%' : parseInt(docHeight * 0.50, 10), 150 '75%' : parseInt(docHeight * 0.75, 10), 151 // Cushion to trigger 100% event in iOS 152 '100%': docHeight - 5 153 }; 154 } 155 156 function checkMarks(marks, scrollDistance, timing) { 157 // Check each active mark 158 $.each(marks, function(key, val) { 159 if ( $.inArray(key, cache) === -1 && scrollDistance >= val ) { 160 sendEvent('Percentage', key, scrollDistance, timing); 161 cache.push(key); 162 } 163 }); 164 } 165 166 function checkElements(elements, scrollDistance, timing) { 167 $.each(elements, function(index, elem) { 168 if ( $.inArray(elem, cache) === -1 && $(elem).length ) { 169 if ( scrollDistance >= $(elem).offset().top ) { 170 sendEvent('Elements', elem, scrollDistance, timing); 171 cache.push(elem); 172 } 173 } 174 }); 175 } 176 177 function rounded(scrollDistance) { 178 // Returns String 179 return (Math.floor(scrollDistance/250) * 250).toString(); 180 } 181 182 function init() { 183 bindScrollDepth(); 184 } 185 186 /* 187 * Public Methods 188 */ 189 190 // Reset Scroll Depth with the originally initialized options 191 $.scrollDepth.reset = function() { 192 cache = []; 193 lastPixelDepth = 0; 194 $window.off('scroll.scrollDepth'); 195 bindScrollDepth(); 196 }; 197 198 // Add DOM elements to be tracked 199 $.scrollDepth.addElements = function(elems) { 200 201 if (typeof elems == "undefined" || !$.isArray(elems)) { 202 return; 203 } 204 205 $.merge(options.elements, elems); 206 207 // If scroll event has been unbound from window, rebind 208 if (!scrollEventBound) { 209 bindScrollDepth(); 210 } 211 212 }; 213 214 // Remove DOM elements currently tracked 215 $.scrollDepth.removeElements = function(elems) { 216 217 if (typeof elems == "undefined" || !$.isArray(elems)) { 218 return; 219 } 220 221 $.each(elems, function(index, elem) { 222 223 var inElementsArray = $.inArray(elem, options.elements); 224 var inCacheArray = $.inArray(elem, cache); 225 226 if (inElementsArray != -1) { 227 options.elements.splice(inElementsArray, 1); 228 } 229 230 if (inCacheArray != -1) { 231 cache.splice(inCacheArray, 1); 232 } 233 234 }); 235 236 }; 237 238 /* 239 * Throttle function borrowed from: 240 * Underscore.js 1.5.2 241 * http://underscorejs.org 242 * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 243 * Underscore may be freely distributed under the MIT license. 244 */ 245 246 function throttle(func, wait) { 247 var context, args, result; 248 var timeout = null; 249 var previous = 0; 250 var later = function() { 251 previous = new Date; 252 timeout = null; 253 result = func.apply(context, args); 254 }; 255 return function() { 256 var now = new Date; 257 if (!previous) previous = now; 258 var remaining = wait - (now - previous); 259 context = this; 260 args = arguments; 261 if (remaining <= 0) { 262 clearTimeout(timeout); 263 timeout = null; 264 previous = now; 265 result = func.apply(context, args); 266 } else if (!timeout) { 267 timeout = setTimeout(later, remaining); 268 } 269 return result; 270 }; 271 } 272 273 /* 274 * Scroll Event 275 */ 276 277 function bindScrollDepth() { 278 279 scrollEventBound = true; 280 281 $window.on('scroll.scrollDepth', throttle(function() { 282 /* 283 * We calculate document and window height on each scroll event to 284 * account for dynamic DOM changes. 285 */ 286 287 var docHeight = $(document).height(), 288 winHeight = window.innerHeight ? window.innerHeight : $window.height(), 289 scrollDistance = $window.scrollTop() + winHeight, 290 291 // Recalculate percentage marks 292 marks = calculateMarks(docHeight),
293 294 // Timing 295 timing = +new Date - startTime; 296 297 // If all marks already hit, unbind scroll event 298 if (cache.length >= options.elements.length + (options.percentage ? 4:0)) { 299 $window.off('scroll.scrollDepth'); 300 scrollEventBound = false; 301 return; 302 } 303 304 // Check specified DOM elements 305 if (options.elements) { 306 checkElements(options.elements, scrollDistance, timing); 307 } 308 309 // Check standard marks 310 if (options.percentage) { 311 checkMarks(marks, scrollDistance, timing); 312 } 313 }, 500)); 314 315 } 316 317 init(); 318 319 }; 320 321})); 322jQuery(function() { 323 jQuery.scrollDepth(); 324}); 325/* jshint ignore:end */
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.