UNPKG

neft

Version:

Universal Platform

272 lines (195 loc) 6.77 kB
# Signal > events-like Signal is a function with listeners which can be emitted. Access it with: ```javascript const { signal } = Neft; ``` 'use strict' utils = require 'src/utils' assert = require 'src/assert' # *Integer* STOP_PROPAGATION Special constant used to stop calling further listeners. Must be returned by the listener function. ```javascript var obj = {}; signal.create(obj, 'onPress'); obj.onPress(function(){ console.log('listener 1'); return signal.STOP_PROPAGATION; }); // this listener won't be called, because first listener will capture this signal obj.onPress(function(){ console.log('listener 2'); }); obj.onPress.emit(); // listener 1 ``` STOP_PROPAGATION = exports.STOP_PROPAGATION = 1 << 30 # *Signal* create([*NotPrimitive* object, *String* name]) Creates a new signal in the given object under the given name property. Returns created signal. ```javascript var obj = {}; signal.create(obj, 'onRename'); obj.onRename.connect(function(){ console.log(arguments); }); obj.onRename.emit('Max', 'George'); // {0: "Max", 1: "George"} ``` exports.create = (obj, name) -> signal = createSignalFunction obj if name is undefined return signal assert.isNotPrimitive obj, 'signal object cannot be primitive' assert.isString name, 'signal name must be a string' assert.notLengthOf name, 0, 'signal name cannot be an empty string' assert not obj.hasOwnProperty(name) , "signal object has already defined the '#{name}' property" obj[name] = signal # *Boolean* isEmpty(*Signal* signal) Returns `true` if the given signal has no listeners. exports.isEmpty = (signal) -> for func in signal.listeners by 2 if func isnt null return false return true # **Class** Signal callSignal = (obj, listeners, arg1, arg2) -> i = 0 n = listeners.length result = 0 containsGaps = false while i < n func = listeners[i] if func is null containsGaps = true else ctx = listeners[i + 1] if result <= 0 and func.call(ctx or obj, arg1, arg2) is STOP_PROPAGATION result = STOP_PROPAGATION if containsGaps break i += 2 if containsGaps shift = 0 while i < n func = listeners[i] if func is null shift -= 2 else if shift > 0 assert.isNotDefined listeners[i + shift] assert.isNotDefined listeners[i + shift + 1] listeners[i + shift] = func listeners[i + shift + 1] = listeners[i + 1] listeners[i] = null listeners[i + 1] = null i += 2 return result createSignalFunction = (obj) -> handler = (listener, ctx) -> handler.connect listener, ctx handler.obj = obj handler.listeners = [] utils.setPrototypeOf handler, SignalPrototype handler SignalPrototype = ## Signal::emit([*Any* argument1, *Any* argument2]) Call all of the signal listeners with the given arguments (2 maximally). emit: (arg1, arg2) -> assert.isFunction @, 'emit must be called on a signal function' assert.isArray @listeners, 'emit must be called on a signal function' assert.operator arguments.length, '<', 3, 'signal accepts maximally two parameters; use object instead' callSignal @obj, @listeners, arg1, arg2 ## Signal::connect(*Function* listener, [*Any* context]) Adds the given listener function into the signal listeners. By default, the signal function works like this method. ```javascript var obj = {}; signal.create(obj, 'onPress'); obj.onPress(function(){ console.log('listener 1'); }); obj.onPress.connect(function(){ console.log('listener 2'); }); obj.onPress.emit() // listener 1 // listener 2 ``` The given context will be used as a context in listener calling. By default, the listener is called with the object on which the signal is created. ```javascript var obj = {standard: true}; signal.create(obj, 'onPress'); var fakeContext = {fake: true}; obj.onPress(function(){ console.log(this); }, fakeContext); obj.onPress(function(){ console.log(this); }); obj.onPress.emit(); // {fake: true} // {standard: true} ``` connect: (listener, ctx = null) -> assert.isFunction @, 'connect must be called on a signal function' assert.isFunction listener, 'listener is not a function' {listeners} = @ i = n = listeners.length while (i -= 2) >= 0 if listeners[i] isnt null break if i + 2 is n listeners.push listener, ctx else assert.isNotDefined listeners[i + 2] assert.isNotDefined listeners[i + 3] listeners[i + 2] = listener listeners[i + 3] = ctx return ## Signal::disconnect(*Function* listener, [*Any* context]) Returns the given listener function from the signal listeners. ```javascript var obj = {}; signal.create(obj, 'onPress'); var listener = function(){ console.log('listener called!'); }; obj.onPress.connect(listener); obj.onPress.disconnect(listener); obj.onPress.emit() // no loggs... ``` disconnect: (listener, ctx = null) -> assert.isFunction @, 'disconnect must be called on a signal function' assert.isFunction listener, 'listener is not a function' {listeners} = @ index = 0 loop index = listeners.indexOf listener, index if index is -1 or listeners[index + 1] is ctx break index += 2 assert.isNot index, -1, "listener doesn't exist in this signal" assert.is listeners[index], listener assert.is listeners[index + 1], ctx listeners[index] = null listeners[index + 1] = null return ## Signal::disconnectAll() Removes all the signal listeners. disconnectAll: -> assert.isFunction @, 'disconnectAll must be called on a signal function' {listeners} = @ for _, i in listeners listeners[i] = null return exports.Emitter = require('./emitter') create: exports.create createSignalFunction: createSignalFunction callSignal: callSignal # Glossary - [Signal](#class-signal)