From c221a538a149c620f70a375f26e327a129f88098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E5=BF=86?= Date: Fri, 19 Jan 2024 21:44:34 +0800 Subject: [PATCH] feat(mw.hook): add MediaWiki core hooks check other authors/commits at https://github.com/wikimedia-gadgets/types-mediawiki/pull/33 --- mw/hook.d.ts | 249 +++++++++++++++++++++++++++++++++++++- mw/user.d.ts | 320 ++++++++++++++++++++++++------------------------- package.json | 3 +- pnpm-lock.yaml | 16 ++- 4 files changed, 420 insertions(+), 168 deletions(-) diff --git a/mw/hook.d.ts b/mw/hook.d.ts index 468bfbf..19c0f6a 100644 --- a/mw/hook.d.ts +++ b/mw/hook.d.ts @@ -1,5 +1,7 @@ +import {User} from './user'; + /** - * Registry and firing of events. + * An instance of a hook, created via {@link mw.hook mw.hook method}. * * MediaWiki has various interface components that are extended, enhanced * or manipulated in some other way by extensions, gadgets and even @@ -40,7 +42,7 @@ */ interface Hook { /** - * Register a hook handler + * Register a hook handler. * * @param {((...data: T) => any)[]} handler Function to bind. * @return {this} @@ -60,7 +62,7 @@ interface Hook { fire(...data: T): this; /** - * Unregister a hook handler + * Unregister a hook handler. * * @param {((...data: T) => any)[]} handler Function to unbind. * @return {this} @@ -70,16 +72,251 @@ interface Hook { remove(...handler: Array<(...data: T) => any>): this; } +interface PostEditData { + /** + * Message that listeners should use when displaying notifications. + * String for plain text, use array or jQuery object to pass actual nodes. + */ + message?: string | JQuery | HTMLElement[]; + /** + * User that made the edit. + */ + user?: string | User; + /** + * Whether a temporary user account was created. + */ + tempUserCreated?: boolean; +} + +interface SearchIndex { + [k: string]: SearchIndexEntry[]; +} + +interface SearchIndexEntry { + $highlight: JQuery; + $field: JQuery; + $wrapper: JQuery; + $tabPanel: JQuery; +} + +interface EditRecovery { + fieldChangeHandler(): void; +} + declare global { namespace mw { /** * Create an instance of mw.hook. * - * @method hook - * @member mw * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook */ - function hook(event: string): Hook; + function hook( + event: 'apisandbox.formatRequest' + ): Hook< + [ + items: OO.ui.MenuOptionWidget[], + displayParams: object, + rawParams: object, + method: string, + ajaxOptions: JQuery.AjaxSettings, + ] + >; + + /** + * Create an instance of mw.hook, fired after EditRecovery has loaded any recovery data, added event handlers, etc. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'editRecovery.loadEnd'): Hook<[editRecovery: EditRecovery]>; + + /** + * Create an instance of mw.hook. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'htmlform.enhance'): Hook<[$root: JQuery]>; + + /** + * Create an instance of mw.hook, fired after an edit was successfully saved. + * + * Does not fire for null edits. + * + * Code that fires the postEdit hook should first set `wgRevisionId` and `wgCurRevisionId` to the revision associated with the edit that triggered the postEdit hook, then fire the postEdit hook, e.g.: + * + * ``` + * mw.config.set( { + * wgCurRevisionId: data.newrevid, + * wgRevisionId: data.newrevid + * } ); + * // Now fire the hook. + * mw.hook( 'postEdit' ).fire(); + * ``` + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'postEdit'): Hook<[data?: PostEditData]>; + + /** + * Create an instance of mw.hook, fired after the listener for #postEdit removes the notification. + * + * @deprecated + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'postEdit.afterRemoval'): Hook<[]>; + + /** + * Create an instance of mw.hook. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'prefs.search.buildIndex'): Hook<[index: SearchIndex]>; + + /** + * Create an instance of mw.hook, fired when a trusted UI element to perform a logout has been activated. + * + * This will end the user session, and either redirect to the given URL on success, or queue an error message via mw.notification. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'skin.logout'): Hook<[href: string]>; + + /** + * Create an instance of mw.hook, fired when initialization of the filtering interface for changes list is complete. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'structuredChangeFilters.ui.initialized'): Hook<[]>; + + /** + * Create an instance of mw.hook, fired when a portlet is successfully created. + * + * Example usage: + * + * ``` + * mw.hook( 'util.addPortlet' ).add( ( p ) => { + * p.style.border = 'solid 1px black'; + * } ); + * ``` + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'util.addPortlet'): Hook<[portlet: HTMLElement, before: string | undefined]>; + + /** + * Create an instance of mw.hook, fired when a portlet link is successfully created. + * + * Example usage: + * + * ``` + * mw.hook( 'util.addPortletLink' ).add( ( link ) => { + * const span = $( '' ); + * link.appendChild( span ); + * } ); + * ``` + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'util.addPortletLink'): Hook<[item: HTMLLIElement, data: object]>; + + /** + * Create an instance of mw.hook, fired when categories are being added to the DOM. + * + * It is encouraged to fire it before the main DOM is changed (when $content is still detached). However, this order is not defined either way, so you should only rely on $content itself. + * + * This includes the ready event on a page load (including post-edit loads) and when content has been previewed with LivePreview. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.categories'): Hook<[$content: JQuery]>; + + /** + * Create an instance of mw.hook, fired after collapsible content has been initialized. + * + * This gives an option to modify the collapsible behavior. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.collapsibleContent'): Hook<[$collapsible: JQuery]>; + + /** + * Create an instance of mw.hook, fired when wiki content has been added to the DOM. + * + * This should only be fired after $content has been attached. + * + * This includes the ready event on a page load (including post-edit loads) and when content has been previewed with LivePreview. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.content'): Hook<[$content: JQuery]>; + + /** + * Create an instance of mw.hook, fired when a diff is added to a page or dynamically displayed to the user. + * + * Similar to the wikipage.content hook, `$diff` may still be detached when the hook is fired. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.diff'): Hook<[$table: JQuery]>; + + /** + * Create an instance of mw.hook. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.diff.diffTypeSwitch'): Hook<[inlineToggleSwitch: OO.ui.ToggleSwitchWidget]>; + + /** + * Create an instance of mw.hook. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.diff.wikitextBodyUpdate'): Hook<[$wikitextDiffBody: JQuery]>; + + /** + * Create an instance of mw.hook, fired when the editform is added to the edit page. + * + * Similar to the wikipage.content hoo $editForm can still be detached when this hook is fired. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.editform'): Hook<[$editForm: JQuery]>; + + /** + * Create an instance of mw.hook, fired when a page's {@link https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Page_status_indicators status indicators} are being added to the DOM or updated. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.indicators'): Hook<[$indicators: JQuery]>; + + /** + * Create an instance of mw.hook, fired when dynamic changes have been made to the table of contents. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(event: 'wikipage.tableOfContents'): Hook<[sections: any[]]>; + + /** + * Create an instance of mw.hook, fired when the page watch status has changed. + * + * Example usage: + * ``` + * mw.hook( 'wikipage.watchlistChange' ).add( ( isWatched, expiry, expirySelected ) => { + * // Do things + * } ); + * ``` + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook( + event: 'wikipage.watchlistChange' + ): Hook<[isWatched: boolean, expiry: string, expirySelected: string]>; + + /** + * Create an instance of mw.hook. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook + */ + function hook(name: string): Hook; } } diff --git a/mw/user.d.ts b/mw/user.d.ts index f5572d4..777b7d1 100644 --- a/mw/user.d.ts +++ b/mw/user.d.ts @@ -1,168 +1,168 @@ +export interface User { + /** + * @property {Map} + * @see: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-property-options + */ + // TODO: add types for items in the options map + options: mw.Map; + + /** + * @property {mw.Map} + * @see: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-property-tokens + */ + tokens: mw.Map<{ + csrfToken: string; + patrolToken: string; + watchToken: string; + }>; + + /** + * Generate a random user session ID. + * + * This information would potentially be stored in a cookie to identify a user during a + * session or series of sessions. Its uniqueness should not be depended on unless the + * browser supports the crypto API. + * + * Known problems with Math.random(): + * Using the Math.random function we have seen sets + * with 1% of non uniques among 200,000 values with Safari providing most of these. + * Given the prevalence of Safari in mobile the percentage of duplicates in + * mobile usages of this code is probably higher. + * + * Rationale: + * We need about 80 bits to make sure that probability of collision + * on 155 billion is <= 1% + * + * See https://en.wikipedia.org/wiki/Birthday_attack#Mathematics + * n(p;H) = n(0.01,2^80)= sqrt (2 * 2^80 * ln(1/(1-0.01))) + * + * @return {string} 80 bit integer in hex format, padded + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-generateRandomSessionId + */ + generateRandomSessionId(): string; + + /** + * Get the current user's groups + * + * @param {Function} [callback] + * @return {JQuery.Promise} + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getGroups + */ + getGroups(callback?: (groups: string[]) => any): JQuery.Promise; + + /** + * Get the current user's database id + * + * Not to be confused with #id. + * + * @return {number} Current user's id, or 0 if user is anonymous + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getId + */ + getId(): number; + + /** + * Get the current user's name + * + * @return {string|null} User name string or null if user is anonymous + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getName + */ + getName(): string | null; + + /** + * A sticky generateRandomSessionId for the current JS execution context, + * cached within this class (also known as a page view token). + * + * @since 1.32 + * @return {string} 80 bit integer in hex format, padded + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getPageviewToken + */ + getPageviewToken(): string; + + /** + * Get date user registered, if available + * + * @return {boolean|null|Date} False for anonymous users, null if data is + * unavailable, or Date for when the user registered. + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getRegistration + */ + getRegistration(): boolean | null | Date; + + /** + * Get the current user's rights + * + * @param {Function} [callback] + * @return {JQuery.Promise} + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getRights + */ + getRights(callback?: (rights: string[]) => any): JQuery.Promise; + + /** + * Get the current user's name or the session ID + * + * Not to be confused with #getId. + * + * @return {string} User name or random session ID + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-id + */ + id(): string; + + /** + * Whether the current user is anonymous + * + * @return {boolean} + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-isAnon + */ + isAnon(): boolean; + + /** + * Is the user a normal non-temporary registered user? + * + * @return {boolean} + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-isNamed + */ + isNamed(): boolean; + + /** + * Is the user an autocreated temporary user? + * + * @return {boolean} + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-isTemp + */ + isTemp(): boolean; + + /** + * Retrieve a random ID, generating it if needed + * + * This ID is shared across windows, tabs, and page views. It is persisted + * for the duration of one browser session (until the browser app is closed), + * unless the user evokes a "restore previous session" feature that some browsers have. + * + * **Note:** Server-side code must never interpret or modify this value. + * + * @return {string} Random session ID + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-sessionId + */ + sessionId(): string; + + /** + * Get the current user's groups or rights + * + * @private + * @return {JQuery.Promise} + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getUserInfo + */ + getUserInfo(): JQuery.Promise<{ + groups: string[]; + rights: string[]; + }>; +} + declare global { namespace mw { /** - * @class mw.user - * @singleton * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user */ - namespace user { - /** - * @property {Map} - * @see: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-property-options - */ - // TODO: add types for items in the options map - const options: Map; - - /** - * @property {Map} - * @see: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-property-tokens - */ - const tokens: Map<{ - csrfToken: string; - patrolToken: string; - watchToken: string; - }>; - - /** - * Generate a random user session ID. - * - * This information would potentially be stored in a cookie to identify a user during a - * session or series of sessions. Its uniqueness should not be depended on unless the - * browser supports the crypto API. - * - * Known problems with Math.random(): - * Using the Math.random function we have seen sets - * with 1% of non uniques among 200,000 values with Safari providing most of these. - * Given the prevalence of Safari in mobile the percentage of duplicates in - * mobile usages of this code is probably higher. - * - * Rationale: - * We need about 80 bits to make sure that probability of collision - * on 155 billion is <= 1% - * - * See https://en.wikipedia.org/wiki/Birthday_attack#Mathematics - * n(p;H) = n(0.01,2^80)= sqrt (2 * 2^80 * ln(1/(1-0.01))) - * - * @return {string} 80 bit integer in hex format, padded - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-generateRandomSessionId - */ - function generateRandomSessionId(): string; - - /** - * Get the current user's groups - * - * @param {Function} [callback] - * @return {JQuery.Promise} - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getGroups - */ - function getGroups(callback?: (groups: string[]) => any): JQuery.Promise; - - /** - * Get the current user's database id - * - * Not to be confused with #id. - * - * @return {number} Current user's id, or 0 if user is anonymous - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getId - */ - function getId(): number; - - /** - * Get the current user's name - * - * @return {string|null} User name string or null if user is anonymous - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getName - */ - function getName(): string | null; - - /** - * A sticky generateRandomSessionId for the current JS execution context, - * cached within this class (also known as a page view token). - * - * @since 1.32 - * @return {string} 80 bit integer in hex format, padded - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getPageviewToken - */ - function getPageviewToken(): string; - - /** - * Get date user registered, if available - * - * @return {boolean|null|Date} False for anonymous users, null if data is - * unavailable, or Date for when the user registered. - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getRegistration - */ - function getRegistration(): boolean | null | Date; - - /** - * Get the current user's rights - * - * @param {Function} [callback] - * @return {JQuery.Promise} - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getRights - */ - function getRights(callback?: (rights: string[]) => any): JQuery.Promise; - - /** - * Get the current user's name or the session ID - * - * Not to be confused with #getId. - * - * @return {string} User name or random session ID - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-id - */ - function id(): string; - - /** - * Whether the current user is anonymous - * - * @return {boolean} - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-isAnon - */ - function isAnon(): boolean; - - /** - * Is the user a normal non-temporary registered user? - * - * @return {boolean} - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-isNamed - */ - function isNamed(): boolean; - - /** - * Is the user an autocreated temporary user? - * - * @return {boolean} - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-isTemp - */ - function isTemp(): boolean; - - /** - * Retrieve a random ID, generating it if needed - * - * This ID is shared across windows, tabs, and page views. It is persisted - * for the duration of one browser session (until the browser app is closed), - * unless the user evokes a "restore previous session" feature that some browsers have. - * - * **Note:** Server-side code must never interpret or modify this value. - * - * @return {string} Random session ID - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-sessionId - */ - function sessionId(): string; - - /** - * Get the current user's groups or rights - * - * @private - * @return {JQuery.Promise} - * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.user-method-getUserInfo - */ - function getUserInfo(): JQuery.Promise<{ - groups: string[]; - rights: string[]; - }>; - } + const user: User; } } diff --git a/package.json b/package.json index 9b3ca9a..0989f70 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ ], "types": "index.d.ts", "dependencies": { - "@types/jquery": "^3.5.29" + "@types/jquery": "^3.5.29", + "@types/oojs-ui": "^0.47.6" }, "devDependencies": { "dtslint": "^4.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ecffc4..729eb82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ dependencies: '@types/jquery': specifier: ^3.5.29 version: 3.5.29 + '@types/oojs-ui': + specifier: ^0.47.6 + version: 0.47.6 devDependencies: dtslint: @@ -100,6 +103,17 @@ packages: undici-types: 5.26.5 dev: true + /@types/oojs-ui@0.47.6: + resolution: {integrity: sha512-rq24I8qov2hFnfF6PBFx3ECQI81sCr9HqghGpad2LnyUH30j/zHFgtqMqVUSthtxd0Ke4d1ZJmq2Vhg9qJrWrw==} + dependencies: + '@types/jquery': 3.5.29 + '@types/oojs': 7.0.6 + dev: false + + /@types/oojs@7.0.6: + resolution: {integrity: sha512-e6UBEoqJ3bLBvxtPnaFmqkK4HzPwrS3sqfB2dORUmAaShp/T7WhMoYjlwhe+7g748qv7eeX/q4WdOaeeVG2LtA==} + dev: false + /@types/sizzle@2.3.8: resolution: {integrity: sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==} dev: false @@ -1439,7 +1453,7 @@ packages: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} requiresBuild: true dependencies: - string-width: 1.0.2 + string-width: 4.2.3 dev: true optional: true