UNPKG

@eclipse-scout/core

Version:
200 lines (180 loc) 6.28 kB
/* * Copyright (c) 2010, 2024 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import {arrays, Event, EventHandler, EventListener, objects, scout} from '../index'; import $ from 'jquery'; export type EventSubTypePredicate = (type: Event, subType: string) => boolean; export class EventSupport { protected _eventListeners: EventListener[]; protected _subTypePredicates: EventSubTypePredicate[]; constructor() { this._eventListeners = []; this._subTypePredicates = objects.createMap(); } protected _assertFunc(func: EventHandler) { if (!func) { throw new Error('Missing callback function'); } } /** * Registers the given event handler for the event specified by the type param. * * @param type One or more event names separated by space. * @param func Event handler executed when the event is triggered. An event object is passed to the function as first parameter. * @param origFunc Used internally when func is registered with {@link one}. The property is set on the listener * object so the event-handler can be de-registered by using the original function. */ on(type: string, func: EventHandler, origFunc?: EventHandler): EventListener { this._assertFunc(func); let listener = { type: type, func: func, origFunc: origFunc }; this.addListener(listener); return listener; } /** * Registers the given event handler for the event specified by the type param. * The function will only be called once. After that it is automatically de-registered using {@link off}. * * @param type One or more event names separated by space. * @param func Event handler executed when the event is triggered. An event object is passed to the function as first parameter */ one(type: string, func: EventHandler): EventListener { this._assertFunc(func); let offFunc = event => { this.off(type, offFunc); func(event); }; return this.on(type, offFunc, func); } /** * De-registers the given event handler for the event specified by the type param. * * @param type One or more event names separated by space.<br/> * Important: the string must be equal to the one used for {@link on} or {@link one}. This also applies if a string containing multiple types separated by space was used. * @param func The exact same event handler that was used for registration using {@link on} or {@link one}. * If no handler is specified, all handlers are de-registered for the given type. */ off(type: string, func?: EventHandler) { if (!type && !func) { return; } for (let i = this._eventListeners.length - 1; i >= 0; i--) { let listener = this._eventListeners[i]; let funcMatches = (func === listener.func || func === listener.origFunc); let typeMatches = (type === listener.type); let remove = false; if (func && type) { remove = (funcMatches && typeMatches); } else if (func) { remove = funcMatches; } else { // always type. all other cases have been checked above remove = typeMatches; } if (remove) { this._eventListeners.splice(i, 1); } } } /** * Adds an event handler using {@link one} and returns a promise. * The promise is resolved as soon as the event is triggered. */ when(type: string): JQuery.Promise<Event> { let deferred = $.Deferred(); this.one(type, deferred.resolve.bind(deferred)); return deferred.promise(); } addListener(listener: EventListener) { this._eventListeners.push(listener); } removeListener(listener: EventListener) { arrays.remove(this._eventListeners, listener); } count(type?: string, func?: EventHandler): number { let count = 0; this._eventListeners.forEach(listener => { if (type && type !== listener.type) { return; } if (func && func !== listener.func) { return; } count++; }); return count; } /** * @returns the event types of the registered listeners */ types(): string[] { let types = new Set<string>(); for (const listener of this._eventListeners) { if (listener.type) { types.add(listener.type); } } return Array.from(types); } trigger(type: string, event?: Event) { event = event || {} as Event; event.type = type; // Create copy because firing a trigger might modify the list of listeners let listeners = this._eventListeners.slice(); // Use traditional "for" loop to reduce size of stack trace for (let i = 0; i < listeners.length; i++) { let listener = listeners[i]; if (!listener.type || this._typeMatches(event, listener.type)) { listener.func(event); } } } protected _typeMatches(event: Event, listenerType: string): boolean { let eventType = event.type; let types = listenerType.split(' '); // support for multi type definition 'type1 type2 [...]' for (let i = 0; i < types.length; i++) { if (eventType === types[i]) { return true; } if (this._subTypeMatches(event, types[i])) { return true; } } return false; } protected _subTypeMatches(event: Event, listenerType: string): boolean { if (listenerType.indexOf(':') < 0) { return false; } let parts = listenerType.split(':'); let type = parts[0]; let subType = parts[1]; if (type !== event.type) { return false; } let predicate = this._subTypePredicates[type]; if (!predicate) { return; } return predicate(event, subType); } /** * * @param type the type which could contain a subtype * @param predicate the predicate which will be tested when an event with the given type is triggered. */ registerSubTypePredicate(type: string, predicate: EventSubTypePredicate) { scout.assertParameter('type', type); scout.assertParameter('predicate', predicate); this._subTypePredicates[type] = predicate; } }