mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Popups
synced 2024-09-23 10:21:11 +00:00
Switch to Schema:Popups revid 15597282
Bug: T131315 Change-Id: I2ed18e00afb3e355327b417e68e5930b46d49086
This commit is contained in:
parent
058b863304
commit
aba43fd560
|
@ -75,6 +75,7 @@ class PopupsHooks {
|
|||
$schemaPopups += [
|
||||
'dependencies' => [
|
||||
'schema.Popups',
|
||||
'ext.popups.schemaPopups.utils',
|
||||
],
|
||||
'scripts' => [
|
||||
'resources/ext.popups.schemaPopups.js',
|
||||
|
@ -229,12 +230,14 @@ class PopupsHooks {
|
|||
'tests/qunit/ext.popups.renderer/desktopRenderer.test.js',
|
||||
'tests/qunit/ext.popups.renderer.article.test.js',
|
||||
'tests/qunit/ext.popups.core.test.js',
|
||||
'tests/qunit/ext.popups.schemaPopups.utils.test.js',
|
||||
'tests/qunit/ext.popups.settings.test.js',
|
||||
'tests/qunit/ext.popups.experiment.test.js',
|
||||
),
|
||||
'dependencies' => array(
|
||||
'ext.popups.desktop',
|
||||
'ext.popups.experiment'
|
||||
'ext.popups.experiment',
|
||||
'ext.popups.schemaPopups.utils'
|
||||
),
|
||||
'localBasePath' => __DIR__,
|
||||
'remoteExtPath' => 'Popups',
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
]
|
||||
},
|
||||
"EventLoggingSchemas": {
|
||||
"Popups": 11625443
|
||||
"Popups": 15597282
|
||||
},
|
||||
"config": {
|
||||
"@PopupsBetaFeature": "@var bool: Whether the extension should be enabled as an opt-in beta feature. If true, the BetaFeatures extension must be installed. False by default.",
|
||||
|
@ -112,6 +112,21 @@
|
|||
"targets": [
|
||||
"desktop"
|
||||
]
|
||||
},
|
||||
"ext.popups.schemaPopups.utils": {
|
||||
"scripts": [
|
||||
"resources/ext.popups.schemaPopups.utils.js"
|
||||
],
|
||||
"dependencies": [
|
||||
"mediawiki.experiments",
|
||||
"mediawiki.Title",
|
||||
"mediawiki.user",
|
||||
"ext.popups.core",
|
||||
"ext.popups.renderer.desktopRenderer"
|
||||
],
|
||||
"targets": [
|
||||
"desktop"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ResourceFileModulePaths": {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
( function ( $, mw ) {
|
||||
'use strict';
|
||||
var previewCountStorageKey = 'ext.popups.core.previewCount',
|
||||
popupsEnabledStorageKey = 'mwe-popups-enabled';
|
||||
|
||||
/**
|
||||
* @class mw.popups
|
||||
|
@ -70,7 +72,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
mw.popups.render.render( $( this ), event );
|
||||
mw.popups.render.render( $( this ), event, mw.now(), mw.popups.getRandomToken() );
|
||||
} );
|
||||
};
|
||||
|
||||
|
@ -161,4 +163,80 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a random token.
|
||||
* Append the current timestamp to make the return value more unique.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
mw.popups.getRandomToken = function () {
|
||||
return mw.user.generateRandomSessionId() + Math.round( mw.now() ).toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return edit count bucket based on the number of edits.
|
||||
* The returned value is "unknown" is `window.localStorage` is not supported.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
mw.popups.getPreviewCountBucket = function () {
|
||||
var bucket,
|
||||
previewCount = mw.storage.get( previewCountStorageKey );
|
||||
|
||||
// no support for localStorage
|
||||
if ( previewCount === false ) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
// Fall back to 0 if this is the first time.
|
||||
previewCount = parseInt( previewCount || 0, 10 );
|
||||
|
||||
if ( previewCount === 0 ) {
|
||||
bucket = '0';
|
||||
} else if ( previewCount >= 1 && previewCount <= 4 ) {
|
||||
bucket = '1-4';
|
||||
} else if ( previewCount >= 5 && previewCount <= 20 ) {
|
||||
bucket = '5-20';
|
||||
} else if ( previewCount >= 21 ) {
|
||||
bucket = '21+';
|
||||
}
|
||||
|
||||
return bucket + ' previews';
|
||||
};
|
||||
|
||||
/**
|
||||
* Increment the preview count and save it to localStorage.
|
||||
*/
|
||||
mw.popups.incrementPreviewCount = function () {
|
||||
var previewCount = parseInt( mw.storage.get( previewCountStorageKey ) || 0, 10 );
|
||||
|
||||
mw.storage.set( previewCountStorageKey, ( previewCount + 1 ).toString() );
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the popups enabled state via $.jStorage
|
||||
*
|
||||
* @param {boolean} isEnabled
|
||||
*/
|
||||
mw.popups.saveEnabledState = function ( isEnabled ) {
|
||||
$.jStorage.set( popupsEnabledStorageKey, isEnabled.toString() );
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the popups enabled state via $.jStorage or 'wgPopupsExperiment'
|
||||
* config variable.
|
||||
* If the experiment isn't running, then continue to enable Popups
|
||||
* by default during initialisation. In this case the return value
|
||||
* defaults to `true` if the setting hasn't been saved before.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
mw.popups.getEnabledState = function () {
|
||||
if ( !mw.config.get( 'wgPopupsExperiment', false ) ) {
|
||||
return $.jStorage.get( popupsEnabledStorageKey ) !== 'false';
|
||||
} else {
|
||||
return mw.popups.experiment.isUserInCondition();
|
||||
}
|
||||
};
|
||||
|
||||
} )( jQuery, mediaWiki );
|
||||
|
|
|
@ -36,9 +36,10 @@
|
|||
* Send an API request and cache the jQuery element
|
||||
*
|
||||
* @param {jQuery} link
|
||||
* @param {Object} logData data to be logged
|
||||
* @return {jQuery.Promise}
|
||||
*/
|
||||
article.init = function ( link ) {
|
||||
article.init = function ( link, logData ) {
|
||||
var href = link.attr( 'href' ),
|
||||
title = mw.popups.getTitle( href ),
|
||||
deferred = $.Deferred(),
|
||||
|
@ -71,7 +72,14 @@
|
|||
}
|
||||
} );
|
||||
|
||||
mw.popups.render.currentRequest.fail( deferred.reject );
|
||||
mw.popups.render.currentRequest.fail( function ( textStatus ) {
|
||||
mw.track( 'ext.popups.schemaPopups', $.extend( logData, {
|
||||
action: 'error',
|
||||
errorState: textStatus,
|
||||
totalInteractionTime: Math.round( mw.now() - logData.dwellStartTime )
|
||||
} ) );
|
||||
deferred.reject();
|
||||
} );
|
||||
mw.popups.render.currentRequest.done( function ( re ) {
|
||||
mw.popups.render.currentRequest = undefined;
|
||||
|
||||
|
@ -149,6 +157,7 @@
|
|||
|
||||
mw.popups.render.cache[ href ].settings = {
|
||||
title: page.title,
|
||||
namespace: page.ns,
|
||||
tall: ( tall === undefined ) ? false : tall,
|
||||
thumbnail: ( thumbnail === undefined ) ? false : thumbnail
|
||||
};
|
||||
|
@ -550,8 +559,9 @@
|
|||
*
|
||||
* @method processPopups
|
||||
* @param {jQuery} link
|
||||
* @param {Object} logData data to be logged
|
||||
*/
|
||||
article.processPopup = function ( link ) {
|
||||
article.processPopup = function ( link, logData ) {
|
||||
var
|
||||
svg = mw.popups.supportsSVG,
|
||||
cache = mw.popups.render.cache [ link.attr( 'href' ) ],
|
||||
|
@ -562,7 +572,15 @@
|
|||
flippedX = cache.settings.flippedX;
|
||||
|
||||
popup.find( '.mwe-popups-settings-icon' ).click( function () {
|
||||
mw.popups.settings.open();
|
||||
delete logData.pageTitleHover;
|
||||
delete logData.namespaceIdHover;
|
||||
|
||||
mw.popups.settings.open( $.extend( {}, logData ) );
|
||||
|
||||
mw.track( 'ext.popups.schemaPopups', $.extend( logData, {
|
||||
action: 'tapped settings cog',
|
||||
totalInteractionTime: Math.round( mw.now() - logData.dwellStartTime )
|
||||
} ) );
|
||||
} );
|
||||
|
||||
if ( !flippedY && !tall && cache.settings.thumbnail.height < article.SIZES.landscapeImage.h ) {
|
||||
|
|
|
@ -1,6 +1,19 @@
|
|||
/*global popupDelay: true, popupHideDelay: true*/
|
||||
|
||||
( function ( $, mw ) {
|
||||
var logData = {};
|
||||
|
||||
/**
|
||||
* Logs the click on link or popup
|
||||
*
|
||||
* @param {Object} event
|
||||
*/
|
||||
function logClickAction( event ) {
|
||||
mw.track( 'ext.popups.schemaPopups', $.extend( {}, logData, {
|
||||
action: mw.popups.getAction( event ),
|
||||
totalInteractionTime: Math.round( mw.now() - logData.dwellStartTime )
|
||||
} ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @class mw.popups.render
|
||||
|
@ -79,8 +92,12 @@
|
|||
* @method render
|
||||
* @param {Object} link
|
||||
* @param {Object} event
|
||||
* @param {number} dwellStartTime the instant when the link is dwelled on
|
||||
* @param {string} linkInteractionToken random token representing the current interaction with the link
|
||||
*/
|
||||
mw.popups.render.render = function ( link, event ) {
|
||||
mw.popups.render.render = function ( link, event, dwellStartTime, linkInteractionToken ) {
|
||||
var linkHref = link.attr( 'href' );
|
||||
|
||||
// This will happen when the mouse goes from the popup box back to the
|
||||
// anchor tag. In such a case, the timer to close the box is cleared.
|
||||
if (
|
||||
|
@ -101,7 +118,7 @@
|
|||
|
||||
// Ignore if its meant to call a function
|
||||
// TODO: Remove this when adding reference popups
|
||||
if ( link.attr( 'href' ) === '#' ) {
|
||||
if ( linkHref === '#' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -109,7 +126,15 @@
|
|||
mw.popups.disableNavPopup();
|
||||
|
||||
mw.popups.render.currentLink = link;
|
||||
|
||||
logData = {
|
||||
pageTitleHover: mw.popups.getTitle( linkHref ),
|
||||
dwellStartTime: dwellStartTime,
|
||||
linkInteractionToken: linkInteractionToken
|
||||
};
|
||||
|
||||
link.on( 'mouseleave blur', mw.popups.render.leaveInactive );
|
||||
link.off( 'click', logClickAction ).on( 'click', logClickAction );
|
||||
|
||||
if ( mw.popups.render.cache[ link.attr( 'href' ) ] ) {
|
||||
mw.popups.render.openTimer = mw.popups.render.wait( mw.popups.render.POPUP_DELAY )
|
||||
|
@ -127,14 +152,14 @@
|
|||
for ( key in renderers ) {
|
||||
if ( renderers.hasOwnProperty( key ) && key !== 'article' ) {
|
||||
if ( !!renderers[ key ].matcher( link.attr( 'href' ) ) ) {
|
||||
cachePopup = renderers[ key ].init( link );
|
||||
cachePopup = renderers[ key ].init( link, $.extend( {}, logData ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the article renderer if nothing else matches
|
||||
if ( cachePopup === undefined ) {
|
||||
cachePopup = mw.popups.render.renderers.article.init( link );
|
||||
cachePopup = mw.popups.render.renderers.article.init( link, $.extend( {}, logData ) );
|
||||
}
|
||||
|
||||
mw.popups.render.openTimer = mw.popups.render.wait( mw.popups.render.POPUP_DELAY - mw.popups.render.API_DELAY );
|
||||
|
@ -183,15 +208,15 @@
|
|||
// More information and workarounds here - http://stackoverflow.com/a/13654655/366138
|
||||
mw.popups.$popup.html( mw.popups.$popup.html() );
|
||||
|
||||
cache.process( link );
|
||||
|
||||
// Event logging
|
||||
mw.popups.logData = {
|
||||
$.extend( logData, {
|
||||
pageTitleHover: cache.settings.title,
|
||||
pageTitleSource: mw.config.get( 'wgTitle' ),
|
||||
popupEnabled: mw.popups.enabled,
|
||||
time: mw.now()
|
||||
};
|
||||
namespaceIdHover: cache.settings.namespace,
|
||||
perceivedWait: Math.round( mw.now() - logData.dwellStartTime )
|
||||
} );
|
||||
|
||||
cache.process( link, $.extend( {}, logData ) );
|
||||
|
||||
mw.popups.$popup.find( 'a.mwe-popups-extract, a.mwe-popups-discreet' ).click( mw.popups.render.clickHandler );
|
||||
|
||||
link
|
||||
|
@ -199,6 +224,8 @@
|
|||
.on( 'mouseleave blur', mw.popups.render.leaveActive );
|
||||
|
||||
$( document ).on( 'keydown', mw.popups.render.closeOnEsc );
|
||||
|
||||
mw.popups.incrementPreviewCount();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -208,15 +235,11 @@
|
|||
* @param {Object} event
|
||||
*/
|
||||
mw.popups.render.clickHandler = function ( event ) {
|
||||
var action = mw.popups.getAction( event ),
|
||||
logData;
|
||||
var action = mw.popups.getAction( event );
|
||||
|
||||
logClickAction( event );
|
||||
|
||||
if ( action === 'opened in same tab' ) {
|
||||
logData = $.extend( {}, mw.popups.logData, {
|
||||
action: action
|
||||
} );
|
||||
computeDurationFromTime( logData );
|
||||
mw.track( 'ext.popups.schemaPopups', logData );
|
||||
window.location.href = mw.popups.render.currentLink.attr( 'href' );
|
||||
}
|
||||
};
|
||||
|
@ -228,19 +251,12 @@
|
|||
* @method closePopup
|
||||
*/
|
||||
mw.popups.render.closePopup = function () {
|
||||
var fadeInClass, fadeOutClass,
|
||||
logData = $.extend( {}, mw.popups.logData, {
|
||||
action: 'dismissed'
|
||||
} );
|
||||
var fadeInClass, fadeOutClass;
|
||||
|
||||
if ( mw.popups.render.currentLink === undefined ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Event logging
|
||||
computeDurationFromTime( logData );
|
||||
mw.track( 'ext.popups.schemaPopups', logData );
|
||||
|
||||
$( mw.popups.render.currentLink ).off( 'mouseleave blur', mw.popups.render.leaveActive );
|
||||
|
||||
fadeInClass = ( mw.popups.$popup.hasClass( 'mwe-popups-fade-in-up' ) ) ?
|
||||
|
@ -318,6 +334,11 @@
|
|||
*/
|
||||
mw.popups.render.leaveActive = function () {
|
||||
mw.popups.render.closeTimer = mw.popups.render.wait( mw.popups.render.POPUP_CLOSE_DELAY ).done( function () {
|
||||
mw.track( 'ext.popups.schemaPopups', $.extend( {}, logData, {
|
||||
action: 'dismissed',
|
||||
totalInteractionTime: Math.round( mw.now() - logData.dwellStartTime )
|
||||
} ) );
|
||||
|
||||
mw.popups.render.closePopup();
|
||||
} );
|
||||
};
|
||||
|
@ -328,6 +349,16 @@
|
|||
* @method leaveInactive
|
||||
*/
|
||||
mw.popups.render.leaveInactive = function () {
|
||||
if ( logData.dwellStartTime &&
|
||||
logData.linkInteractionToken &&
|
||||
mw.now() - logData.dwellStartTime >= 250
|
||||
) {
|
||||
mw.track( 'ext.popups.schemaPopups', $.extend( {}, logData, {
|
||||
action: 'dwelledButAbandoned',
|
||||
totalInteractionTime: Math.round( mw.now() - logData.dwellStartTime )
|
||||
} ) );
|
||||
}
|
||||
// TODO: should `blur` also be here?
|
||||
$( mw.popups.render.currentLink ).off( 'mouseleave', mw.popups.render.leaveInactive );
|
||||
if ( mw.popups.render.openTimer ) {
|
||||
mw.popups.render.openTimer.abort();
|
||||
|
@ -345,22 +376,11 @@
|
|||
* @method reset
|
||||
*/
|
||||
mw.popups.render.reset = function () {
|
||||
logData = {};
|
||||
mw.popups.render.currentLink = undefined;
|
||||
mw.popups.render.currentRequest = undefined;
|
||||
mw.popups.render.openTimer = undefined;
|
||||
mw.popups.render.closeTimer = undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function that computes duration from time.
|
||||
* Modifies the data so that it can be logged with EL.
|
||||
*
|
||||
* @ignore
|
||||
* @param {Object} data
|
||||
*/
|
||||
function computeDurationFromTime( data ) {
|
||||
data.duration = Math.floor( mw.now() - data.time );
|
||||
delete data.time;
|
||||
}
|
||||
|
||||
} )( jQuery, mediaWiki );
|
||||
|
|
|
@ -1,15 +1,30 @@
|
|||
( function ( $, mw ) {
|
||||
/**
|
||||
* Log the popup event as defined in the schema
|
||||
*
|
||||
* https://meta.wikimedia.org/wiki/Schema:Popups
|
||||
*/
|
||||
var schemaPopups = new mw.eventLog.Schema(
|
||||
'Popups',
|
||||
mw.config.get( 'wgPopupsSchemaPopupsSamplingRate', 0 )
|
||||
);
|
||||
var previousLogData,
|
||||
// Log the popup event as defined in the schema
|
||||
// https://meta.wikimedia.org/wiki/Schema:Popups
|
||||
schemaPopups = new mw.eventLog.Schema(
|
||||
'Popups',
|
||||
mw.popups.schemaPopups.getSamplingRate(),
|
||||
mw.popups.schemaPopups.getDefaultValues()
|
||||
);
|
||||
|
||||
mw.trackSubscribe( 'ext.popups.schemaPopups', function ( topic, data ) {
|
||||
schemaPopups.log( data );
|
||||
var shouldLog = true;
|
||||
|
||||
data = mw.popups.schemaPopups.getMassagedData( data );
|
||||
|
||||
// Only one action is recorded per link interaction token...
|
||||
if ( data.linkInteractionToken &&
|
||||
data.linkInteractionToken === previousLogData.linkInteractionToken ) {
|
||||
// however, the 'disabled' action takes two clicks by nature, so allow it
|
||||
if ( data.action !== 'disabled' ) {
|
||||
shouldLog = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( shouldLog ) {
|
||||
schemaPopups.log( data );
|
||||
}
|
||||
previousLogData = data;
|
||||
} );
|
||||
} )( jQuery, mediaWiki );
|
||||
|
|
111
resources/ext.popups.schemaPopups.utils.js
Normal file
111
resources/ext.popups.schemaPopups.utils.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
( function ( $, mw ) {
|
||||
/**
|
||||
* 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(),
|
||||
popupEnabled: mw.popups.getEnabledState(),
|
||||
popupDelay: mw.popups.render.POPUP_DELAY,
|
||||
pageToken: mw.user.generateRandomSessionId() +
|
||||
Math.floor( mw.now() ).toString() +
|
||||
mw.user.generateRandomSessionId(),
|
||||
// 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' ) );
|
||||
}
|
||||
// Also add the session token
|
||||
defaults.sessionToken = mw.user.sessionId();
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return data after making some adjustments so that it's ready to be logged
|
||||
*
|
||||
* @param {Object}data
|
||||
* @return {Object}
|
||||
*/
|
||||
function getMassagedData( data ) {
|
||||
data.previewCountBucket = mw.popups.getPreviewCountBucket();
|
||||
delete data.dwellStartTime;
|
||||
// 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 );
|
|
@ -1,10 +1,11 @@
|
|||
( function ( $, mw ) {
|
||||
|
||||
/**
|
||||
* @class mw.popups.settings
|
||||
* @singleton
|
||||
*/
|
||||
var settings = {};
|
||||
var currentLinkLogData,
|
||||
/**
|
||||
* @class mw.popups.settings
|
||||
* @singleton
|
||||
*/
|
||||
settings = {};
|
||||
|
||||
/**
|
||||
* The settings' dialog's section element.
|
||||
|
@ -138,13 +139,16 @@
|
|||
settings.save = function () {
|
||||
var v = $( 'input[name=mwe-popups-setting]:checked', '#mwe-popups-settings' ).val();
|
||||
if ( v === 'simple' ) {
|
||||
$.jStorage.set( 'mwe-popups-enabled', 'true' );
|
||||
mw.popups.saveEnabledState( true );
|
||||
settings.reloadPage();
|
||||
settings.close();
|
||||
} else {
|
||||
$.jStorage.set( 'mwe-popups-enabled', 'false' );
|
||||
mw.popups.saveEnabledState( false );
|
||||
$( '#mwe-popups-settings-form' ).hide();
|
||||
$( '#mwe-popups-settings-help' ).show();
|
||||
mw.track( 'ext.popups.schemaPopups', $.extend( {}, currentLinkLogData, {
|
||||
action: 'disabled'
|
||||
} ) );
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -152,12 +156,15 @@
|
|||
* Show the settings element and position it correctly
|
||||
*
|
||||
* @method open
|
||||
* @param {Object} logData data to log
|
||||
*/
|
||||
settings.open = function () {
|
||||
settings.open = function ( logData ) {
|
||||
var
|
||||
h = $( window ).height(),
|
||||
w = $( window ).width();
|
||||
|
||||
currentLinkLogData = logData;
|
||||
|
||||
$( 'body' ).append( $( '<div>' ).addClass( 'mwe-popups-overlay' ) );
|
||||
|
||||
if ( !settings.$element ) {
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
( function ( $, mw ) {
|
||||
|
||||
// If the experiment isn't running, then continue to enable Popups by default during
|
||||
// initialisation.
|
||||
if ( !mw.config.get( 'wgPopupsExperiment', false ) ) {
|
||||
mw.popups.enabled = $.jStorage.get( 'mwe-popups-enabled' ) !== 'false';
|
||||
} else {
|
||||
mw.popups.enabled = mw.popups.experiment.isUserInCondition();
|
||||
}
|
||||
mw.popups.enabled = mw.popups.getEnabledState();
|
||||
|
||||
/**
|
||||
* Returns valid jQuery selectors for which a popup should be triggered.
|
||||
|
@ -99,7 +92,8 @@
|
|||
};
|
||||
|
||||
mw.hook( 'wikipage.content' ).add( function ( $content ) {
|
||||
var $elements;
|
||||
var $elements, dwellStartTime, linkInteractionToken;
|
||||
|
||||
mw.popups.$content = $content;
|
||||
$elements = mw.popups.selectPopupElements();
|
||||
|
||||
|
@ -107,6 +101,13 @@
|
|||
mw.popups.removeTooltips( $elements );
|
||||
mw.popups.setupTriggers( $elements );
|
||||
} else {
|
||||
|
||||
$elements.on( mw.popups.triggers, function () {
|
||||
// cache the hover start time and link interaction token for a later use
|
||||
dwellStartTime = mw.now();
|
||||
linkInteractionToken = mw.popups.getRandomToken();
|
||||
} );
|
||||
|
||||
// Events are logged even when Hovercards are disabled
|
||||
// See T88166 for details
|
||||
$elements.on( 'click', function ( event ) {
|
||||
|
@ -116,9 +117,9 @@
|
|||
|
||||
mw.track( 'ext.popups.schemaPopups', {
|
||||
pageTitleHover: $this.attr( 'title' ),
|
||||
pageTitleSource: mw.config.get( 'wgTitle' ),
|
||||
popupEnabled: mw.popups.enabled,
|
||||
action: action
|
||||
action: action,
|
||||
totalInteractionTime: Math.round( mw.now() - dwellStartTime ),
|
||||
linkInteractionToken: linkInteractionToken
|
||||
} );
|
||||
|
||||
if ( action === 'opened in same tab' ) {
|
||||
|
@ -135,5 +136,9 @@
|
|||
mw.popups.createSVGMask();
|
||||
mw.popups.createPopupElement();
|
||||
}
|
||||
|
||||
mw.track( 'ext.popups.schemaPopups', {
|
||||
action: 'pageLoaded'
|
||||
} );
|
||||
} );
|
||||
} )( jQuery, mediaWiki );
|
||||
|
|
|
@ -185,4 +185,199 @@
|
|||
}
|
||||
} );
|
||||
|
||||
QUnit.test( 'getRandomToken', function ( assert ) {
|
||||
var token;
|
||||
|
||||
QUnit.expect( 3 );
|
||||
|
||||
token = mw.popups.getRandomToken();
|
||||
assert.ok(
|
||||
token.length > mw.user.generateRandomSessionId().length,
|
||||
'Random token is long enough.'
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
typeof token === 'string',
|
||||
'Random token is a string.'
|
||||
);
|
||||
|
||||
assert.notEqual(
|
||||
mw.popups.getRandomToken(),
|
||||
token,
|
||||
'Newly generated random token is not equal to the one generated earlier.'
|
||||
);
|
||||
} );
|
||||
|
||||
QUnit.test( 'getPreviewCountBucket', function ( assert ) {
|
||||
var i, previewCount, bucket,
|
||||
cases = [
|
||||
[ '0', '0 previews' ],
|
||||
[ '1', '1-4 previews' ],
|
||||
[ '2', '1-4 previews' ],
|
||||
[ '4', '1-4 previews' ],
|
||||
[ '5', '5-20 previews' ],
|
||||
[ '10', '5-20 previews' ],
|
||||
[ '20', '5-20 previews' ],
|
||||
[ '21', '21+ previews' ],
|
||||
[ '100', '21+ previews' ],
|
||||
[ '1000', '21+ previews' ]
|
||||
],
|
||||
storageKey = 'ext.popups.core.previewCount',
|
||||
mwStorageStub = this.sandbox.stub( mw.storage, 'get' )
|
||||
.withArgs( storageKey );
|
||||
|
||||
QUnit.expect( cases.length + 1 );
|
||||
|
||||
mwStorageStub.returns( false );
|
||||
assert.equal(
|
||||
mw.popups.getPreviewCountBucket(),
|
||||
'unknown',
|
||||
'Preview count bucket is `unkown` when localStorage is unsupported.'
|
||||
);
|
||||
|
||||
for ( i = 0; i < cases.length; i++ ) {
|
||||
previewCount = cases[ i ][ 0 ];
|
||||
mwStorageStub.returns( previewCount );
|
||||
bucket = mw.popups.getPreviewCountBucket();
|
||||
assert.equal(
|
||||
bucket,
|
||||
cases[ i ][ 1 ],
|
||||
'Preview count bucket is "' + bucket + '" when preview count is ' +
|
||||
previewCount + '.'
|
||||
);
|
||||
}
|
||||
} );
|
||||
|
||||
QUnit.test( 'incrementPreviewCount', function ( assert ) {
|
||||
var storageKey = 'ext.popups.core.previewCount',
|
||||
mwStorageGetStub = this.sandbox.stub( mw.storage, 'get' )
|
||||
.withArgs( storageKey ),
|
||||
mwStorageSetStub = this.sandbox.stub( mw.storage, 'set' );
|
||||
|
||||
QUnit.expect( 3 );
|
||||
|
||||
mwStorageGetStub.returns( null );
|
||||
mw.popups.incrementPreviewCount();
|
||||
assert.equal(
|
||||
mwStorageSetStub.firstCall.args[ 1 ],
|
||||
1,
|
||||
'Incrementing the preview count to 1 when no value has' +
|
||||
' been saved in localStorage yet.'
|
||||
);
|
||||
|
||||
mwStorageGetStub.returns( '1' );
|
||||
mw.popups.incrementPreviewCount();
|
||||
assert.equal(
|
||||
mwStorageSetStub.secondCall.args[ 1 ],
|
||||
2,
|
||||
'Incrementing the preview count to 2 when the value in localStorage' +
|
||||
' is already "1".'
|
||||
);
|
||||
|
||||
mwStorageGetStub.returns( '5' );
|
||||
mw.popups.incrementPreviewCount();
|
||||
assert.equal(
|
||||
mwStorageSetStub.thirdCall.args[ 1 ],
|
||||
6,
|
||||
'Incrementing the preview count to 6 when the value in localStorage' +
|
||||
' is already "5".'
|
||||
);
|
||||
} );
|
||||
|
||||
QUnit.test( 'saveEnabledState', function ( assert ) {
|
||||
var storageKey = 'mwe-popups-enabled',
|
||||
jStorageStub = this.sandbox.stub( $.jStorage, 'set' )
|
||||
.withArgs( storageKey );
|
||||
|
||||
QUnit.expect( 2 );
|
||||
|
||||
mw.popups.saveEnabledState( true );
|
||||
assert.equal(
|
||||
jStorageStub.firstCall.args[ 1 ],
|
||||
'true',
|
||||
'Popups enabled state has been saved as "true".'
|
||||
);
|
||||
|
||||
mw.popups.saveEnabledState( false );
|
||||
assert.equal(
|
||||
jStorageStub.secondCall.args[ 1 ],
|
||||
'false',
|
||||
'Popups enabled state has been saved as "false".'
|
||||
);
|
||||
} );
|
||||
|
||||
QUnit.test( 'getEnabledState', function ( assert ) {
|
||||
var storageKey = 'mwe-popups-enabled',
|
||||
mwConfigStub = this.sandbox.stub( mw.config, 'get' )
|
||||
.withArgs( 'wgPopupsExperiment' ),
|
||||
jStorageStub = this.sandbox.stub( $.jStorage, 'get' )
|
||||
.withArgs( storageKey ),
|
||||
experimentStub = this.sandbox.stub( mw.popups.experiment,
|
||||
'isUserInCondition' );
|
||||
|
||||
QUnit.expect( 7 );
|
||||
|
||||
mwConfigStub.returns( null );
|
||||
jStorageStub.returns( null );
|
||||
assert.equal(
|
||||
mw.popups.getEnabledState(),
|
||||
true,
|
||||
'Popups are enabled when the experiment is not running, nor has' +
|
||||
' Popups been disabled via the settings.'
|
||||
);
|
||||
|
||||
mwConfigStub.returns( null );
|
||||
jStorageStub.returns( 'true' );
|
||||
assert.equal(
|
||||
mw.popups.getEnabledState(),
|
||||
true,
|
||||
'Popups are enabled when the experiment is not running and when' +
|
||||
' Popups has been enabled via the settings.'
|
||||
);
|
||||
|
||||
mwConfigStub.returns( null );
|
||||
jStorageStub.returns( 'false' );
|
||||
assert.equal(
|
||||
mw.popups.getEnabledState(),
|
||||
false,
|
||||
'Popups are disabled when the experiment is not running and when' +
|
||||
' Popups has been disabled via the settings.'
|
||||
);
|
||||
|
||||
mwConfigStub.returns( false );
|
||||
jStorageStub.returns( 'true' );
|
||||
assert.equal(
|
||||
mw.popups.getEnabledState(),
|
||||
true,
|
||||
'Popups are enabled when the experiment is disabled and when' +
|
||||
' Popups has been enabled via the settings.'
|
||||
);
|
||||
|
||||
mwConfigStub.returns( false );
|
||||
jStorageStub.returns( 'false' );
|
||||
assert.equal(
|
||||
mw.popups.getEnabledState(),
|
||||
false,
|
||||
'Popups are disabled when the experiment is disabled and when' +
|
||||
' Popups has been disabled via the settings.'
|
||||
);
|
||||
|
||||
mwConfigStub.returns( true );
|
||||
experimentStub.returns( true );
|
||||
assert.equal(
|
||||
mw.popups.getEnabledState(),
|
||||
true,
|
||||
'Popups are disabled when the experiment is running and ' +
|
||||
' the user is bucketed.'
|
||||
);
|
||||
|
||||
mwConfigStub.returns( true );
|
||||
experimentStub.returns( false );
|
||||
assert.equal(
|
||||
mw.popups.getEnabledState(),
|
||||
false,
|
||||
'Popups are disabled when the experiment is running and ' +
|
||||
' the user is not bucketed.'
|
||||
);
|
||||
} );
|
||||
} )( jQuery, mediaWiki );
|
||||
|
|
104
tests/qunit/ext.popups.schemaPopups.utils.test.js
Normal file
104
tests/qunit/ext.popups.schemaPopups.utils.test.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
( function ( $, mw ) {
|
||||
var schemaPopups = mw.popups.schemaPopups;
|
||||
|
||||
QUnit.module( 'ext.popups.schemaPopups.utils' );
|
||||
|
||||
QUnit.test( 'getSamplingRate', function ( assert ) {
|
||||
var configStub = this.sandbox.stub( mw.config, 'get' )
|
||||
.withArgs( 'wgPopupsSchemaPopupsSamplingRate' ),
|
||||
isFunctionStub = this.sandbox.stub( $, 'isFunction' )
|
||||
.withArgs( navigator.sendBeacon ),
|
||||
mwUserSessionIdStub = this.sandbox.stub( mw.user, 'sessionId' );
|
||||
|
||||
QUnit.expect( 9 );
|
||||
|
||||
isFunctionStub.returns( false );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 0,
|
||||
'Sampling rate is 0 when `navigator.sendBeacon` is unavailable.' );
|
||||
|
||||
isFunctionStub.returns( true );
|
||||
|
||||
configStub.returns( null );
|
||||
mwUserSessionIdStub.returns( 'abc' );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 0,
|
||||
'Sampling rate is 0 when the `wgPopupsSchemaPopupsSamplingRate`' +
|
||||
' config variable is undefined and' +
|
||||
' `mw.user.sessionId() = ' + mw.user.sessionId() + '`.' );
|
||||
|
||||
configStub.returns( null );
|
||||
mwUserSessionIdStub.returns( 'def' );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 0,
|
||||
'Sampling rate is 0 when the `wgPopupsSchemaPopupsSamplingRate`' +
|
||||
' config variable is undefined and' +
|
||||
' `mw.user.sessionId() = ' + mw.user.sessionId() + '`.' );
|
||||
|
||||
configStub.returns( 0 );
|
||||
mwUserSessionIdStub.returns( 'abc' );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 0,
|
||||
'Sampling rate is 0 when `wgPopupsSchemaPopupsSamplingRate = 0`' +
|
||||
' `mw.user.sessionId() = ' + mw.user.sessionId() + '`.' );
|
||||
|
||||
configStub.returns( 0 );
|
||||
mwUserSessionIdStub.returns( 'def' );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 0,
|
||||
'Sampling rate is 0 when `wgPopupsSchemaPopupsSamplingRate = 0`' +
|
||||
' `mw.user.sessionId() = ' + mw.user.sessionId() + '`.' );
|
||||
|
||||
configStub.returns( 1 );
|
||||
mwUserSessionIdStub.returns( 'abc' );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 1,
|
||||
'Sampling rate is 1 when `wgPopupsSchemaPopupsSamplingRate = 1`' +
|
||||
' `mw.user.sessionId() = ' + mw.user.sessionId() + '`.' );
|
||||
|
||||
configStub.returns( 1 );
|
||||
mwUserSessionIdStub.returns( 'def' );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 1,
|
||||
'Sampling rate is 1 when `wgPopupsSchemaPopupsSamplingRate = 1`' +
|
||||
' `mw.user.sessionId() = ' + mw.user.sessionId() + '`.' );
|
||||
|
||||
configStub.returns( 0.5 );
|
||||
mwUserSessionIdStub.returns( 'abc' );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 1,
|
||||
'Sampling rate is 1 when `wgPopupsSchemaPopupsSamplingRate = 0.5` and' +
|
||||
' `mw.user.sessionId() = ' + mw.user.sessionId() + '`.' );
|
||||
|
||||
configStub.returns( 0.5 );
|
||||
mwUserSessionIdStub.returns( 'def' );
|
||||
assert.equal( schemaPopups.getSamplingRate(), 0,
|
||||
'Sampling rate is 0 when `wgPopupsSchemaPopupsSamplingRate = 0.5` and' +
|
||||
' `mw.user.sessionId() = ' + mw.user.sessionId() + '`.' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'getEditCountBucket', function ( assert ) {
|
||||
var i, bucket, editCount,
|
||||
cases = [
|
||||
[ 0, '0 edits' ],
|
||||
[ 1, '1-4 edits' ],
|
||||
[ 2, '1-4 edits' ],
|
||||
[ 4, '1-4 edits' ],
|
||||
[ 5, '5-99 edits' ],
|
||||
[ 25, '5-99 edits' ],
|
||||
[ 50, '5-99 edits' ],
|
||||
[ 99, '5-99 edits' ],
|
||||
[ 100, '100-999 edits' ],
|
||||
[ 101, '100-999 edits' ],
|
||||
[ 500, '100-999 edits' ],
|
||||
[ 999, '100-999 edits' ],
|
||||
[ 1000, '1000+ edits' ],
|
||||
[ 1500, '1000+ edits' ]
|
||||
];
|
||||
|
||||
QUnit.expect( cases.length );
|
||||
|
||||
for ( i = 0; i < cases.length; i++ ) {
|
||||
editCount = cases[ i ][ 0 ];
|
||||
bucket = schemaPopups.getEditCountBucket( editCount );
|
||||
assert.equal(
|
||||
bucket,
|
||||
cases[ i ][ 1 ],
|
||||
'Edit count bucket is "' + bucket + '" when edit count is ' +
|
||||
editCount + '.'
|
||||
);
|
||||
}
|
||||
} );
|
||||
} )( jQuery, mediaWiki );
|
Loading…
Reference in a new issue