define('core/engagement/page',[ 'jquery', 'underscore', 'backbone', 'core/bus', 'core/utils', 'core/engagement/vendor/visibly', ], function ( $, _, Backbone, bus, utils, visibly ) { 'use strict'; /** * This module reports if the user is engaged on the current * page through an amalgamation of various HTML standards and * best guesses. * * Do not use this module for UX methods. We are intentionally * conservative when marking a user as engaged for reporting reasons. * * Inspiration from: * - https://chartbeat.com/public-release-methodology/ * - http://upworthy.github.io/2014/06/implementing-attention-minutes-part-1/ * - http://jsfiddle.net/zanes/mbGBr/ * - http://static.chartbeat.com/js/chartbeat.js * - https://github.com/serkanyersen/ifvisible.js */ // Exported Event object w/ initial defaults var exports = _.extend(Backbone.Events, { _initialized: false, isEngaged: false, resetTimeout: null, ACTIVE_TIMEOUT: 60 * 1000, }); exports._markNotEngaged = function () { if (!exports.isEngaged) return; exports.isEngaged = false; exports.trigger('change:isEngaged', exports.isEngaged, exports); }; exports._markIsEngaged = function () { // Restart the interaction timeout clock. if (exports.resetTimeout) exports.resetTimeout(); if (exports.isEngaged) return; exports.isEngaged = true; exports.trigger('change:isEngaged', exports.isEngaged, exports); }; exports.start = function () { if (exports._initialized) return; exports._initialized = true; // Page Visibility API // If the window is currently visible, mark the user as // engaged, just to start things off. If it's not, we'll // wait for an interaction event to fire first. if (!visibly.hidden()) exports._markIsEngaged(); // On mobile-like user agents all we need is the Page Visibility API. visibly.onVisible(exports._markIsEngaged); visibly.onHidden(exports._markNotEngaged); if (utils.isMobileUserAgent()) return; // On desktop-like user agents, we'll additionally use interaction events // and a timeout clock to more closely track if a user is still // engaged on the page. Each time we record an interaction on the page // we reset the clock. If the clock reaches it's`ACTIVE_TIMEOUT` time, we // mark the user as no longer engaged with the page (see `_markIsEngaged` method). exports.resetTimeout = _.debounce(exports._markNotEngaged, exports.ACTIVE_TIMEOUT); exports.resetTimeout(); // Bind into interaction events on the current page. $(window.document).on('mousemove keyup', exports._markIsEngaged); $(window).on('scroll', exports._markIsEngaged); // Then bind into interaction events from the parent page. exports.listenTo(bus.frame, 'window.mousemove window.scroll window.click', exports._markIsEngaged); }; exports.stop = function () { if (!exports._initialized) return; exports._initialized = false; // Clean up the resetTimeout method. exports.resetTimeout = null; // Turn off listeners before changing // the state to off. exports.stopListening(); exports.off(); visibly.visibleCallbacks = []; visibly.hiddenCallbacks = []; $(window.document).off('mousemove keyup', exports._markIsEngaged); $(window).off('scroll', exports._markIsEngaged); exports._markNotEngaged(); }; return exports; }); // https://c.disquscdn.com/next/next-core/core/engagement/page.js