@qooxdoo/framework
Version:
The JS Framework for Coders
261 lines (225 loc) • 8.45 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2011 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Tino Butz (tbtz)
* Martin Wittemann (wittemann)
************************************************************************ */
/**
* Define messages to react on certain channels.
*
* The channel names will be used in the {@link #on} method to define handlers which will
* be called on certain channels and routes. The {@link #emit} method can be used
* to execute a given route on a channel. {@link #onAny} defines a handler on any channel.
*
* *Example*
*
* Here is a little example of how to use the messaging.
*
* <pre class='javascript'>
* var m = new qx.event.Messaging();
*
* m.on("get", "/address/{id}", function(data) {
* var id = data.params.id; // 1234
* // do something with the id...
* },this);
*
* m.emit("get", "/address/1234");
* </pre>
*/
qx.Bootstrap.define("qx.event.Messaging",
{
construct : function()
{
this._listener = {},
this.__listenerIdCount = 0;
this.__channelToIdMapping = {};
},
members :
{
_listener : null,
__listenerIdCount : null,
__channelToIdMapping : null,
/**
* Adds a route handler for the given channel. The route is called
* if the {@link #emit} method finds a match.
*
* @param channel {String} The channel of the message.
* @param type {String|RegExp} The type, used for checking if the executed path matches.
* @param handler {Function} The handler to call if the route matches the executed path.
* @param scope {var ? null} The scope of the handler.
* @return {String} The id of the route used to remove the route.
*/
on : function(channel, type, handler, scope) {
return this._addListener(channel, type, handler, scope);
},
/**
* Adds a handler for the "any" channel. The "any" channel is called
* before all other channels.
*
* @param type {String|RegExp} The route, used for checking if the executed path matches
* @param handler {Function} The handler to call if the route matches the executed path
* @param scope {var ? null} The scope of the handler.
* @return {String} The id of the route used to remove the route.
*/
onAny : function(type, handler, scope) {
return this._addListener("any", type, handler, scope);
},
/**
* Adds a listener for a certain channel.
*
* @param channel {String} The channel the route should be registered for
* @param type {String|RegExp} The type, used for checking if the executed path matches
* @param handler {Function} The handler to call if the route matches the executed path
* @param scope {var ? null} The scope of the handler.
* @return {String} The id of the route used to remove the route.
*/
_addListener : function(channel, type, handler, scope) {
var listeners = this._listener[channel] = this._listener[channel] || {};
var id = this.__listenerIdCount++;
var params = [];
var param = null;
// Convert the route to a regular expression.
if (qx.lang.Type.isString(type))
{
var paramsRegexp = /\{([\w\d]+)\}/g;
while ((param = paramsRegexp.exec(type)) !== null) {
params.push(param[1]);
}
type = new RegExp("^" + type.replace(paramsRegexp, "([^\/]+)") + "$");
}
listeners[id] = {regExp:type, params:params, handler:handler, scope:scope};
this.__channelToIdMapping[id] = channel;
return id;
},
/**
* Removes a registered listener by the given id.
*
* @param id {String} The id of the registered listener.
*/
remove : function(id) {
var channel = this.__channelToIdMapping[id];
var listener = this._listener[channel];
delete listener[id];
delete this.__channelToIdMapping[id];
},
/**
* Checks if a listener is registered for the given path in the given channel.
*
* @param channel {String} The channel of the message.
* @param path {String} The path to check.
* @return {Boolean} Whether a listener is registered.
*/
has : function(channel, path) {
var listeners = this._listener[channel];
if (!listeners || qx.lang.Object.isEmpty(listeners)) {
return false;
}
for (var id in listeners)
{
var listener = listeners[id];
if (listener.regExp.test(path)) {
return true;
}
}
return false;
},
/**
* Sends a message on the given channel and informs all matching route handlers.
*
* @param channel {String} The channel of the message.
* @param path {String} The path to execute
* @param params {Map} The given parameters that should be propagated
* @param customData {var} The given custom data that should be propagated
*/
emit : function(channel, path, params, customData) {
this._emit(channel, path, params, customData);
},
/**
* Executes a certain channel with a given path. Informs all
* route handlers that match with the path.
*
* @param channel {String} The channel to execute.
* @param path {String} The path to check
* @param params {Map} The given parameters that should be propagated
* @param customData {var} The given custom data that should be propagated
*/
_emit : function(channel, path, params, customData)
{
var listenerMatchedAny = false;
var listener = this._listener["any"];
listenerMatchedAny = this._emitListeners(channel, path, listener, params, customData);
var listenerMatched = false;
listener = this._listener[channel];
listenerMatched = this._emitListeners(channel, path, listener, params, customData);
if (!listenerMatched && !listenerMatchedAny) {
qx.Bootstrap.info("No listener found for " + path);
}
},
/**
* Executes all given listener for a certain channel. Checks all listeners if they match
* with the given path and executes the stored handler of the matching route.
*
* @param channel {String} The channel to execute.
* @param path {String} The path to check
* @param listeners {Map[]} All routes to test and execute.
* @param params {Map} The given parameters that should be propagated
* @param customData {var} The given custom data that should be propagated
*
* @return {Boolean} Whether the route has been executed
*/
_emitListeners : function(channel, path, listeners, params, customData)
{
if (!listeners || qx.lang.Object.isEmpty(listeners)) {
return false;
}
var listenerMatched = false;
for (var id in listeners)
{
var listener = listeners[id];
listenerMatched |= this._emitRoute(channel, path, listener, params, customData);
}
return listenerMatched;
},
/**
* Executes a certain listener. Checks if the listener matches the given path and
* executes the stored handler of the route.
*
* @param channel {String} The channel to execute.
* @param path {String} The path to check
* @param listener {Map} The route data.
* @param params {Map} The given parameters that should be propagated
* @param customData {var} The given custom data that should be propagated
*
* @return {Boolean} Whether the route has been executed
*/
_emitRoute : function(channel, path, listener, params, customData)
{
var match = listener.regExp.exec(path);
if (match)
{
var params = params || {};
var param = null;
var value = null;
match.shift(); // first match is the whole path
for (var i=0; i < match.length; i++)
{
value = match[i];
param = listener.params[i];
if (param) {
params[param] = value;
} else {
params[i] = value;
}
}
listener.handler.call(listener.scope, {path:path, params:params, customData:customData});
}
return match != undefined;
}
}
});