Jump to content

Instant Diffs/API

From mediawiki.org
This page is a translated version of the page Instant Diffs/API and the translation is 100% complete.
The logo's colors symbolize added and removed content in revision diffs.
The logo's colors symbolize added and removed content in revision diffs.
Instant Diffs using the light color scheme in Vector 2022
Instant Diffs using the dark color scheme in Vector 2022

Tato stránka poskytuje dokumentaci pro vývojáře a pokročilé uživatele modulu Instant Diffs , který zlepšuje uživatelský zážitek při prohlížení rozdílů a revizí napříč projekty MediaWiki. Zde naleznete informace o API, vlastních akcích, konfiguraci odkazů a možnostech integrace.

Stav aplikace

Jednotka Instant Diffs, dostupná jako argument window.instantDiffs nebo id v háčku instantDiffs.ready, zpřístupňuje následující vlastnosti:

Úvodní příznaky

Property Type Description
isRunning boolean Set to true when the script starts initializing. Used to prevent multiple concurrent instances.
isFirstRun boolean Set to true during the initial content processing run, and reset to false on subsequent runs.
isRunCompleted boolean Set to true after the initial content processing run has finished.
isReady boolean Set to true after the script has fully initialized and is ready to process content.
isReplaced boolean Set to true if a standalone script instance was replaced by a newer non-standalone instance.
isUnloading boolean Set to true when the tab is being unloaded. Script activity is paused while this flag is set.
isPageAdjustmentsApplied boolean Set to true after page-specific adjustments have been applied for the current MediaWiki page type.

Modules and data

Property Type Description
config Object The active configuration object, containing selectors, labels, page lists, and other settings.
local Object Local runtime variables, including assembled link selectors, special page aliases, server names, and observer instances.
timers Object Script timer loggers used for performance tracking.
i18n Object Localized strings map, keyed by language code.
utils Object Utility functions used internally by Instant Diffs.
view Object The active view instance managing the dialog window.
settings Object The active settings instance managing user preferences.
modules Object Exported classes and instances available for advanced integration. See Advanced integration.

Hooks

Instant Diffs fires the following mw.hook events that scripts and gadgets can listen to. All hook names are prefixed with instantDiffs.

Hook Argument Description
instantDiffs.ready id — the Instant Diffs singleton Fires once after Instant Diffs has fully initialized. The safest entry point for accessing id.modules and registering integrations. Only fires if the gadget is enabled by the user.
instantDiffs.process $context — a jQuery context Fires to trigger link processing within a given container. Can be fired externally to process newly added content, or listened to for observing when processing is triggered. See Simple integration.
instantDiffs.processed links — an array of Link instances Fires after each processing cycle completes, passing all successfully processed Link instances in the current batch.
instantDiffs.link.renderSuccess link — a Link instance Fires after a link is successfully rendered. Useful for post-processing individual links after they appear in the page.
instantDiffs.link.renderError link — a Link instance Fires after a link fails to render due to an API error or invalid data.
instantDiffs.navigation.complete navigation — a Navigation instance Fires each time the navigation bar is rendered in the dialog. The primary hook for registering custom actions. See Custom actions.
instantDiffs.page.ready page — a Page instance Fires when the page content and navigation are rendered and hooks are about to fire. Fires before wikipage.content and wikipage.diff.
instantDiffs.page.complete page — a Page instance Fires after all page hooks have fired, including wikipage.content and wikipage.diff.
instantDiffs.page.renderSuccess page — a Page instance Fires after the page content is successfully rendered.
instantDiffs.page.renderError page — a Page instance Fires after the page fails to render due to a request error.
instantDiffs.page.renderComplete page — a Page instance Fires after rendering completes regardless of success or failure, after instantDiffs.page.renderSuccess or instantDiffs.page.renderError.
instantDiffs.page.beforeDetach page — a Page instance Fires before the page is detached from the DOM, for example when the dialog is closed or a new page is loaded in the same dialog.
instantDiffs.page.detach page — a Page instance Fires after the page is detached from the DOM.

This section is intended for developers who want to integrate Instant Diffs link actions into their own scripts or gadgets. There are two levels of integration: simple integration using data attributes and mw.hook process hooks, and advanced integration by creating Link class instances directly. Both approaches support the same set of link options.

Simple integration

The simplest way to integrate Instant Diffs is by adding data attributes directly to your link elements. Instant Diffs will automatically detect and process them on the next wikipage.content or instantDiffs.process hook call.

Use data-instantdiffs-link to set the link behavior:

<a href="https://en.wikipedia.org/w/index.php?diff=1&oldid=2" data-instantdiffs-link="basic">diff</a>

Use data-instantdiffs-options to pass a JSON object with additional link options:

<a href="https://en.wikipedia.org/w/index.php?diff=42&oldid=37"
  data-instantdiffs-link="basic"
  data-instantdiffs-options='{"showLink": false}'>diff</a>

To trigger processing of links in a specific container, fire the instantDiffs.process hook with a jQuery context:

mw.hook( 'instantDiffs.process' ).fire( $( '.my-container' ) );

Mark container as line

You can mark any container element with the data-instantdiffs-line attribute to enable line highlighting when a link inside it is opened in the dialog. This is useful when rendering custom lists or tables that contain diff or revision links.

<ul>
    <li data-instantdiffs-line>
        <a href="https://en.wikipedia.org/w/index.php?diff=42&oldid=37" data-instantdiffs-link>diff</a>
    </li>
</ul>

Marking a container as a line also changes the default link behavior to 'basic' for all links inside it.

<ul>
    <li data-instantdiffs-line="all">
        <a href="https://en.wikipedia.org/w/index.php?diff=42&oldid=37">diff</a>
    </li>
</ul>

Setting data-instantdiffs-line to 'all' will automatically process all links inside the container, without requiring data-instantdiffs-link on each link.

Advanced integration

For more control, you can access the Link class directly via the instantDiffs.ready hook and create instances manually. This approach is safe to use in your own gadgets and scripts — the hook only fires if Instant Diffs is enabled by the user, so your code will not run for users who do not have the gadget enabled.

The Link class is available via the hook callback argument:

mw.hook( 'instantDiffs.ready' ).add( ( id ) => {
    const { Link } = id.modules;
} );

Alternatively, it can be accessed via the global window.instantDiffs object if Instant Diffs is already initialized:

const { Link } = window.instantDiffs.modules;

To create a Link instance for a specific element:

mw.hook( 'instantDiffs.ready' ).add( ( id ) => {
    const { Link } = id.modules;

    const node = document.querySelector( 'a.my-diff-link' );
    const link = new Link( node, {
        behavior: 'basic',
        showLink: false,
        onLoad: ( link ) => {
            console.log( 'Dialog loaded for:', link.getArticle().get( 'title' ) );
        },
    } );
} );

You can also use the static Link.findLinks method to process all diff and revision links within a given container, skipping any already processed by Instant Diffs. The method uses the same link selector ruleset that Instant Diffs uses internally to match supported diff and revision links across MediaWiki interface pages.

mw.hook( 'instantDiffs.ready' ).add( ( id ) => {
    const { Link } = id.modules;

    const $container = $( '.my-container' );
    Link.findLinks( $container ).each( ( i, node ) => {
        if ( Link.hasLink( node ) ) return;
        new Link( node, { behavior: 'basic' } );
    } );
} );

Open dialog programmatically

You can open the Instant Diffs dialog for any diff or revision without a link element, by creating a ViewButton instance directly and calling openDialog(), which returns a Promise that resolves when the dialog has opened. This is useful when you want to trigger the dialog from your own UI elements or scripts.

mw.hook( 'instantDiffs.ready' ).add( ( id ) => {
    const { ViewButton } = id.modules;

    const button = new ViewButton( {
        article: {
            oldid: 42,
            diff: 'prev',
        },
    } );
    button.openDialog();
} );

You can also open the dialog for an existing link element by creating a Link instance and calling openDialog() directly, which also returns a Promise that resolves when the dialog has opened:

mw.hook( 'instantDiffs.ready' ).add( ( id ) => {
    const { Link } = id.modules;

    const node = document.querySelector( 'a.my-diff-link' );
    const link = Link.getLink( node ) || new Link( node );
    link.openDialog();
} );

The article option of ViewButton accepts key-value pairs passed to the Article constructor. The following values are commonly used:

Option Type Description
diff number|string The revision ID of the newer revision in a diff. Accepts a numeric ID or a direction string: 'prev', 'next', or 'cur'.
oldid number The revision ID of the older revision in a diff, or the revision ID when viewing a single revision.
direction string Fallback direction when oldid is empty. Accepted values: 'prev', 'next', 'cur'.
curid number The page ID, used to open the latest revision of a page.
title string The page title, used together with diff or oldid to identify the page.
hostname string The hostname of a foreign wiki, used to open diffs from other Wikimedia projects. For example: 'en.wikipedia.org'.
page1 string First page title for a Special:ComparePages diff. Requires title to be set to 'Special:ComparePages'.
page2 string Second page title for a Special:ComparePages diff. Requires title to be set to 'Special:ComparePages'.
rev1 number First revision ID for a Special:ComparePages diff. Requires title to be set to 'Special:ComparePages'.
rev2 number Second revision ID for a Special:ComparePages diff. Requires title to be set to 'Special:ComparePages'.
hash string A URL fragment to scroll to after the dialog opens.
section string A section name appended to the page title link.

The following options can be passed to the Link constructor or via the data-instantdiffs-options attribute.

Option Type Default Description
behavior string 'basic' Link behavior type. Accepted values:
  • 'basic' — renders a link action button (❖ for diffs or ✪ for revisions) with applied styles;
  • 'request' — requests additional data for the diff or revision and renders both a link action button and a page action button (➔) after the link;
  • 'event' — attaches a click handler directly to the existing link without rendering an actions panel;
  • 'none' — skips processing entirely.
insertMethod string 'insertAfter' Where to embed the actions panel relative to the link. Accepted values:
  • 'insertAfter' — embeds the panel after the link;
  • 'insertBefore' — embeds the panel before the link.
showLink boolean false[a] Whether to render the main action button. When false, the click handler is attached directly to the existing link element instead.
showPageLink boolean true[a] Whether to render the page navigation action button. Only applicable for 'request' behavior.
showAltTitle boolean false Whether to display the original link title instead of the title generated by Instant Diffs.
useAltKey boolean true Whether Alt+Click opens the link normally in the current tab when the click handler is attached directly to the link.
setClasses boolean|string true Whether to add Instant Diffs CSS classes to the existing link element. Accepted values:
  • true — adds classes based on the current behavior;
  • false — disables class addition entirely;
  • 'always' — forces classes regardless of behavior;
  • 'clear' — applies a minimal unstyled class set.
onRequest Function Callback fired before the dialog loads. Receives the Link instance as its argument.
onLoad Function Callback fired after the dialog content loads. Receives the Link instance as its argument.
onOpen Function Callback fired after the dialog opens. Receives the Link instance as its argument.
onClose Function Callback fired after the dialog closes. Receives the Link instance as its argument.

See also

Custom actions

This section describes how to add custom actions to the Instant Diffs Actions menu using the instantDiffs.navigation.complete hook. The hook fires each time the navigation bar is rendered, passing a Navigation instance as its argument. Custom actions are registered via navigation.addCustomAction() and appear in the Actions menu of the dialog.

The simplest way to add a custom action is by registering a link with an href. No click handler is needed — Instant Diffs will render the action as a direct link in the Actions menu.

/**
 * Add a custom "What links here" action to Instant Diffs navigation.
 */
mw.hook( 'instantDiffs.navigation.complete' ).add( ( navigation ) => {
	if ( !navigation ) return;

	const title = navigation.getArticle().get( 'title' );
	if ( !title ) return;

	navigation.addCustomAction( {
		name: 'whatLinksHere',
		label: 'What links here',
		icon: 'linkExternal',
		href: mw.util.getUrl( `Special:WhatLinksHere/${ title }` ),
	} );
} );

Call simple handler

This example shows how to register an action with a synchronous click handler. The handler receives the action button instance as widget, performs an operation, and returns focus to the button when done.

/**
 * Add a custom "Copy info link" action to Instant Diffs navigation.
 * Copies a link to the page information to the clipboard.
 */
mw.hook( 'instantDiffs.navigation.complete' ).add( ( navigation ) => {
	if ( !navigation ) return;

	const { utils } = instantDiffs;
	const { Article } = instantDiffs.modules;

	/**
	 * Handle the "Copy info link" action click
	 */
	const handler = ( widget ) => {
		// Get article data
		const article = widget.getArticle();
		const title = article.get( 'title' );

		if ( !title ) return;

		// Build absolute URL to page information
		const url = mw.util.getUrl( title, { action: 'info' } );
		const href = Article.utils.getHrefAbsolute( article, url );

		// Copy link to clipboard
		utils.clipboardWriteLink( href );

		// Return focus to the action button
		navigation.focusAction( widget );
	};

	// Register the custom action
	navigation.addCustomAction( {
		name: 'copyInfoLink',
		label: 'Copy info link',
		icon: 'copy',
		handler: handler,
	} );
} );

Call advanced handler

This example demonstrates a full asynchronous integration where the handler receives the action button instance as widget, shows a loading state, makes an API request, handles errors, and dynamically updates the button after a successful response.

/**
 * Add a custom "Examine change" action to Instant Diffs navigation.
 * Searches for abuse log entries near the current revision timestamp.
 */
mw.hook( 'instantDiffs.navigation.complete' ).add( ( navigation ) => {
	if ( !navigation ) return;

	const { Api, Article } = instantDiffs.modules;

	/**
	 * Handle the "Examine change" action click
	 */
	const handler = ( widget ) => {
		// Get article data
		const article = widget.getArticle();
		const title = article.get( 'title' );
		const timestamp = article.get( 'timestamp' );

		if ( !title || !timestamp ) {
			onRequestError( widget, 'Missing title or timestamp.' );
			return;
		}

		// Calculate timestamp range (±30 seconds from revision time)
		const revisionDate = new Date( timestamp );
		const startDate = new Date( revisionDate.getTime() - 30000 );  // 30 seconds before
		const endDate = new Date( revisionDate.getTime() + 30000 );    // 30 seconds after

		// Show loading state
		navigation.pendingAction( widget, true );

		// Query abuse log for entries near this revision
		const params = {
			action: 'query',
			list: 'abuselog',
			afldir: 'newer',
			afltitle: title,
			afluser: article.get( 'user' ),
			aflstart: startDate.toISOString(),
			aflend: endDate.toISOString(),
			afllimit: 1,
			aflprop: 'ids',
			format: 'json',
			formatversion: 2,
			uselang: mw.config.get( 'wgUserLanguage' ),
			errorformat: 'html',
		};

		// Article argument is specified to support foreign wikis as well
		Api.get( params, article )
			.then( ( data ) => onRequestDone( widget, data ) )
			.fail( ( error, data ) => onRequestError( widget, Api.getApi.getErrorMessage( data ) ) );
	};

	/**
	 * Handle a successful API response
	 */
	const onRequestDone = ( widget, data ) => {
		// Clear loading state
		navigation.pendingAction( widget, false );

		// Extract first abuse log entry
		const entry = data && data.query && data.query.abuselog && data.query.abuselog[ 0 ];
		if ( !entry ) {
			onRequestError( widget, 'No abuse log entries found for this revision.' );
			return;
		}

		// Build URL to abuse filter examination page
		const article = widget.getArticle();
		const url = mw.util.getUrl( `Special:AbuseFilter/examine/log/${ entry.id }` );
		const href = Article.utils.getHrefAbsolute( article, url );

		// Update all instances of this action with the found URL
		navigation.eachCustomActionWidget( widget, ( actionWidget ) => {
			actionWidget
				.setHref( href )
				.setHandler();  // Remove handler since we now have a direct link
		} );

		// Navigate to the examination page
		navigation.execAction( widget );
	};

	/**
	 * Handle API error or no results
	 */
	const onRequestError = ( widget, message ) => {
		// Clear loading state
		navigation.pendingAction( widget, false );

		// Show error notification
		mw.notify( message, {
			type: 'error',
			tag: widget.getOption( 'name' ),
		} );

		// Return focus to the action button
		navigation.focusAction( widget );
	};

	// Load icon styles
	mw.loader.load( [ 'oojs-ui.styles.icons-editing-citation' ] );

	// Register the custom action
	navigation.addCustomAction( {
		name: 'examineAbuseFilter',
		label: 'Examine change',
		icon: 'journal',
		handler: handler,
	} );
} );

Action widget

Each action's handler function receives a MenuButton instance as its first argument, referred to as widget in the examples above. MenuButton extends OO.ui.ButtonWidget, so all standard OOUI button options and methods are available, including setHref(), setDisabled(), setLabel(), focus(), and others. In addition, MenuButton provides the following methods:

Method Description
getArticle() Returns the Article instance associated with the button, providing access to diff and revision metadata.
getOption( name ) Returns a configuration option by name.
getOptions() Returns all configuration options.
setHandler( handler, useAltKey ) Sets a click handler on the button. Pass no arguments to remove the current handler.
execHandler() Programmatically triggers the button's click handler.
setLink( linkOptions ) Creates a Link instance around the button element. Accepts the same options as described in Link options.
setPending( value ) Toggles a pending state on the button.

The following methods of the Navigation instance are available for use in custom action handlers. These methods accept the original unprefixed action name — the custom- prefix is handled internally:

Method Description
addCustomAction( options ) Registers a custom action in the Actions menu. See available options below.
getCustomAction( widgetOrName ) Returns all registered instances of the specified custom action. Accepts a MenuButton instance or an unprefixed action name string.
getCustomActionWidget( widgetOrName ) Returns all widget instances of the specified custom action. Accepts a MenuButton instance or an unprefixed action name string.
eachCustomAction( widgetOrName, handler ) Iterates over all registered instances of the specified custom action and calls handler with each entry. Accepts a MenuButton instance or an unprefixed action name string.
eachCustomActionWidget( widgetOrName, handler ) Iterates over all widget instances of the specified custom action and calls handler with each widget. Accepts a MenuButton instance or an unprefixed action name string. Useful for updating all placements of an action after an async operation.

The following general Navigation methods are useful for managing button state and focus within handlers. When referencing a custom action by name, the full prefixed name must be used, e.g. custom-whatLinksHere:

Method Description
focusAction( widgetOrName ) Hides the Actions menu and sets focus on the specified button. Accepts a MenuButton instance or a full action name string.
pendingAction( widgetOrName, value ) Sets the loading state of the specified button. Pass true to show and false to hide. Accepts a MenuButton instance or a full action name string.
execAction( widgetOrName ) Programmatically triggers a click on the specified button if it is not disabled. Accepts a MenuButton instance or a full action name string.
toggleActions( value ) Toggles the visibility of the Actions menu. Pass true to show and false to hide.
getArticle() Returns the Article instance associated with the current diff or revision.

The following options can be passed to navigation.addCustomAction():

Option Type Description
name string A unique action identifier. The prefix custom- is automatically prepended to the name internally, so getCustomAction() and related methods should always be called with the original unprefixed name.
label string The action label displayed in the Actions menu.
icon string An OOUI icon name to display on the button.
href string A URL to navigate to when the action is clicked. When provided without a handler, the action renders as a direct link.
pending boolean Whether to set a pending state.
pin boolean Whether to pin the action button outside the Actions menu. Overrides the user's pin setting for this action.
handler Function A click handler function. Receives the MenuButton instance as its first argument.
useAltKey boolean Whether Alt+Click bypasses the handler and follows the href directly.
setLink boolean Whether to create a Link instance around the button element, enabling Instant Diffs dialog behavior on the button's href.
linkOptions Object Configuration options for the Link instance created when setLink is true. Accepts the same options as described in Link options.

See also

Notes

  1. 1.0 1.1 Default value reflects the current user setting.