From 1b27878256374e786981758c13da3972a5d0d66e Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Sat, 17 Oct 2020 01:28:55 +0200 Subject: [PATCH] Convert `Ajax` to TypeScript Moved the interfaces and types into a separate file to prevent a circular dependency. --- global.d.ts | 1 + package-lock.json | 18 ++ .../files/js/WoltLabSuite/Core/Ajax.js | 195 ++++++++---------- .../files/js/WoltLabSuite/Core/Ajax/Data.js | 4 + .../files/ts/WoltLabSuite/Core/Ajax.ts | 93 +++++++++ .../files/ts/WoltLabSuite/Core/Ajax/Data.ts | 51 +++++ .../ts/WoltLabSuite/Core/Ajax/Request.ts | 53 +---- 7 files changed, 253 insertions(+), 162 deletions(-) create mode 100644 package-lock.json create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ajax/Data.js create mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax.ts create mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax/Data.ts diff --git a/global.d.ts b/global.d.ts index 0410faf60a..70ad473efc 100644 --- a/global.d.ts +++ b/global.d.ts @@ -6,6 +6,7 @@ declare global { interface Window { Devtools?: typeof Devtools; ENABLE_DEBUG_MODE: boolean; + SECURITY_TOKEN: string; WCF_PATH: string; WSC_API_URL: string; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..eed871d56f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,18 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@types/pica": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/pica/-/pica-5.1.1.tgz", + "integrity": "sha512-zSLdgIcZXxqamFwIuogGVM22UwStYhWhHgzXXczW3GwNAv1LZdgL0XYGaufipf/FgB2Jj5jTT0ov0v5TVYnUjA==", + "dev": true + }, + "typescript": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", + "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==", + "dev": true + } + } +} diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ajax.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ajax.js index b1a5e26020..822e6fc365 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ajax.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ajax.js @@ -1,115 +1,88 @@ /** * Handles AJAX requests. - * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @module Ajax (alias) - * @module WoltLabSuite/Core/Ajax + * + * @author Alexander Ebert + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @module Ajax (alias) + * @module WoltLabSuite/Core/Ajax */ -define(['AjaxRequest', 'Core', 'ObjectMap'], function(AjaxRequest, Core, ObjectMap) { - "use strict"; - - var _requests = new ObjectMap(); - - /** - * @exports WoltLabSuite/Core/Ajax - */ - return { - /** - * Shorthand function to perform a request against the WCF-API with overrides - * for success and failure callbacks. - * - * @param {object} callbackObject callback object - * @param {object=} data request data - * @param {function=} success success callback - * @param {function=} failure failure callback - * @return {AjaxRequest} - */ - api: function(callbackObject, data, success, failure) { - // Fetch AjaxRequest, as it cannot be provided because of a circular dependency - if (AjaxRequest === undefined) AjaxRequest = require('AjaxRequest'); - - if (typeof data !== 'object') data = {}; - - var request = _requests.get(callbackObject); - if (request === undefined) { - if (typeof callbackObject._ajaxSetup !== 'function') { - throw new TypeError("Callback object must implement at least _ajaxSetup()."); - } - - var options = callbackObject._ajaxSetup(); - - options.pinData = true; - options.callbackObject = callbackObject; - - if (!options.url) { - options.url = 'index.php?ajax-proxy/&t=' + SECURITY_TOKEN; - options.withCredentials = true; - } - - request = new AjaxRequest(options); - - _requests.set(callbackObject, request); - } - - var oldSuccess = null; - var oldFailure = null; - - if (typeof success === 'function') { - oldSuccess = request.getOption('success'); - request.setOption('success', success); - } - if (typeof failure === 'function') { - oldFailure = request.getOption('failure'); - request.setOption('failure', failure); - } - - request.setData(data); - request.sendRequest(); - - // restore callbacks - if (oldSuccess !== null) request.setOption('success', oldSuccess); - if (oldFailure !== null) request.setOption('failure', oldFailure); - - return request; - }, - - /** - * Shorthand function to perform a single request against the WCF-API. - * - * Please use `Ajax.api` if you're about to repeatedly send requests because this - * method will spawn an new and rather expensive `AjaxRequest` with each call. - * - * @param {object} options request options - */ - apiOnce: function(options) { - // Fetch AjaxRequest, as it cannot be provided because of a circular dependency - if (AjaxRequest === undefined) AjaxRequest = require('AjaxRequest'); - - options.pinData = false; - options.callbackObject = null; - if (!options.url) { - options.url = 'index.php?ajax-proxy/&t=' + SECURITY_TOKEN; - options.withCredentials = true; - } - - var request = new AjaxRequest(options); - request.sendRequest(false); - }, - - /** - * Returns the request object used for an earlier call to `api()`. - * - * @param {Object} callbackObject callback object - * @return {AjaxRequest} - */ - getRequestObject: function(callbackObject) { - if (!_requests.has(callbackObject)) { - throw new Error('Expected a previously used callback object, provided object is unknown.'); - } - - return _requests.get(callbackObject); - } - }; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +define(["require", "exports", "./Ajax/Request"], function (require, exports, Request_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getRequestObject = exports.apiOnce = exports.api = void 0; + Request_1 = __importDefault(Request_1); + const _cache = new WeakMap(); + /** + * Shorthand function to perform a request against the WCF-API with overrides + * for success and failure callbacks. + */ + function api(callbackObject, data, success, failure) { + if (typeof data !== 'object') + data = {}; + let request = _cache.get(callbackObject); + if (request === undefined) { + if (typeof callbackObject._ajaxSetup !== 'function') { + throw new TypeError("Callback object must implement at least _ajaxSetup()."); + } + const options = callbackObject._ajaxSetup(); + options.pinData = true; + options.callbackObject = callbackObject; + if (!options.url) { + options.url = 'index.php?ajax-proxy/&t=' + window.SECURITY_TOKEN; + options.withCredentials = true; + } + request = new Request_1.default(options); + _cache.set(callbackObject, request); + } + let oldSuccess = null; + let oldFailure = null; + if (typeof success === 'function') { + oldSuccess = request.getOption('success'); + request.setOption('success', success); + } + if (typeof failure === 'function') { + oldFailure = request.getOption('failure'); + request.setOption('failure', failure); + } + request.setData(data); + request.sendRequest(); + // restore callbacks + if (oldSuccess !== null) + request.setOption('success', oldSuccess); + if (oldFailure !== null) + request.setOption('failure', oldFailure); + return request; + } + exports.api = api; + /** + * Shorthand function to perform a single request against the WCF-API. + * + * Please use `Ajax.api` if you're about to repeatedly send requests because this + * method will spawn an new and rather expensive `AjaxRequest` with each call. + */ + function apiOnce(options) { + options.pinData = false; + options.callbackObject = null; + if (!options.url) { + options.url = 'index.php?ajax-proxy/&t=' + window.SECURITY_TOKEN; + options.withCredentials = true; + } + const request = new Request_1.default(options); + request.sendRequest(false); + } + exports.apiOnce = apiOnce; + /** + * Returns the request object used for an earlier call to `api()`. + */ + function getRequestObject(callbackObject) { + if (!_cache.has(callbackObject)) { + throw new Error('Expected a previously used callback object, provided object is unknown.'); + } + return _cache.get(callbackObject); + } + exports.getRequestObject = getRequestObject; }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ajax/Data.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ajax/Data.js new file mode 100644 index 0000000000..2ae92b6a8b --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ajax/Data.js @@ -0,0 +1,4 @@ +define(["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); +}); diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax.ts new file mode 100644 index 0000000000..55f0f7b09a --- /dev/null +++ b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax.ts @@ -0,0 +1,93 @@ +/** + * Handles AJAX requests. + * + * @author Alexander Ebert + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @module Ajax (alias) + * @module WoltLabSuite/Core/Ajax + */ + +import AjaxRequest from './Ajax/Request'; +import { CallbackObject, CallbackSuccess, CallbackFailure, RequestData, RequestOptions } from './Ajax/Data'; + +const _cache = new WeakMap(); + +/** + * Shorthand function to perform a request against the WCF-API with overrides + * for success and failure callbacks. + */ +export function api(callbackObject: CallbackObject, data: RequestData, success: CallbackSuccess, failure: CallbackFailure): AjaxRequest { + if (typeof data !== 'object') data = {}; + + let request = _cache.get(callbackObject); + if (request === undefined) { + if (typeof callbackObject._ajaxSetup !== 'function') { + throw new TypeError("Callback object must implement at least _ajaxSetup()."); + } + + const options = callbackObject._ajaxSetup(); + + options.pinData = true; + options.callbackObject = callbackObject; + + if (!options.url) { + options.url = 'index.php?ajax-proxy/&t=' + window.SECURITY_TOKEN; + options.withCredentials = true; + } + + request = new AjaxRequest(options); + + _cache.set(callbackObject, request); + } + + let oldSuccess = null; + let oldFailure = null; + + if (typeof success === 'function') { + oldSuccess = request.getOption('success'); + request.setOption('success', success); + } + if (typeof failure === 'function') { + oldFailure = request.getOption('failure'); + request.setOption('failure', failure); + } + + request.setData(data); + request.sendRequest(); + + // restore callbacks + if (oldSuccess !== null) request.setOption('success', oldSuccess); + if (oldFailure !== null) request.setOption('failure', oldFailure); + + return request; +} + +/** + * Shorthand function to perform a single request against the WCF-API. + * + * Please use `Ajax.api` if you're about to repeatedly send requests because this + * method will spawn an new and rather expensive `AjaxRequest` with each call. + */ +export function apiOnce(options: RequestOptions): void { + options.pinData = false; + options.callbackObject = null; + if (!options.url) { + options.url = 'index.php?ajax-proxy/&t=' + window.SECURITY_TOKEN; + options.withCredentials = true; + } + + const request = new AjaxRequest(options); + request.sendRequest(false); +} + +/** + * Returns the request object used for an earlier call to `api()`. + */ +export function getRequestObject(callbackObject: CallbackObject): AjaxRequest { + if (!_cache.has(callbackObject)) { + throw new Error('Expected a previously used callback object, provided object is unknown.'); + } + + return _cache.get(callbackObject); +} diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax/Data.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax/Data.ts new file mode 100644 index 0000000000..2422a61c25 --- /dev/null +++ b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax/Data.ts @@ -0,0 +1,51 @@ +export interface RequestPayload { + [key: string]: any; +} + +export type RequestData = FormData | RequestPayload; + +export interface ResponseData { + [key: string]: any; +} + +export type CallbackFailure = (data: ResponseData, responseText: string, xhr: XMLHttpRequest, requestData: RequestData) => boolean; +export type CallbackFinalize = (xhr: XMLHttpRequest) => void; +export type CallbackProgress = (event: ProgressEvent) => void; +export type CallbackSuccess = (data: ResponseData, responseText: string, xhr: XMLHttpRequest, requestData: RequestData) => void; +export type CallbackUploadProgress = (event: ProgressEvent) => void; +export type CallbackSetup = () => RequestOptions; + +export interface CallbackObject { + _ajaxFailure?: CallbackFailure; + _ajaxFinalize?: CallbackFinalize; + _ajaxProgress?: CallbackProgress; + _ajaxSuccess: CallbackSuccess; + _ajaxUploadProgress?: CallbackUploadProgress; + _ajaxSetup: CallbackSetup; +} + +export interface RequestOptions { + // request data + data?: RequestData, + contentType?: string, + responseType?: string, + type?: string, + url?: string, + withCredentials?: boolean, + + // behavior + autoAbort?: boolean, + ignoreError?: boolean, + pinData?: boolean, + silent?: boolean, + includeRequestedWith?: boolean, + + // callbacks + failure?: CallbackFailure, + finalize?: CallbackFinalize, + success?: CallbackSuccess, + progress?: CallbackProgress, + uploadProgress?: CallbackUploadProgress, + + callbackObject?: CallbackObject | null, +} diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax/Request.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax/Request.ts index b8fb545a5c..1908550872 100644 --- a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax/Request.ts +++ b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ajax/Request.ts @@ -11,6 +11,7 @@ */ import * as AjaxStatus from './Status'; +import { ResponseData, RequestOptions, RequestData } from './Data'; import * as Core from '../Core'; import DomChangeListener from '../Dom/Change/Listener'; @@ -89,7 +90,7 @@ class AjaxRequest { /** * Dispatches a request, optionally aborting a currently active request. */ - sendRequest(abortPrevious: boolean): void { + sendRequest(abortPrevious?: boolean): void { if (abortPrevious || this._options.autoAbort) { this.abortPrevious(); } @@ -346,54 +347,4 @@ class AjaxRequest { } } -interface RequestPayload { - [key: string]: any; -} - -type RequestData = FormData | RequestPayload; - -interface ResponseData { - [key: string]: any; -} - -type CallbackFailure = (data: ResponseData, responseText: string, xhr: XMLHttpRequest, requestData: RequestData) => boolean; -type CallbackFinalize = (xhr: XMLHttpRequest) => void; -type CallbackProgress = (event: ProgressEvent) => void; -type CallbackSuccess = (data: ResponseData, responseText: string, xhr: XMLHttpRequest, requestData: RequestData) => void; -type CallbackUploadProgress = (event: ProgressEvent) => void; - -interface CallbackObject { - _ajaxFailure?: CallbackFailure; - _ajaxFinalize?: CallbackFinalize; - _ajaxProgress?: CallbackProgress; - _ajaxSuccess: CallbackSuccess; - _ajaxUploadProgress?: CallbackUploadProgress; -} - -interface RequestOptions { - // request data - data?: RequestData, - contentType?: string, - responseType?: string, - type?: string, - url?: string, - withCredentials?: boolean, - - // behavior - autoAbort?: boolean, - ignoreError?: boolean, - pinData?: boolean, - silent?: boolean, - includeRequestedWith?: boolean, - - // callbacks - failure?: CallbackFailure, - finalize?: CallbackFinalize, - success?: CallbackSuccess, - progress?: CallbackProgress, - uploadProgress?: CallbackUploadProgress, - - callbackObject?: CallbackObject | null, -} - export = AjaxRequest; -- 2.20.1