mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-24 22:35:41 +00:00
Merge "Add instrumentation for edit schema"
This commit is contained in:
commit
0ed86dd9da
|
@ -297,6 +297,7 @@ $wgResourceModules += array(
|
|||
'modules/ve-mw/init/ve.init.mw.Platform.js',
|
||||
'modules/ve-mw/init/ve.init.mw.Target.js',
|
||||
'modules/ve-mw/init/ve.init.mw.TargetEvents.js',
|
||||
'modules/ve-mw/init/ve.init.mw.trackSubscriber.js',
|
||||
),
|
||||
'dependencies' => array(
|
||||
'jquery.visibleText',
|
||||
|
|
|
@ -31,13 +31,6 @@
|
|||
.then( function () {
|
||||
var target = new ve.init.mw.ViewPageTarget();
|
||||
|
||||
// Tee tracked events to MediaWiki firehose, if available (1.23+).
|
||||
if ( mw.track ) {
|
||||
ve.trackSubscribeAll( function ( topic, data ) {
|
||||
mw.track.call( null, 've.' + topic, data );
|
||||
} );
|
||||
}
|
||||
|
||||
// Transfer methods
|
||||
ve.init.mw.ViewPageTarget.prototype.setupSectionEditLinks = init.setupSectionLinks;
|
||||
|
||||
|
@ -342,6 +335,7 @@
|
|||
}
|
||||
|
||||
init.showLoading();
|
||||
ve.track( 'mwedit.init', { type: 'page', mechanism: 'click' } );
|
||||
|
||||
if ( history.pushState && uri.query.veaction !== 'edit' ) {
|
||||
// Replace the current state with one that is tagged as ours, to prevent the
|
||||
|
@ -357,8 +351,11 @@
|
|||
e.preventDefault();
|
||||
|
||||
getTarget().done( function ( target ) {
|
||||
ve.track( 'Edit', { action: 'edit-link-click' } );
|
||||
target.activate().always( init.hideLoading );
|
||||
target.activate()
|
||||
.done( function () {
|
||||
ve.track( 'mwedit.ready' );
|
||||
} )
|
||||
.always( init.hideLoading );
|
||||
} );
|
||||
},
|
||||
|
||||
|
@ -368,6 +365,7 @@
|
|||
}
|
||||
|
||||
init.showLoading();
|
||||
ve.track( 'mwedit.init', { type: 'section', mechanism: 'click' } );
|
||||
|
||||
if ( history.pushState && uri.query.veaction !== 'edit' ) {
|
||||
// Replace the current state with one that is tagged as ours, to prevent the
|
||||
|
@ -382,9 +380,12 @@
|
|||
e.preventDefault();
|
||||
|
||||
getTarget().done( function ( target ) {
|
||||
ve.track( 'Edit', { action: 'section-edit-link-click' } );
|
||||
target.saveEditSection( $( e.target ).closest( 'h1, h2, h3, h4, h5, h6' ).get( 0 ) );
|
||||
target.activate().always( init.hideLoading );
|
||||
target.activate()
|
||||
.done( function () {
|
||||
ve.track( 'mwedit.ready' );
|
||||
} )
|
||||
.always( init.hideLoading );
|
||||
} );
|
||||
},
|
||||
|
||||
|
@ -476,9 +477,16 @@
|
|||
$( function () {
|
||||
if ( init.isAvailable ) {
|
||||
if ( isViewPage && uri.query.veaction === 'edit' ) {
|
||||
var isSection = uri.query.vesection !== undefined;
|
||||
init.showLoading();
|
||||
|
||||
ve.track( 'mwedit.init', { type: isSection ? 'section' : 'page', mechanism: 'url' } );
|
||||
getTarget().done( function ( target ) {
|
||||
target.activate().always( init.hideLoading );
|
||||
target.activate()
|
||||
.done( function () {
|
||||
ve.track( 'mwedit.ready' );
|
||||
} )
|
||||
.always( init.hideLoading );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,6 @@ ve.init.mw.ViewPageTarget = function VeInitMwViewPageTarget() {
|
|||
saveErrorEmpty: 'onSaveErrorEmpty',
|
||||
saveErrorSpamBlacklist: 'onSaveErrorSpamBlacklist',
|
||||
saveErrorAbuseFilter: 'onSaveErrorAbuseFilter',
|
||||
saveErrorBadToken: 'onSaveErrorBadToken',
|
||||
saveErrorNewUser: 'onSaveErrorNewUser',
|
||||
saveErrorCaptcha: 'onSaveErrorCaptcha',
|
||||
saveErrorUnknown: 'onSaveErrorUnknown',
|
||||
|
@ -155,7 +154,7 @@ ve.init.mw.ViewPageTarget.compatibility = {
|
|||
|
||||
/**
|
||||
* @event saveWorkflowBegin
|
||||
* Fired when user enters the save workflow
|
||||
* Fired when user clicks the button to open the save dialog.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -276,19 +275,20 @@ ve.init.mw.ViewPageTarget.prototype.activate = function () {
|
|||
* Determines whether we want to switch to view mode or not (displaying a dialog if necessary)
|
||||
* Then, if we do, actually switches to view mode.
|
||||
*
|
||||
* @method
|
||||
* @param {boolean} [override] Do not display a dialog
|
||||
* @param {string} [trackMechanism] Abort mechanism; used for event tracking if present
|
||||
*/
|
||||
ve.init.mw.ViewPageTarget.prototype.deactivate = function ( override ) {
|
||||
ve.init.mw.ViewPageTarget.prototype.deactivate = function ( override, trackMechanism ) {
|
||||
var target = this;
|
||||
if ( override || ( this.active && !this.deactivating ) ) {
|
||||
if ( override || !this.edited ) {
|
||||
this.cancel();
|
||||
this.cancel( trackMechanism );
|
||||
} else {
|
||||
this.surface.dialogs.openWindow( 'cancelconfirm' ).then( function ( opened ) {
|
||||
opened.then( function ( closing ) {
|
||||
closing.then( function ( data ) {
|
||||
if ( data.action === 'discard' ) {
|
||||
target.cancel();
|
||||
target.cancel( trackMechanism );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
@ -300,10 +300,29 @@ ve.init.mw.ViewPageTarget.prototype.deactivate = function ( override ) {
|
|||
/**
|
||||
* Switch to view mode
|
||||
*
|
||||
* @method
|
||||
* @param {string} [trackMechanism] Abort mechanism; used for event tracking if present
|
||||
*/
|
||||
ve.init.mw.ViewPageTarget.prototype.cancel = function () {
|
||||
var promises = [];
|
||||
ve.init.mw.ViewPageTarget.prototype.cancel = function ( trackMechanism ) {
|
||||
var abortType, promises = [];
|
||||
|
||||
// Event tracking
|
||||
if ( trackMechanism ) {
|
||||
if ( this.activating ) {
|
||||
abortType = 'preinit';
|
||||
} else if ( !this.edited ) {
|
||||
abortType = 'nochange';
|
||||
} else if ( this.saving ) {
|
||||
abortType = 'abandonMidsave';
|
||||
} else {
|
||||
// switchwith and switchwithout do not go through this code path,
|
||||
// they go through switchToWikitextEditor() instead
|
||||
abortType = 'abandon';
|
||||
}
|
||||
ve.track( 'mwedit.abort', {
|
||||
type: abortType,
|
||||
mechanism: trackMechanism
|
||||
} );
|
||||
}
|
||||
|
||||
this.deactivating = true;
|
||||
// User interface changes
|
||||
|
@ -380,8 +399,12 @@ ve.init.mw.ViewPageTarget.prototype.onLoadError = function ( jqXHR, status ) {
|
|||
this.currentUri.query.action = 'edit';
|
||||
location.href = this.currentUri.toString();
|
||||
} else {
|
||||
// Something weird happened? Deactivate
|
||||
// TODO: how does this handle load errors triggered from
|
||||
// calling this.loading.abort()?
|
||||
this.activating = false;
|
||||
// User interface changes
|
||||
// Not passing trackMechanism because we don't know what happened
|
||||
// and this is not a user action
|
||||
this.deactivate( true );
|
||||
}
|
||||
};
|
||||
|
@ -497,6 +520,8 @@ ve.init.mw.ViewPageTarget.prototype.onSave = function (
|
|||
contentSub
|
||||
);
|
||||
this.setupSectionEditLinks();
|
||||
// Tear down the target now that we're done saving
|
||||
// Not passing trackMechanism because this isn't an abort action
|
||||
this.deactivate( true );
|
||||
if ( newid !== undefined ) {
|
||||
mw.hook( 'postEdit' ).fire( {
|
||||
|
@ -513,7 +538,6 @@ ve.init.mw.ViewPageTarget.prototype.onSave = function (
|
|||
*/
|
||||
ve.init.mw.ViewPageTarget.prototype.onSaveErrorEmpty = function () {
|
||||
this.showSaveError( ve.msg( 'visualeditor-saveerror', 'Empty server response' ), false /* prevents reapply */ );
|
||||
this.events.trackSaveError( 'empty' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -530,7 +554,6 @@ ve.init.mw.ViewPageTarget.prototype.onSaveErrorSpamBlacklist = function ( editAp
|
|||
),
|
||||
false // prevents reapply
|
||||
);
|
||||
this.events.trackSaveError( 'spamblacklist' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -544,16 +567,6 @@ ve.init.mw.ViewPageTarget.prototype.onSaveErrorAbuseFilter = function ( editApi
|
|||
// Don't disable the save button. If the action is not disallowed the user may save the
|
||||
// edit by pressing Save again. The AbuseFilter API currently has no way to distinguish
|
||||
// between filter triggers that are and aren't disallowing the action.
|
||||
this.events.trackSaveError( 'abusefilter' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Track when there is a bad edit token on save
|
||||
*
|
||||
* @method
|
||||
*/
|
||||
ve.init.mw.ViewPageTarget.prototype.onSaveErrorBadToken = function () {
|
||||
this.events.trackSaveError( 'badtoken' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -625,8 +638,6 @@ ve.init.mw.ViewPageTarget.prototype.onSaveErrorCaptcha = function ( editApi ) {
|
|||
this.saveDialog.clearMessage( 'api-save-error' );
|
||||
this.saveDialog.showMessage( 'api-save-error', $captchaDiv );
|
||||
this.saveDialog.popPending();
|
||||
|
||||
this.events.trackSaveError( 'captcha' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -647,7 +658,6 @@ ve.init.mw.ViewPageTarget.prototype.onSaveErrorUnknown = function ( editApi, dat
|
|||
) ),
|
||||
false // prevents reapply
|
||||
);
|
||||
this.events.trackSaveError( 'unknown' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -738,11 +748,11 @@ ve.init.mw.ViewPageTarget.prototype.onViewTabClick = function ( e ) {
|
|||
return;
|
||||
}
|
||||
if ( this.active ) {
|
||||
this.deactivate();
|
||||
this.deactivate( false, 'navigate-read' );
|
||||
// Prevent the edit tab's normal behavior
|
||||
e.preventDefault();
|
||||
} else if ( this.activating ) {
|
||||
this.deactivate( true );
|
||||
this.deactivate( true, 'navigate-read' );
|
||||
this.activating = false;
|
||||
e.preventDefault();
|
||||
}
|
||||
|
@ -1188,6 +1198,7 @@ ve.init.mw.ViewPageTarget.prototype.attachToolbarSaveButton = function () {
|
|||
* @fires saveWorkflowBegin
|
||||
*/
|
||||
ve.init.mw.ViewPageTarget.prototype.showSaveDialog = function () {
|
||||
this.emit( 'saveWorkflowBegin' );
|
||||
this.surface.getDialogs().getWindow( 'mwSave' ).then( function ( win ) {
|
||||
var currentWindow = this.surface.getContext().getInspectors().getCurrentWindow(),
|
||||
target = this;
|
||||
|
@ -1227,7 +1238,6 @@ ve.init.mw.ViewPageTarget.prototype.showSaveDialog = function () {
|
|||
.always( function ( opened ) {
|
||||
opened.always( target.onSaveDialogClose.bind( target ) );
|
||||
} );
|
||||
this.emit( 'saveWorkflowBegin' );
|
||||
}.bind( this ) );
|
||||
};
|
||||
|
||||
|
@ -1525,7 +1535,7 @@ ve.init.mw.ViewPageTarget.prototype.onWindowPopState = function ( e ) {
|
|||
}
|
||||
if ( this.active && newUri.query.veaction !== 'edit' ) {
|
||||
this.actFromPopState = true;
|
||||
this.deactivate();
|
||||
this.deactivate( false, 'navigate-back' );
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1748,6 +1758,7 @@ ve.init.mw.ViewPageTarget.prototype.onBeforeUnload = function () {
|
|||
*/
|
||||
ve.init.mw.ViewPageTarget.prototype.switchToWikitextEditor = function ( discardChanges ) {
|
||||
if ( discardChanges ) {
|
||||
ve.track( 'mwedit.abort', { type: 'switchwithout', mechanism: 'navigate' } );
|
||||
this.submitting = true;
|
||||
location.href = this.viewUri.clone().extend( {
|
||||
action: 'edit',
|
||||
|
@ -1756,7 +1767,10 @@ ve.init.mw.ViewPageTarget.prototype.switchToWikitextEditor = function ( discardC
|
|||
} else {
|
||||
this.serialize(
|
||||
this.docToSave || ve.dm.converter.getDomFromModel( this.surface.getModel().getDocument() ),
|
||||
this.submitWithSaveFields.bind( this, { wpDiff: 1, veswitched: 1 } )
|
||||
function () {
|
||||
ve.track( 'mwedit.abort', { type: 'switchwith', mechanism: 'navigate' } );
|
||||
this.submitWithSaveFields( { wpDiff: 1, veswitched: 1 } );
|
||||
}.bind( this )
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -277,6 +277,14 @@ ve.init.mw.Target.static.iconModuleStyles = [
|
|||
*/
|
||||
ve.init.mw.Target.static.name = 'mwTarget';
|
||||
|
||||
/**
|
||||
* Type of integration. Used by ve.init.mw.trackSubscriber.js for event tracking.
|
||||
* @static
|
||||
* @property {string}
|
||||
* @inheritable
|
||||
*/
|
||||
ve.init.mw.Target.static.integrationType = 'page';
|
||||
|
||||
/* Static Methods */
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,15 +23,15 @@ ve.init.mw.TargetEvents = function ( target ) {
|
|||
saveInitiated: 'onSaveInitated',
|
||||
save: 'onSaveComplete',
|
||||
saveReview: 'onSaveReview',
|
||||
saveErrorEmpty: 'onSaveErrorEmpty',
|
||||
saveErrorSpamBlacklist: 'onSaveErrorSpamBlacklist',
|
||||
saveErrorAbuseFilter: 'onSaveErrorAbuseFilter',
|
||||
saveErrorBadToken: 'onSaveErrorBadToken',
|
||||
saveErrorNewUser: 'onSaveErrorNewUser',
|
||||
saveErrorCaptcha: 'onSaveErrorCaptcha',
|
||||
saveErrorUnknown: 'onSaveErrorUnknown',
|
||||
saveErrorEmpty: [ 'trackSaveError', 'empty' ],
|
||||
saveErrorSpamBlacklist: [ 'trackSaveError', 'spamblacklist' ],
|
||||
saveErrorAbuseFilter: [ 'trackSaveError', 'abusefilter' ],
|
||||
saveErrorBadToken: [ 'trackSaveError', 'badtoken' ],
|
||||
saveErrorNewUser: [ 'trackSaveError', 'newuser' ],
|
||||
saveErrorCaptcha: [ 'trackSaveError', 'captcha' ],
|
||||
saveErrorUnknown: [ 'trackSaveError', 'unknown' ],
|
||||
editConflict: [ 'trackSaveError', 'editconflict' ],
|
||||
surfaceReady: 'onSurfaceReady',
|
||||
editConflict: 'onEditConflict',
|
||||
showChanges: 'onShowChanges',
|
||||
showChangesError: 'onShowChangesError',
|
||||
noChanges: 'onNoChanges',
|
||||
|
@ -48,7 +48,12 @@ ve.init.mw.TargetEvents = function ( target ) {
|
|||
*/
|
||||
ve.init.mw.TargetEvents.prototype.track = function ( topic, data ) {
|
||||
data.targetName = this.target.constructor.static.name;
|
||||
ve.track( topic, data );
|
||||
ve.track( 'mwtiming.' + topic, data );
|
||||
|
||||
if ( topic.indexOf( 'performance.system.serializeforcache' ) === 0 ) {
|
||||
// HACK: track serializeForCache duration here, because there's no event for that
|
||||
this.timings.serializeForCache = data.duration;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -59,6 +64,7 @@ ve.init.mw.TargetEvents.prototype.onSaveWorkflowBegin = function () {
|
|||
this.track( 'behavior.lastTransactionTillSaveDialogOpen', {
|
||||
duration: this.timings.saveWorkflowBegin - this.timings.lastTransaction
|
||||
} );
|
||||
ve.track( 'mwedit.saveIntent' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -78,14 +84,22 @@ ve.init.mw.TargetEvents.prototype.onSaveInitated = function () {
|
|||
this.track( 'behavior.saveDialogOpenTillSave', {
|
||||
duration: this.timings.saveInitiated - this.timings.saveWorkflowBegin
|
||||
} );
|
||||
ve.track( 'mwedit.saveAttempt' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Track when document save is complete
|
||||
* Track when the save is complete
|
||||
* @param {string} content
|
||||
* @param {string} categoriesHtml
|
||||
* @param {number} newRevId
|
||||
*/
|
||||
ve.init.mw.TargetEvents.prototype.onSaveComplete = function () {
|
||||
ve.init.mw.TargetEvents.prototype.onSaveComplete = function ( content, categoriesHtml, newRevId ) {
|
||||
this.track( 'performance.user.saveComplete', { duration: ve.now() - this.timings.saveInitiated } );
|
||||
this.timings.saveRetries = 0;
|
||||
ve.track( 'mwedit.saveSuccess', {
|
||||
timing: ve.now() - this.timings.saveInitiated + ( this.timings.serializeForCache || 0 ),
|
||||
'page.revid': newRevId
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -95,11 +109,36 @@ ve.init.mw.TargetEvents.prototype.onSaveComplete = function () {
|
|||
* @param {string} type Text for error type
|
||||
*/
|
||||
ve.init.mw.TargetEvents.prototype.trackSaveError = function ( type ) {
|
||||
this.track( 'performance.user.saveError', {
|
||||
var key,
|
||||
// Maps mwtiming types to mwedit types
|
||||
typeMap = {
|
||||
badtoken: 'userBadToken',
|
||||
newuser: 'userNewUser',
|
||||
abusefilter: 'extensionAbuseFilter',
|
||||
captcha: 'extensionCaptcha',
|
||||
spamblacklist: 'extensionSpamBlacklist',
|
||||
empty: 'responseEmpty',
|
||||
unknown: 'responseUnknown',
|
||||
editconflict: 'editConflict'
|
||||
},
|
||||
// Types that are logged as performance.user.saveError.{type}
|
||||
// (for historical reasons; this sucks)
|
||||
specialTypes = [ 'editconflict' ];
|
||||
|
||||
key = 'performance.user.saveError';
|
||||
if ( specialTypes.indexOf( type ) !== -1 ) {
|
||||
key += '.' + type;
|
||||
}
|
||||
this.track( key, {
|
||||
duration: ve.now() - this.timings.saveInitiated,
|
||||
retries: this.timings.saveRetries,
|
||||
type: type
|
||||
} );
|
||||
|
||||
ve.track( 'mwedit.saveFailure', {
|
||||
type: typeMap[type] || 'responseUnknown',
|
||||
timing: ve.now() - this.timings.saveInitiated + ( this.timings.serializeForCache || 0 )
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -119,55 +158,6 @@ ve.init.mw.TargetEvents.prototype.onSaveReview = function () {
|
|||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* 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, {
|
||||
|
@ -175,16 +165,6 @@ ve.init.mw.TargetEvents.prototype.onSurfaceReady = function () {
|
|||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
|
105
modules/ve-mw/init/ve.init.mw.trackSubscriber.js
Normal file
105
modules/ve-mw/init/ve.init.mw.trackSubscriber.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*!
|
||||
* VisualEditor MediaWiki event subscriber.
|
||||
*
|
||||
* Subscribes to ve.track() events and routes them to mw.track().
|
||||
*
|
||||
* @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
( function () {
|
||||
|
||||
var lastEventWithAction = {},
|
||||
editingSessionId = mw.user.generateRandomSessionId();
|
||||
|
||||
function getDefaultTiming( action, data, now ) {
|
||||
switch ( action ) {
|
||||
case 'init':
|
||||
// Account for second opening
|
||||
return now - Math.max(
|
||||
Math.floor( window.mediaWikiLoadStart ),
|
||||
lastEventWithAction.saveSuccess || 0,
|
||||
lastEventWithAction.abort || 0
|
||||
);
|
||||
case 'ready':
|
||||
return now - lastEventWithAction.init;
|
||||
case 'saveIntent':
|
||||
return now - lastEventWithAction.ready;
|
||||
case 'saveAttempt':
|
||||
return now - lastEventWithAction.saveIntent;
|
||||
case 'saveSuccess':
|
||||
case 'saveFailure':
|
||||
// HERE BE DRAGONS: the caller must compute these themselves
|
||||
// for sensible results. Deliberately sabotage any attempts to
|
||||
// use the default by returning -1
|
||||
mw.log.warn( 've.init.mw.trackSubscriber: Do not rely on default timing value for saveSuccess/saveFailure' );
|
||||
return -1;
|
||||
case 'abort':
|
||||
switch ( data.type ) {
|
||||
case 'preinit':
|
||||
return now - lastEventWithAction.init;
|
||||
case 'nochange':
|
||||
case 'switchwith':
|
||||
case 'switchwithout':
|
||||
case 'abandon':
|
||||
return now - lastEventWithAction.ready;
|
||||
case 'abandonMidsave':
|
||||
return now - lastEventWithAction.saveAttempt;
|
||||
}
|
||||
}
|
||||
mw.log.warn( 've.init.mw.trackSubscriber: Unrecognized action', action );
|
||||
return -1;
|
||||
}
|
||||
|
||||
ve.trackSubscribeAll( function ( topic, data ) {
|
||||
data = data || {};
|
||||
var newData, action, now = Math.floor( ve.now() ), prefix = topic.substr( 0, topic.indexOf( '.' ) );
|
||||
if ( prefix === 'mwtiming' ) {
|
||||
// Legacy TimingData events
|
||||
// Map timing.foo --> ve.foo
|
||||
topic = 've.' + topic.substr( prefix.length + 1 );
|
||||
} else if ( prefix === 'mwedit' ) {
|
||||
// Edit schema
|
||||
action = topic.split( '.' )[1];
|
||||
if ( action === 'init' ) {
|
||||
// Regenerate editingSessionId
|
||||
editingSessionId = mw.user.generateRandomSessionId();
|
||||
}
|
||||
newData = $.extend( {
|
||||
version: 1,
|
||||
action: action,
|
||||
editor: 'visualeditor',
|
||||
platform: 'desktop', // FIXME
|
||||
integration: ve.init.target && ve.init.target.constructor.static.integrationType || 'page',
|
||||
'page.id': mw.config.get( 'wgArticleId' ),
|
||||
'page.title': mw.config.get( 'wgPageName' ),
|
||||
'page.ns': mw.config.get( 'wgNamespaceNumber' ),
|
||||
'page.revid': mw.config.get( 'wgRevisionId' ),
|
||||
'page.length': -1, // FIXME
|
||||
editingSessionId: editingSessionId,
|
||||
'user.id': mw.user.getId(),
|
||||
'user.editCount': mw.config.get( 'wgUserEditCount', 0 )
|
||||
}, data );
|
||||
|
||||
if ( mw.user.isAnon() ) {
|
||||
newData['user.class'] = 'IP';
|
||||
}
|
||||
|
||||
newData['action.' + action + '.type'] = data.type;
|
||||
newData['action.' + action + '.mechanism'] = data.mechanism;
|
||||
newData['action.' + action + '.timing'] = data.timing !== undefined ?
|
||||
Math.floor( data.timing ) : getDefaultTiming( action, data, now );
|
||||
// Remove renamed properties
|
||||
delete newData.type;
|
||||
delete newData.mechanism;
|
||||
delete newData.timing;
|
||||
|
||||
data = newData;
|
||||
topic = 'event.Edit';
|
||||
lastEventWithAction[action] = now;
|
||||
}
|
||||
|
||||
mw.track( topic, data );
|
||||
} );
|
||||
|
||||
} )();
|
Loading…
Reference in a new issue