2016-05-16 20:32:11 +00:00
|
|
|
( function ( $, mw ) {
|
2016-10-17 21:30:32 +00:00
|
|
|
var dwellStartTime, perceivedWait;
|
|
|
|
|
2016-05-16 20:32:11 +00:00
|
|
|
/**
|
|
|
|
* Return data that will be logged with each EL request
|
|
|
|
*
|
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
function getDefaultValues() {
|
|
|
|
var defaults = {
|
|
|
|
pageTitleSource: mw.config.get( 'wgTitle' ),
|
|
|
|
namespaceIdSource: mw.config.get( 'wgNamespaceNumber' ),
|
|
|
|
pageIdSource: mw.config.get( 'wgArticleId' ),
|
|
|
|
isAnon: mw.user.isAnon(),
|
2016-07-19 10:52:27 +00:00
|
|
|
hovercardsSuppressedByGadget: false,
|
2016-05-16 20:32:11 +00:00
|
|
|
popupEnabled: mw.popups.getEnabledState(),
|
|
|
|
popupDelay: mw.popups.render.POPUP_DELAY,
|
|
|
|
pageToken: mw.user.generateRandomSessionId() +
|
2016-05-25 20:02:05 +00:00
|
|
|
Math.floor( mw.now() ).toString() +
|
|
|
|
mw.user.generateRandomSessionId(),
|
|
|
|
sessionToken: mw.user.sessionId(),
|
2016-05-16 20:32:11 +00:00
|
|
|
// arbitrary name that represents the current UI of the popups
|
|
|
|
version: 'legacy',
|
|
|
|
// current API version
|
|
|
|
api: 'mwapi'
|
|
|
|
};
|
|
|
|
|
|
|
|
// Include edit count bucket to the list of default values if the user is logged in.
|
|
|
|
if ( !mw.user.isAnon() ) {
|
|
|
|
defaults.editCountBucket = mw.popups.schemaPopups.getEditCountBucket(
|
|
|
|
mw.config.get( 'wgUserEditCount' ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return defaults;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the sampling rate for the Schema:Popups
|
|
|
|
*
|
|
|
|
* User's session ID is used to determine the eligibility for logging,
|
|
|
|
* thus the function will result the same outcome as long as the browser
|
|
|
|
* hasn't been restarted or the cookie hasn't been cleared.
|
|
|
|
*
|
|
|
|
* @return {number}
|
|
|
|
*/
|
|
|
|
function getSamplingRate() {
|
|
|
|
var bucket,
|
|
|
|
samplingRate = mw.config.get( 'wgPopupsSchemaPopupsSamplingRate', 0 );
|
|
|
|
|
|
|
|
if ( !$.isFunction( navigator.sendBeacon ) ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bucket = mw.experiments.getBucket( {
|
|
|
|
name: 'ext.popups.schemaPopus',
|
|
|
|
enabled: true,
|
|
|
|
buckets: {
|
|
|
|
control: 1 - samplingRate,
|
|
|
|
A: samplingRate
|
|
|
|
}
|
|
|
|
}, mw.user.sessionId() );
|
|
|
|
|
|
|
|
return bucket === 'A' ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return edit count bucket based on the number of edits
|
|
|
|
*
|
|
|
|
* @param {number} editCount
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
function getEditCountBucket( editCount ) {
|
|
|
|
var bucket;
|
|
|
|
|
|
|
|
if ( editCount === 0 ) {
|
|
|
|
bucket = '0';
|
|
|
|
} else if ( editCount >= 1 && editCount <= 4 ) {
|
|
|
|
bucket = '1-4';
|
|
|
|
} else if ( editCount >= 5 && editCount <= 99 ) {
|
|
|
|
bucket = '5-99';
|
|
|
|
} else if ( editCount >= 100 && editCount <= 999 ) {
|
|
|
|
bucket = '100-999';
|
|
|
|
} else if ( editCount >= 1000 ) {
|
|
|
|
bucket = '1000+';
|
|
|
|
}
|
|
|
|
|
|
|
|
return bucket + ' edits';
|
|
|
|
}
|
|
|
|
|
2016-10-17 21:30:32 +00:00
|
|
|
/**
|
|
|
|
* Checks whether the event signals the end of a hovercards lifecycle
|
|
|
|
*
|
|
|
|
* @param {string} action
|
|
|
|
* @return {boolean}
|
|
|
|
*/
|
|
|
|
function isFinalLifeCycleEvent( action ) {
|
|
|
|
return [ 'dwelledButAbandoned', 'opened in new window', 'dismissed',
|
|
|
|
'opened in new window', 'opened in same tab' ].indexOf( action ) > -1;
|
|
|
|
}
|
|
|
|
|
2016-05-16 20:32:11 +00:00
|
|
|
/**
|
|
|
|
* Return data after making some adjustments so that it's ready to be logged
|
2016-10-24 22:58:24 +00:00
|
|
|
* Returns false if the event should not be logged based on its contents or previous logged data
|
2016-05-16 20:32:11 +00:00
|
|
|
*
|
2016-10-24 22:58:24 +00:00
|
|
|
* @param {Object} data
|
|
|
|
* @param {Object} previousLogData
|
|
|
|
* @return {Object|boolean}
|
2016-05-16 20:32:11 +00:00
|
|
|
*/
|
2016-10-24 22:58:24 +00:00
|
|
|
function getMassagedData( data, previousLogData ) {
|
2016-10-17 20:52:08 +00:00
|
|
|
// We don't log hover and display events as they are not compatible with the schema
|
|
|
|
// but they are useful for debugging
|
|
|
|
var action = data.action;
|
2016-10-24 22:58:24 +00:00
|
|
|
|
2016-10-17 21:30:32 +00:00
|
|
|
if ( dwellStartTime ) {
|
|
|
|
// Calculate the perceived wait to show the hovercard (currently unused)
|
|
|
|
// or the time elapsed before the user abandoned their hover
|
|
|
|
if ( action === 'display' ) {
|
|
|
|
perceivedWait = Math.round( mw.now() - dwellStartTime );
|
|
|
|
} else {
|
|
|
|
if ( perceivedWait ) {
|
|
|
|
data.perceivedWait = perceivedWait;
|
|
|
|
}
|
|
|
|
data.totalInteractionTime = Math.round( mw.now() - dwellStartTime );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep track of dwell time - a hover event should always be the first event in the hovercard lifecycle
|
|
|
|
if ( !dwellStartTime && action === 'hover' ) {
|
|
|
|
dwellStartTime = mw.now();
|
|
|
|
perceivedWait = false;
|
|
|
|
} else if ( isFinalLifeCycleEvent( action ) ) {
|
|
|
|
// reset dwell start time to allow a new hover event to begin
|
|
|
|
dwellStartTime = false;
|
|
|
|
}
|
|
|
|
|
2016-10-17 20:52:08 +00:00
|
|
|
if ( action && [ 'hover', 'display' ].indexOf( action ) > -1 ) {
|
|
|
|
return false;
|
2016-10-24 22:58:24 +00:00
|
|
|
// Only one action is recorded per link interaction token...
|
2016-10-17 21:30:32 +00:00
|
|
|
} else if ( data.linkInteractionToken ) {
|
2016-10-24 22:58:24 +00:00
|
|
|
// however, the 'disabled' action takes two clicks by nature, so allow it
|
2016-10-17 21:30:32 +00:00
|
|
|
if ( previousLogData && data.linkInteractionToken === previousLogData.linkInteractionToken &&
|
|
|
|
action !== 'disabled'
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
// and a dwelled but abandoned event must following an event which has a dwell start
|
|
|
|
} else if ( !data.totalInteractionTime && action === 'dwelledButAbandoned' ) {
|
2016-10-24 22:58:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-05-16 20:32:11 +00:00
|
|
|
data.previewCountBucket = mw.popups.getPreviewCountBucket();
|
|
|
|
// Figure out `namespaceIdHover` from `pageTitleHover`.
|
|
|
|
if ( data.pageTitleHover && data.namespaceIdHover === undefined ) {
|
|
|
|
data.namespaceIdHover = new mw.Title( data.pageTitleHover ).getNamespaceId();
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
mw.popups.schemaPopups = {
|
|
|
|
getDefaultValues: getDefaultValues,
|
|
|
|
getSamplingRate: getSamplingRate,
|
|
|
|
getEditCountBucket: getEditCountBucket,
|
|
|
|
getMassagedData: getMassagedData
|
|
|
|
};
|
|
|
|
|
|
|
|
} )( jQuery, mediaWiki );
|