|
JSignal | |||||||
PREV NEXT | FRAMES NO FRAMES |
Requires Javascript v1.5
Version: 0.2
Author: jpbarto (jpbarto.freeshell.org)
Licensed under the GNU GPL
Copyright (C) 2006 jpbarto
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public License along with this library; if not, download a copy from the world wide web at http://www.gnu.org/copyleft/gpl.html or write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Class Summary | |
UnknownHandlerException | User defined exception, thrown when a specified handler ID is not found within an instance's registry. |
UnregisteredSignalException | User defined exception, thrown when a signal is found registered but not registered for use by the calling object. |
SignalNameExistsException | User defined exception, thrown when an attempt to register a signal name as a global signal collides with an existing single class type signal. |
UnknownSignalException | User defined exception, thrown when a specified signal ID or name is not found within the registry. |
UnknownErrorException | User defined exception, thrown when a system exception is thrown. |
UnknownInstanceException | User defined exception, thrown when a specified instance is not found within the registry. |
JEvent | An object which encapsulates the details of an event where an event is defined as the emission of a signal. |
Method Summary | |
static void
|
j_signal_delete(<int> sid)
Removes a signal from the registry; along with all registered handlers. |
static void
|
j_signal_emit(<Object> obj, <int> sid, <Array> data)
Emits a signal. |
static void
|
j_signal_emit_by_name(<Object> obj, <int> name, <Array> data)
Similiar to j_signal_emit but accepts a signal name instead of a signal identifier. |
static void
|
j_signal_handler_block(<Object> obj, <int> hid)
Temporarily deactivates the specified handler. |
static int
|
j_signal_handler_connect(<Object> obj, <String> name, <Function> cb, <Object> data)
Connects a callback function to a signal for a particular object. |
static int
|
j_signal_handler_connect_global(<String> name, <Function> cb, <Object> data)
Connects a callback function to a signal that is registered as a global signal (created via j_signal_new_global). |
static void
|
j_signal_handler_disconnect(<Object> obj, <int> hid)
Disconnects (deletes) a specified handler. |
static boolean
|
j_signal_handler_is_connected(<Object> obj, <int> hid)
Determines if a specified handler is connected to the given instance. |
static void
|
j_signal_handler_unblock(<Object> obj, <int> hid)
Removes a block placed upon a handler. |
static Array
|
j_signal_list_all_ids(<ObjectType> ctype)
Retrieves a list of signal identifiers for a given class type. |
static Array
|
j_signal_list_ids(<ObjectType> ctype)
Retrieves a list of signal identifiers registered by the given class type. |
static int
|
j_signal_lookup(<String> name, <ObjectType> ctype)
Given the name of the signal and the class it connects to, gets the signal's identifier. |
static String
|
j_signal_name(<int> sid)
Given the signal's identifier returns the signal's name. |
static int
|
j_signal_new(<String> name, <ObjectType> ctype)
Registers a new signal. |
static int
|
j_signal_new_global(<String> name)
Registers a new global signal that can be used (emitted) by any and all objects. |
/** * @fileoverview * <h1>JSignal - A signal handling library</h1> * <h2>A loose Javascript port of the GLib signal system used by the GTK</h2> * <p> * Requires Javascript v1.5 * @version 0.2 * @author jpbarto (jpbarto.freeshell.org) * </p> * * <p> * Licensed under the GNU GPL<br/> * Copyright (C) 2006 jpbarto<br/> * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * </p> * <p> * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * </p> * <p> * You should have received a copy of the GNU Library General Public * License along with this library; if not, download a copy from the * world wide web at http://www.gnu.org/copyleft/gpl.html or write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * </p> */ /*============================================================================== * Exception Classes =============================================================================*/ /** @ignore */ function __J_Exception () {} /** Name / Type of exception. @type String */ __J_Exception.prototype.name = ''; /** Message body of exception. @type String */ __J_Exception.prototype.message = ''; /** @type String */ __J_Exception.prototype.toString = function () { return this.name +': '+ this.message; } /** * @class * User defined exception, thrown when a specified signal ID or name is * not found within the registry. * * @extends __J_Exception */ function UnknownSignalException (id, name) { this.name = 'UnknownSignalException'; if (id || name) { this.message = "No signal was found registered with "; if (id) { this.message += "id = "+ id; }else if (name) { this.message += "name = '"+ name +"'"; } }else{ this.message = "No registered signal was found matching the specified name or ID." } } UnknownSignalException.prototype = new __J_Exception (); /** * @class * User defined exception, thrown when a signal is found registered but not * registered for use by the calling object. * * @extends __J_Exception */ function UnregisteredSignalException (id, name) { this.name = 'UnregisteredSignalException'; if (id || name) { this.message = "No signal was found registered to the specified object's class type with "; if (id) { this.message += "id = "+ id; }else if (name) { this.message += "name = '"+ name +"'"; } }else{ this.message = "The specified signal was not found to be registered under the specified object's class type."; } } UnregisteredSignalException.prototype = new __J_Exception (); /** * @class * User defined exception, thrown when an attempt to register a signal name * as a global signal collides with an existing single class type signal. * To correct this unregister all signals with the same name. * * @extends __J_Exception */ function SignalNameExistsException (id, name) { this.name = "SignalNameExistsException"; if (id || name) { this.message = "The signal with "; if (id) { this.message += "id = "+ id; }else if (name) { this.message += "name = '"+ name +"'"; } this.message += " already exists and is registered for one or more class types."; }else{ this.message = "The specified signal already exists and is registered for one or more class types."; } } SignalNameExistsException.prototype = new __J_Exception (); /** * @class * User defined exception, thrown when a specified handler ID is not * found within an instance's registry. * * @extends __J_Exception */ function UnknownHandlerException (id) { this.name = 'UnknownHandlerException'; if (id) { this.message = "No handler was found registered with id = "+ id; }else{ this.message = "No registered handler was found matching the specified ID." } } UnknownHandlerException.prototype = new __J_Exception (); /** * @class * User defined exception, thrown when a specified instance is not * found within the registry. * * @extends __J_Exception */ function UnknownInstanceException () { this.name = 'UnknownInstanceException'; this.message = 'The reference object instance was not found within registries.'; } UnknownInstanceException.prototype = new __J_Exception (); /** * @class * User defined exception, thrown when a system exception is thrown. May happen * when an object is passed to a JSignal function which is protected as a * WrappedNative prototype. * * @extends __J_Exception */ function UnknownErrorException (exMsg) { this.name = 'UnknownErrorException'; this.message = 'An error was caught by JSignal: '+ exMsg; } UnknownErrorException.prototype = new __J_Exception (); /*============================================================================== * Supporting Classes * * These classes are kept light-weight on purpose to conserve memory - with the * exception of JEvent which will be used by user callback functions. =============================================================================*/ /** * @class * @private * An encapsulation object which represents a 'class' or type of signal. */ function JSignal (sid, name, ctype) { /** The id of the signal. @type int */ this.signalId = sid; /** The name of the signal. @type String */ this.signalName = name || ''; /** The name of the class under which the signal is registered. @type Object Type */ this.classType = ctype || null; } /** * @class * @private * A wrapper and management class for an event handling callback function. * Note that the handler constructor does not verify the signal ID passed to it * for existance or correctness, but just accepts it and stores it blindly. */ function JHandler (sid, hid, func, udata) { /** The id of the singal to be handled. @type int */ this.signalId = sid; /** The id of the handler given an instance. @type int */ this.handlerId = hid; /** A pointer to the callback function. @type Function */ this.cbFunc = func || null; /** A pointer to the user defined data object defined when the handler was connected. @type Object */ this.userData = udata || null; /** A count of the number of blocks against the handler. @type int */ this.blockCount = 0; } /** * @class * An object which encapsulates the details of an event where * an event is defined as the emission of a signal. */ function JEvent (sid, name, obj, detail) { this.signalId = sid; this.signalName = name; this.emitter = obj; this.detail = detail; } /** The id of the signal emitted. @type int */ JEvent.prototype.signalId = -1; /** The name of the signal emitted. @type String */ JEvent.prototype.signalName = ''; /** The object which emitted the signal. @type Object */ JEvent.prototype.emitter = null; /** The event details provided by the emitter. @type Array */ JEvent.prototype.detail = null; /** * Used to obtain the ID of the emitted signal. * * @return the ID of the issued signal * @type int */ JEvent.prototype.getSignalId = function () { return this.signalId; } /** * Used to obtain the name of the emitted signal. * * @return the registered name of the issued signal * @type String */ JEvent.prototype.getSignalName = function () { return this.signalName; } /** * Used to obtain a pointer to the object which emitted * or initiated the event. * * @return A pointer to the instance of the emitting object * @type Object */ JEvent.prototype.getOrigin = function () { return this.emitter; } /** * Used to obtain a reference to the collection of name => value * pairs which were emitted along with the signal. The name / value * pairs are arbitrary and are populated by the originating object instance. * * @return A collection of name => value pairs which were defined / created * by the originating object instance. * @type Array */ JEvent.prototype.getEventDetail = function () { return this.detail; } /*============================================================================== * Signal Management Functions (and variables) =============================================================================*/ /** * @private * A flat array containing a list of all registered signals. * @type Array */ var __j_signals = new Array (); /** * @private * A flat array containing a list of all instances. * @type Array */ var __j_instances = new Array (); /** * @private * An array containing a list of all registered handlers per instance. * @type Array */ var __j_handlers = new Array (); /** * @private * An ever-increasing integer assigned as handler id to the next connected handler. * @type int */ var __j_handlers_next_id = 0; /** * @private * The J Global type and class are used to support the global functions * which enable anonymous signal registration, handling, and emission. */ function __j_global_type () { } var __j_global_obj = new __j_global_type (); /** * Registers a new signal. * * @param {String} name The name of the signal * @param {ObjectType} ctype The class from which this signal will originate. * @return The id of the newly created signal * @type int */ function j_signal_new (name, ctype) { var sig = null; for (var i in __j_signals) { sig = __j_signals[i]; if ((sig.signalName == name) && (sig.classType == ctype)) return i; } /* signal not found in registery, create it */ sig = new JSignal (__j_signals.length, name, ctype); __j_signals.push (sig); return sig.signalId; } /** * Registers a new global signal that can be used (emitted) by any and all objects. * * @param {String} name The name of the signal * @return The id of the newly created signal * @type int */ function j_signal_new_global (name) { var sig = null; /* if the signal exists and IS NOT a foral signal throw an exception */ for (var i in __j_signals) { sig = __j_signals[i]; if ((sig.signalName == name) && (sig.classType != __j_global_type)) throw new SignalNameExistsException (null, name); } return j_signal_new (name, __j_global_type); } /** * Removes a signal from the registry; along with all registered handlers. * * @param {int} sid The signal identifier to be removed. * @type void */ function j_signal_delete (sid) { if (sid < 0) return; var sig = __j_signals[sid]; if (!sig) throw new UnknownSignalException (sid, null); /* delete any registered handlers */ /* for every instance which is of the object type under */ /* which the signal is registered */ for (var i in __j_instances) { var obj = __j_instances[i]; if (obj instanceof sig.classType) { var objHlers = __j_handlers[i]; for (var j in objHlers) { var hler = objHlers[j]; /* if the handler is registered to receive the signal */ if (hler.signalId == sig.signalId) { j_signal_handler_disconnect (obj, hler.handlerId); } } } } /* remove the signal from the registry */ __j_signals.splice (sid, 1); } /** * Connects a callback function to a signal for a particular object. * * @param {Object} obj The instance to register to * @param {String} name The name of the signal to recieve * @param {Function} cb A pointer to the callback function * @param {Object} data Data to be passed to the callback pointer. * @return The handler ID * @type int * @throws UnknownSignalException If the specified signal is unregistered */ function j_signal_handler_connect (obj, name, cb, data) { /* determine if the signal is registered */ var sid = j_signal_lookup (name, obj.constructor); if (sid < 0) throw new UnknownSignalException (null, name); var sig = __j_signals[sid]; if (! sig) throw new UnknownSignalException (null, name); /* the signal has been found, register the handler */ var objHlers = null; var ndex = __j_instances.indexOf (obj); if (ndex < 0) { /* the instance is new to the registry, add it */ __j_instances.push (obj); ndex = __j_instances.indexOf (obj); objHlers = new Array (); __j_handlers[ndex] = objHlers; }else{ objHlers = __j_handlers[ndex]; } var hler = new JHandler (sig.signalId, __j_handlers_next_id++, cb, data); objHlers.push (hler); return hler.handlerId; } /** * Connects a callback function to a signal that is registered as a global * signal (created via j_signal_new_global). The handler will be executed * whenever the signal is emitted regardless of the emitting instance. * * Note that all global handlers will be executed BEFORE instance-specific * handlers. * * @param {String} name The name of the signal to recieve * @param {Function} cb A pointer to the callback function * @param {Object} data Data to be passed to the callback pointer. * @return The handler ID * @type int * @throws UnknownSignalException If the specified signal is unregistered */ function j_signal_handler_connect_global (name, cb, data) { return j_signal_handler_connect (__j_global_obj, name, cb, data); } /** * Temporarily deactivates the specified handler. * * @param {Object} obj The instance on which the handler is defined * @param {int} hid The handler's identifier * @type void * @see #j_signal_handler_unblock * @throws UnknownInstanceException If the specified object has no registered handlers * @throws UnknownHandlerException If the handler is not found within the instance's registry */ function j_signal_handler_block (obj, hid) { var ndex = __j_instances.indexOf (obj); if (ndex < 0) throw new UnknownInstanceException (); var objHlers = __j_handlers[ndex]; if (! objHlers) throw new UnknownInstanceException (); var hler = null; for (var i=0; i < objHlers.length; i++) { hler = objHlers[i]; if (hler.handlerId = hid) { hler.blockCount++; return; } } /* the specified handler was not found */ throw new UnknownHandlerException (hid); } /** * Removes a block placed upon a handler. * * @param {Object} obj The instance on which the handler is defined * @param {int} hid The handler's identifier * @type void * @see #j_signal_handler_block * @throws UnknownInstanceException If the specified object has no registered handlers * @throws UnknownHandlerException If the handler is not found within the instance's registry */ function j_signal_handler_unblock (obj, hid) { var ndex = __j_instances.indexOf (obj); if (ndex < 0) throw new UnknownInstanceException (); var objHlers = __j_handlers[ndex]; if (! objHlers) throw new UnknownInstanceException (); var hler = null; for (var i=0; i < objHlers.length; i++) { hler = objHlers[i]; if (hler.handlerId = hid) { if (--hler.blockCount < 0) hler.blockCount = 0; return; } } /* the specified handler was not found */ throw new UnknownHandlerException (hid); } /** * Emits a signal. * * @param {Object} obj The instance emitting the signal * @param {int} sid The id of the signal to emit * @param {Array} data Collection of name => value pairs detailing the event * @type void * @see #j_signal_emit_by_name * @throws UnknownSignalException If the specified signal is unregistered */ function j_signal_emit (obj, sid, data) { /* ensure the specified signal is registered */ var sig = __j_signals[sid]; if (! sig) throw new UnknownSignalException (sid, null); /* if the signal is not registered universal and the object is not of the right type */ if ((sig.classType != __j_global_type) && ! (obj instanceof sig.classType)) throw new UnregisteredSignalException (sid, null); var objArray = new Array (); if (sig.classType == __j_global_type) { /* load global object handlers to execute first */ objArray.push (__j_global_obj); } objArray.push (obj); for (var i in objArray) { var ndex = __j_instances.indexOf(objArray[i]); /* if instance not found then there are no registered handlers; move to next tmpobj */ if (ndex < 0) continue; var objHlers = __j_handlers[ndex]; /* if the instance has no handlers - do nothing */ if (! objHlers) throw new UnknownInstanceException (); var evt = new JEvent (sig.signalId, sig.signalName, obj, data); for (var i in objHlers) { var hler = objHlers[i]; if (hler.signalId == sig.signalId && hler.blockCount == 0) { if (hler.cbFunc (evt, hler.userData) == false) { /* handler returned false, cease execution of additional handlers */ break; } } } } } /** * Similiar to j_signal_emit but accepts a signal name instead * of a signal identifier. This is slower than emitting the * signal by the identifier as the signal must first be located * within the registry. * * Ancestors of the specified object instance will be searched to * determine if superclasses registered the specified signal. * However this requires that the <pre>obj</pre> parameter has - * within its prototype - a member named <pre>parent</pre> which * points to the constructor of the object's superclass. * * @param {Object} obj The instance emitting the signal * @param {int} name The name of the signal to emit * @param {Array} data Collection of name => value pairs detailing the event * @type void * @see #j_signal_emit * @throws UnknownSignalException If the specified signal is unregistered */ function j_signal_emit_by_name (obj, name, data) { var sid = j_signal_lookup (name, obj.constructor); if (sid < 0) throw new UnknownSignalException (null, name); j_signal_emit (obj, sid, data); } /** * Given the name of the signal and the class it connects * to, gets the signal's identifier. Emitting the signal * by number is typically fater than by name. * * Note: In order for this function to work properly it is required that the * ctype argument have a member variable named 'parent' which is a reference to * the class's superclass constructor. * * @param {String} name The signals name * @param {ObjectType} ctype The type that the signal is registered under * @return The ID of the specified signal; -1 if the signal is not found. * @type int */ function j_signal_lookup (name, ctype) { var sig = null; for (var i in __j_signals) { sig = __j_signals[i]; if (sig.signalName == name) { if (sig.classType == ctype || sig.classType == __j_global_type) return i; /* still haven't found specified class type */ /* if a 'parent' attribute exists check against ancestors */ try{ if (ctype.prototype && ctype.prototype.parent) { var tmpCType = ctype; while (tmpCType) { if (sig.classType == tmpCType) return i; tmpCType = tmpCType.prototype.parent; } } }catch (e) { throw new UnknownErrorException (e.toString ()); } } } return -1; } /** * Given the signal's identifier returns the signal's name. * * @param {int} sid Signal identifier * @return The name of the signal or null if the identifier is invalid. * @type String */ function j_signal_name (sid) { if (sid < __j_signals.length) { var sig = __j_signals[sid]; return sig.signalName; } return null; } /** * Retrieves a list of signal identifiers registered by the given class type. * This function does NOT list those signals registered by the class's supertype. * * @param {ObjectType} ctype A reference to the class constructor * @return Array of signal IDs * @type Array * @see #j_signal_list_all_ids */ function j_signal_list_ids (ctype) { var ret = new Array (); var sig = null; for (var i in __j_signals) { sig = __j_signals[i]; if (sig.classType == ctype) { ret.push (sig.signalId); } } return ret; } /** * Retrieves a list of signal identifiers for a given class type. * * Note: In order for this function to work properly it is required that the * ctype argument have a member variable named 'parent' which is a reference to * the class's superclass constructor. * * @param {ObjectType} ctype A reference to the class constructor * @return Array of signal IDs * @type Array * @see #j_signal_list_ids */ function j_signal_list_all_ids (ctype) { var ret = new Array (); var sig = null; for (var i in __j_signals) { sig = __j_signals[i]; if (sig.classType == ctype || sig.classType == __j_global_type) { ret.push (sig.signalId); }else /* if the prototype has its parent defined check against class lineage */ if (ctype.prototype.parent) { var tmpCType = ctype; while (tmpCType) { if (sig.classType == tmpCType) ret.push(sig.signalId); tmpCType = tmpCType.prototype.parent; } } } return ret; } /** * Disconnects (deletes) a specified handler. * * @param {Object} obj Instance the handler is currently attached to * @param {int} hid Handler identifier * @type void * @throw UnknownInstanceException If the specified instance has no known handlers * @throw UnknownHandlerExcpetion If the specified handler is not found within the instance's registry */ function j_signal_handler_disconnect (obj, hid) { var ndex = __j_instances.indexOf (obj); if (ndex < 0) throw new UnknownInstanceException (); var objHlers = __j_handlers[ndex]; if (! objHlers) throw new UnknownInstanceException (); var hler = null; for (var i in objHlers) { hler = objHlers[i]; if (hler.handlerId == hid) { objHlers.splice (i, 1); /* if the handler list for the instance is empty, delete the instance as well */ if (objHlers.length == 0) { /* remove the array of handlers */ __j_handlers.splice(ndex, 1); /* remove the instance from the list of instances */ __j_instances.splice(ndex, 1); } return; } } /* the specified handler was not found */ throw new UnknownHandlerException (hid); } /** * Determines if a specified handler is connected to the given instance. * @param {Object} obj handler-owning instance * @param {int} hid Handler identifier * @return true if the specified handler is connected to the given instance; false otherwise * @type boolean */ function j_signal_handler_is_connected (obj, hid) { var ndex = __j_instances.indexOf (obj); if (ndex < 0) return false; var objHlers = __j_handlers[ndex]; if (! objHlers) return false; var hler = null; for (var i in objHlers) { hler = objHlers[i]; if (hler.handlerId == hid) return true; } return false; }
|
JSignal | |||||||
PREV NEXT | FRAMES NO FRAMES |