UNPKG

nope-js-node

Version:

NoPE Runtime for Nodejs. For Browser-Support please use nope-browser

345 lines (344 loc) 11.9 kB
"use strict"; /** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2020-11-23 08:06:30 * @modify date 2021-10-19 17:55:35 * @desc [description] */ Object.defineProperty(exports, "__esModule", { value: true }); exports.NopeEventEmitter = void 0; const operators_1 = require("rxjs/operators"); const getSubject_1 = require("../helpers/getSubject"); const idMethods_1 = require("../helpers/idMethods"); const runtimeMethods_1 = require("../helpers/runtimeMethods"); const getLogger_1 = require("../logger/getLogger"); const logger = (0, getLogger_1.getNopeLogger)("obervable"); /** * RsJX based Observable. * * Contains additional Functionalities like: * - property with the current value * - function to publish values. (wrapper for next) * - enables performing a subscription with synced call or a immediate call. */ class NopeEventEmitter { get getter() { return this._getter; } set getter(_getter) { this._getter = _getter; } emit(value, options = {}) { return this._emit(value, options); } /** * Function to set the content of the Observable * @param value * @param sender * @param timeStamp * @param data */ _emit(value, options = {}) { let _value = value; // Change the Value. if (this.setter !== null) { const adapted = this.setter(value, options); if (!adapted.valid) { return false; } _value = adapted.data; } _value = this.getter !== null ? this.getter(_value) : _value; // Publish the data. if (options.forced || this.disablePublishing === false) { options = this._updateSenderAndTimestamp(options); // Define the value. this._emitter.next({ value: _value, ...options }); return this.hasSubscriptions; } return false; } /** * Helper to update the Timestamp and sender * * @author M.Karkowski * @protected * @param {IEventAdditionalData} options * @return {*} {ISetContentOptions} * @memberof NopeObservable */ _updateSenderAndTimestamp(options) { // Define a Sender if required if (options.sender === undefined) { options.sender = this.id; } // Generate a Timestamp if required. if (this.options.generateTimeStamp === true) { options.timestamp = options.timestamp === undefined ? Date.now() : options.timestamp; } // Return the adapted element. return options; } /** * Function, used to dispose the observable. * Every item will be unsubscribed. */ dispose() { for (const _unsubscribe of this._subscriptions) { _unsubscribe(); } this._subscriptions.clear(); this._emitter.closed = true; } /** * A Function to subscribe to updates of the Observable. * @param observer The Observer. Could be a Function or a Partial Observer. * @param mode The Mode of the Subscription * @param options Additional Options. */ subscribe(observer, options = { type: "sync", mode: ["direct", "sub", "super"], }) { options.skipCurrent = !!this._options.showCurrent && !this._options.playHistory; return this._subscribe(observer, options); } _subscribe(observer, options = { type: "sync", mode: ["direct", "sub", "super"], }) { const _this = this; let active = true; let _observer; let _first = true; if (typeof observer === "object") { _observer = { next: (data) => { // Make shure we are skipping the current Item, if desired if (_first && options.skipCurrent) { _first = false; return; } _first = false; if (active && data !== undefined && observer.next) { const { value, ...rest } = data; switch (options.type) { case "immediate": (0, runtimeMethods_1.callImmediate)(observer.next, value, rest); break; default: observer.next(value, rest); break; case "sync": observer.next(value, rest); break; } } }, complete: () => { if (observer.complete) { observer.complete(); } }, error: (error) => { if (observer.error) { observer.error(error); } }, }; } else if (typeof observer === "function") { _observer = { next: (data) => { // Make shure we are skipping the current Item, if desired if (_first && options.skipCurrent) { _first = false; return; } _first = false; if (active && data !== undefined) { const { value, ...rest } = data; switch (options.type) { case "immediate": (0, runtimeMethods_1.callImmediate)(observer, value, rest); break; default: observer(value, rest); break; case "sync": observer(value, rest); break; } } }, complete: () => { // Nothing to do here }, error: (error) => { logger.error(""); logger.error(error); }, }; } // Create a Subscription. const subscription = this._emitter.subscribe(_observer); const ret = Object.assign(subscription, { options, pause: () => { active = false; }, unpause: () => { active = true; }, }); return ret; } /** * Create an enhanced Subscription of the Observable. Use the Pipes, to * Define what should be subscribed. * @param next The Next Function, used to transmit changes * @param options The Options, used to determine the Enhancements. */ enhancedSubscription(next, options = {}) { let observable = this; if (options.pipe) { observable = options.pipe(options.scope, this._emitter.pipe((0, operators_1.map)((value) => { return value.value; }))); } const subscription = observable.subscribe({ next, }); return subscription; } /** * Creates a Subscription for the value of the Observable. After one Update the Value will be deleted * @param func Function which is called when new Datas are pushed * @param mode Mode of the Subscription * @param options Additional Options */ once(func, options) { let ret = null; ret = this.subscribe({ next: (...args) => { ret.unsubscribe(); func(...args); }, }, options); return ret; } /** * Async Function to Wait for an Update * @param mode Mode of the Subscription * @param options Additional Options for the Wait Function. */ waitFor(testCallback = (value) => { return value == true; }, options = { testCurrent: true }) { const _this = this; let resolved = false; let subscription = null; let timeout = null; return new Promise((resolve, reject) => { const finish = (error, test, data) => { // Reject the error. if (error) { reject(error); } if (timeout) { clearTimeout(timeout); } // Unsubscribe the Subscription. if (test && subscription) { subscription.unsubscribe(); subscription = null; } if (test && !resolved) { // Mark the Task as Resolved. resolved = true; resolve(data); } }; let first = true; const checkData = (data, opts) => { if ((first && options.testCurrent) || !first) { // Create a promise of the data const prom = Promise.resolve(testCallback(data, opts)); // Now we link the element prom.catch((e) => { finish(e, false, data); }); prom.then((r) => { finish(false, r, data); }); } first = false; }; try { subscription = _this.subscribe((data, opts) => { checkData(data, opts); }); if ((options === null || options === void 0 ? void 0 : options.timeout) > 0) { timeout = setTimeout(() => { finish(Error("Timeout.!"), false, null); }, options.timeout); } } catch (e) { reject(e); } }); } /** * Async Function to Wait for an Update * @param mode Mode of the Subscription * @param options Additional Options for the Wait Function. */ waitForUpdate(options) { const _this = this; return new Promise((resolve, reject) => { try { _this.once((content) => { resolve(content); }, options); } catch (e) { reject(e); } }); } get hasSubscriptions() { return this._emitter.observed; } get observerLength() { return this._emitter.observers.length; } constructor(_options = {}) { this._options = _options; this.id = (0, idMethods_1.generateId)(); this.options = { generateTimeStamp: true, }; /** * Function to specify a Setter */ this.setter = null; /** * Function to specify a Getter */ this._getter = null; /** * A Set containing the Subscriptions */ this._subscriptions = new Set(); /** * Flag to Disable Publishing */ this.disablePublishing = false; this._emitter = (0, getSubject_1.getSubject)(_options); } } exports.NopeEventEmitter = NopeEventEmitter;