mediawiki-extensions-Visual.../modules/ve-mw/init/ve.init.mw.TargetEvents.js
Rob Moen 4bd80b625e Abstract tracking and specify which target it came from
Split tracking out of the base target and from viewPageTarget

Primary reasons for this change:
 * Makes it possible to resolve an issue with tracking in mobile
 * Lets us reuse the viewPage save workflow tracking
 * Support existing and new targets with tracking
 * Simplification of target classes

Change-Id: I036e4f2129d929db0a3b9a4baa87c946a4b194a9
2014-03-06 09:50:11 -08:00

226 lines
6.5 KiB
JavaScript

/*!
* VisualEditor MediaWiki Initialization class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Initialization MediaWiki Target Analytics.
*
* @class
*
* @constructor
* @param {ve.init.mw.Target} target Target class to log events for
*/
ve.init.mw.TargetEvents = function ( target ) {
this.target = target;
this.timings = { 'saveRetries': 0 };
// Events
this.target.connect( this, {
'saveWorkFlowBegin': 'onSaveWorkflowBegin',
'saveWorlFlowEnd': 'onSaveWorkflowEnd',
'saveInitiated': 'onSaveInitated',
'save': 'onSaveComplete',
'saveReview': 'onSaveReview',
'saveErrorEmpty': 'onSaveErrorEmpty',
'saveErrorSpamBlacklist': 'onSaveErrorSpamBlacklist',
'saveErrorAbuseFilter': 'onSaveErrorAbuseFilter',
'saveErrorBadToken': 'onSaveErrorBadToken',
'saveErrorNewUser': 'onSaveErrorNewUser',
'saveErrorCaptcha': 'onSaveErrorCaptcha',
'saveErrorUnknown': 'onSaveErrorUnknown',
'surfaceReady': 'onSurfaceReady',
'editConflict': 'onEditConflict',
'showChanges': 'onShowChanges',
'showChangesError': 'onShowChangesError',
'noChanges': 'onNoChanges',
'serializeComplete': 'onSerializeComplete',
'serializeError': 'onSerializeError'
} );
};
/**
* Target specific ve.track wrapper
*
* @param {string} topic Event name
* @param {Object} data Additional data describing the event, encoded as an object
*/
ve.init.mw.TargetEvents.prototype.track = function ( topic, data ) {
data.targetName = this.target.constructor.static.name;
ve.track( topic, data );
};
/**
* Track when user begins the save workflow
*/
ve.init.mw.TargetEvents.prototype.onSaveWorkflowBegin = function () {
this.timings.saveWorkflowBegin = ve.now();
this.track( 'behavior.lastTransactionTillSaveDialogOpen', {
'duration': this.timings.saveWorkflowBegin - this.timings.lastTransaction
} );
};
/**
* Track when user ends the save workflow
*/
ve.init.mw.TargetEvents.prototype.onSaveWorkflowEnd = function () {
this.track( 'behavior.saveDialogClose', { 'duration': ve.now() - this.timings.saveWorkflowBegin } );
this.timings.saveWorkflowBegin = null;
};
/**
* Track when document save is initiated
*/
ve.init.mw.TargetEvents.prototype.onSaveInitated = function () {
this.timings.saveInitiated = ve.now();
this.timings.saveRetries++;
this.track( 'behavior.saveDialogOpenTillSave', {
'duration': this.timings.saveInitiated - this.timings.saveWorkflowBegin
} );
};
/**
* Track when document save is complete
*/
ve.init.mw.TargetEvents.prototype.onSaveComplete = function () {
this.track( 'performance.user.saveComplete', { 'duration': ve.now() - this.timings.saveInitiated } );
this.timings.saveRetries = 0;
};
/**
* Track a save error by type
*
* @method
* @param {string} type Text for error type
*/
ve.init.mw.TargetEvents.prototype.trackSaveError = function ( type ) {
this.track( 'performance.user.saveError', {
'duration': ve.now() - this.timings.saveInitiated,
'retries': this.timings.saveRetries,
'type': type
} );
};
/**
* Record the time of the last transaction in response to a 'transact' event on the document.
*/
ve.init.mw.TargetEvents.prototype.recordLastTransactionTime = function () {
this.timings.lastTransaction = ve.now();
};
/**
* Track time elapsed from beginning of save workflow to review
*/
ve.init.mw.TargetEvents.prototype.onSaveReview = function () {
this.timings.saveReview = ve.now();
this.track( 'behavior.saveDialogOpenTillReview', {
'duration': this.timings.saveReview - this.timings.saveWorkflowBegin
} );
};
/**
* Track when save api returns no data
*/
ve.init.mw.TargetEvents.prototype.onSaveErrorEmpty = function () {
this.trackSaveError( 'empty' );
};
/**
* Track when spamblacklist save error occurs
*/
ve.init.mw.TargetEvents.prototype.onSaveErrorSpamBlacklist = function () {
this.trackSaveError( 'spamblacklist' );
};
/**
* Track when abusefilter save error occurs
*/
ve.init.mw.TargetEvents.prototype.onSaveErrorAbuseFilter = function () {
this.trackSaveError( 'abusefilter' );
};
/**
* Track when the save request requires a new edit token
*/
ve.init.mw.TargetEvents.prototype.onSaveErrorBadToken = function () {
this.trackSaveError( 'badtoken' );
};
/**
* Track when the save request detects a new user session
*/
ve.init.mw.TargetEvents.prototype.onSaveErrorNewUser = function () {
this.trackSaveError( 'newuser' );
};
/**
* Track when the save request requires about captcha
*/
ve.init.mw.TargetEvents.prototype.onSaveErrorCaptcha = function () {
this.trackSaveError( 'captcha' );
};
/**
* Track when save request has an unknown error
*/
ve.init.mw.TargetEvents.prototype.onSaveErrorUnknown = function () {
this.trackSaveError( 'unknown' );
};
ve.init.mw.TargetEvents.prototype.onSurfaceReady = function () {
this.track( 'performance.system.activation', { 'duration': ve.now() - this.timings.activationStart } );
this.target.surface.getModel().getDocument().connect( this, {
'transact': 'recordLastTransactionTime'
} );
};
/**
* Track when save request results in an edit conflict
*/
ve.init.mw.TargetEvents.prototype.onEditConflict = function () {
this.track( 'performance.user.saveError.editconflict', {
'duration': ve.now() - this.timings.saveInitiated,
'retries': this.timings.saveRetries
} );
};
/**
* Track when the user enters the review workflow
*/
ve.init.mw.TargetEvents.prototype.onShowChanges = function () {
this.track( 'performance.user.reviewComplete', { 'duration': ve.now() - this.timings.saveReview } );
};
/**
* Track when the diff request fails in the review workflow
*/
ve.init.mw.TargetEvents.prototype.onShowChangesError = function () {
this.track( 'performance.user.reviewError', { 'duration': ve.now() - this.timings.saveReview } );
};
/**
* Track when the diff request detects no changes
*/
ve.init.mw.TargetEvents.prototype.onNoChanges = function () {
this.track( 'performance.user.reviewComplete', { 'duration': ve.now() - this.timings.saveReview } );
};
/**
* Track whe serilization is complete in review workflow
*/
ve.init.mw.TargetEvents.prototype.onSerializeComplete = function () {
this.track( 'performance.user.reviewComplete', { 'duration': ve.now() - this.timings.saveReview } );
};
/**
* Track when there is a serlization error
*/
ve.init.mw.TargetEvents.prototype.onSerializeError = function () {
if ( this.timings.saveWorkflowBegin ) {
// This function can be called by the switch to wikitext button as well, so only log
// reviewError if we actually got here from the save workflow
this.track( 'performance.user.reviewError', { 'duration': ve.now() - this.timings.saveReview } );
}
};