Requires Javascript v1.5
Author: jpbarto (jpbarto.freeshell.org)
This page is meant as a temporary placeholder until I can get around to authoring a proper webpage. In the meantime if you like here is a link to JSignal's project page and here is a link to the library's JSDoc documentation.
JSignal is a loose port of the signal system implemented by GTK's GLib. It uses signals and handlers to allow for the registration of callback functions to events.
To use JSignal those classes which will emit signals (cause events) must register the signals that each class will emit. Note that if a class extends another class (has a superclass) it will inherit any signals registered by the parent class and can emit those signals as well. In addition a new 'global'-type of signal can be registered which is class non-specific. Any class can emit a global signal.
After you have a class with registered signals you can begin connecting callback functions to those signals. Callback functions are connected (registered to listen for) to signals by specifying the signal and the object instance that will emit that signal; this registration results in the creation of a handler. Multiple handlers can be configured for the same signal on the same object instance. In addition to instance-specific signal handlers a handler can also be registered to execute when a global signal (created using j_signal_new_global) is emitted; again, regardless of emitting instance. Note that when a global signal is emitted by an instance those handlers that are registered globally (using j_signal_handler_connect_global) will be executed BEFORE any instance-specific handlers.
To emit a signal - thus causing an event - the class which registered the signal should use the j_signal_emit function and pass any additional information to the callback function via the event detail argument. This detail object should be an array of name => value pairs.
Illustrative examples are provided below.
Note: In order for signal inheritance to work properly it is recommended that a class's prototype be given a 'parent' member which points to the constructor of the class's superclass.
JSignal is a Javascript port of the signaling system used by the GTK and implemented as part of the GTK's GLib library. (To learn more about the GTK see the section ABOUT GTK.
The signaling system - event system - as implemented by GLib allows for the customization of object behaviour and provides a general purpose mechanism for notification. The basic concept of the GLib signal system is the 'emission' of a signal which causes the creation and handling of an event. Signals are introduced - registered - on a per-class basis and are made available to derived types. This results in an inheritance model whereby a child class inherits the signals registered by its parent superclass.
All signal handlers are called and executed in the order in which they were registered or connected to the emitted signal. Thus the three callback functions connected by
j_signal_handler_connect (obj, "signal_name", signal_cb01); j_signal_handler_connect (obj, "signal_name", signal_cb03); j_signal_handler_connect (obj, "signal_name", signal_cb02);will be executed in the order signal_cb01, signal_cb03, signal_cb02. The emission of a signal can be cancelled at any time by any executed handler that returns a value of boolean false.
In addition to cancelling signal emission by function return value, handlers can also be blocked, unblocked, and disconnected during signal emission. Placing a block on an individual handler will prevent the handler from firing during the current signal emission and future emissions until the handler has been unblocked. In addition the blocks against a handler are cumulative such that the handler with ID 23 in the following code block:
j_signal_handler_block (obj, 23); j_signal_handler_block (obj, 23); j_signal_handler_unblock (obj, 23);will still be blocked until the j_signal_handler_unblock has been called a second time. A handler has to get unblocked exactly the same amount of times it has been blocked before.
Some features of the signal system defined by GLib have not been - yet? - implemented in JSignal. These features are as follows:
The rest of this document attempts to further illustrate the features implemented by JSignal and provide you with the information you need to get you started using the JSignal library.
GTK (GIMP Toolkit) is a software library which provides users a widget set for the development of GUI applications. Originally written in C to support the development of the GNU Image Manipulation Program (GIMP) the GTK has since enjoyed widespread use in multiple languages, on multiple platforms, and in multiple applications.
The GTK as is typically referred to actually refers to a set of three libraries: the GTK, the GDK, and GLib. The first is the highly abstracted set of windowing objects (such as GtkButton, GtkWindow, and GtkLabel). The GDK library is an abstraction & wrapper library for window system calls. The GDK allows the GTK to operate in various windowing environments such as the X window system (XFree86), MS Windows, wxWindows, and V. And finally the GLib library provides common system functions to the GTK and GDK libraries. Again to provide both a layer of abstraction and also to fill in for missing system calls when those calls are not available on a ported platform.
The GTK has been released (as of this writing it is at version 2.8) and is a mature toolkit. It has bindings to many programming languages such as C++, Perl, Python, Ada95, and Java. In addition to the GIMP the GTK has been used to implement many software packages such as GNU Network Object Model Environment (better known as the GNOME Desktop Manager). The GTK has proven itself to be a mature and capable software library for the development of GUI applications using various languages and operating in various environments and windowing systems.
To learn more about the GTK you can visit the project's homepage located on the world wide web at http://www.gtk.org.
The following code example defines a parent class which registers three signals and a child class that registers one signal of its own. Two callback functions are also defined. Then in the main function the callback functions are connected to the signals the user is interested in - followed by manipulation of an instance of the class defined earlier.
Begin by creating the base class - Vehicle. This class will register three signals, one which is emitted when an instance is created, a second that is emitted when the vehicle's speed changes, and a third which is emitted when the vehicle is destroyed.
/* define a base class - Vehicle */ function Vehicle (mph) { /* register my signals */ /* store the signal id of the 'created' signal for future use */ var create_sid = j_signal_new ("created", Vehicle); j_signal_new ("speed_set", Vehicle); j_signal_new ("destroyed", Vehicle); /* if an initial speed was provided then set it */ if (mph) this.setSpeed (mph); else this.speed = 0; /* signal that the object has been instantiated - no details provided */ j_signal_emit (this, create_sid, null); } Vehicle.prototype.speed = 0;
Create a function setSpeed that changes the vehicles speed and emits a signal to notify handlers that the objects speed has been changed.
Vehicle.prototype.setSpeed = function (mph) { /* this function will throw the speed_set signal */ /* build event details for callback funcs */ var detail = new Array (); detail['old_speed'] = this.speed; /* ... change the speed ... */ detail['new_speed'] = this.speed; j_signal_emit_by_name (this, "speed_set", detail); }
Now create an extending class - Motercycle - which begins to specialize the Vehicle class. Much like Vehicle the constructor registers a signal specific to the Motorcycle class and also emits the 'created' signal to notify that an instance has been created.
Note: Notice that the Motorcycle prototype is given a variable 'parent' which is set to the constructor of its superclass Vehicle. This is required in order for JSignal to be able to properly traverse the class inheritance. If signal inheritance is of no interest to you then you do not need to concern yourself with setting the class's parent property.
/* declare an extending class - Motorcycle */ function Motorcycle (gear) { /* register an additional signal - not an automatic after all */ j_signal_new ("gear_set", Motorcycle); /* if an initial gear was provided then set it */ if (gear) this.setGear (gear); else this.gear = 'N'; /* send word that an instance has been created */ j_signal_emit_by_name (this, "created", null); } Motorcycle.prototype = new Vehicle (); Motorcycle.prototype.constructor = Motorcycle; Motorcycle.prototype.parent = Vehicle; Motorcycle.prototype.gear = 'N';
Again the setGear function - like setSpeed - emits a signal to notify any handlers that something about the object has been altered.
Motorcycle.prototype.setGear = function (setting) { var detail = new Array (); detail['old_gear'] = this.gear; /* ... change gears ... */ detail['new_gear'] = this.gear; j_signal_emit_by_name (this, "gear_set", detail); }
Define callback functions to be registered as handlers. The second callback, handle_gearChange, accepts two parameters - the event object AND an object that is defined by the user when the callback function is registered as a handler.
function handle_speedChange (event) { /* ... access event object to obtain event details ... */ /* ... if speed has increased set gear appropriately ... */ } function handle_gearChange (event, udata) { /* ... access event object and do work ... */ }
So now you have two classes with a class hierarchy like:
- Object | -- Vehicle | -- MotorocycleThe Vehicle class has registered some signals and thanks to class inheritance the Motorcylce has access to those signals in addition to the signal(s) it has registered. Each class instance - when it's data changes - issues a signal to notify any registered handlers. Now instantiate a class and manipulate it to watch the handlers fire.
function main () { var m = new Motorcycle ('N'); var rider = "Bill Mann"; /* register callback functions as handlers */ j_signal_handler_connect (m, "speed_set", handle_speedChange); /* this will pass the value 'rider' to the callback function as 'udata' */ /* this object could be anything - an array, a DOM object, another function, etc */ j_signal_handler_connect (m, "gear_set", handle_gearChange, rider); /* this will emit a signal which will cause the handle_speedChange */ /* callback to increase the gear setting. */ m.setSpeed (35); }
A callback function should be prepared to accept up to two parameters. The first will be a reference to a JEvent object which encapsulates information about the signal which was emitted, the object which emitted the signal (initiated the event), and any event details provided by the initiating object instance. The second parameter is the user-specified data object which was registered alongside the handler when the callback was connected to the signal via j_signal_handler_connect. The callback function should return a true or false value. If the value returned is false execution of all additional handlers will cease - preventing subsequent callback functions from being called; if no value is returned or if a value of true is returned any additionally registered handlers will fire. The following is an example of a callback function's definition:
function evt_callback (event, udata) { var signal_id = event.getSignalId (); var signal_name = event.getSignalName (); var origin = event.getOrigin (); var ev_detail = event.getEventDetail (); ... do some work ... // stop any additional handlers from firing return false }