Skip to content

Instantly share code, notes, and snippets.

@IUnknown68
Created August 13, 2015 09:56
Show Gist options
  • Save IUnknown68/8ec47268c3739dd1ec4e to your computer and use it in GitHub Desktop.
Save IUnknown68/8ec47268c3739dd1ec4e to your computer and use it in GitHub Desktop.
A hierarchical message loop
//==============================================================================
//==============================================================================
window.MessageLoop = (function(){
//============================================================================
var _Set;
if ('undefined' !== typeof Set) {
_Set = Set;
}
else {
//------------------------------------------------------------------------------
_Set = function() {
};
_Set.prototype = new Array;
//------------------------------------------------------------------------------
_Set.prototype.add = function(item) {
if (-1 === this.indexOf(item)) {
this.push(item);
}
return this;
};
//------------------------------------------------------------------------------
_Set.prototype.delete = function(item) {
var i = this.indexOf(item);
if (-1 !== i) {
this.splice(i,1);
}
return this;
};
//------------------------------------------------------------------------------
Object.defineProperty(_Set.prototype, 'size', {
get: function() {
return this.length;
}
});
}
//============================================================================
var _Map = Map;
//==============================================================================
function Registry() {
this.registries = new _Map();
this.items = new _Set();
}
Registry.prototype = {};
Registry.separator = '.';
//------------------------------------------------------------------------------
Registry.parsePath = function(path) {
return (path instanceof Array)
? path
: ('string' === typeof path)
? path.split(Registry.separator)
: [];
};
//----------------------------------------------------------------------------
Registry.prototype._withChild = function(data, path, fn) {
if (path.length) {
var thisName = path.shift();
var child = this.registries.get(thisName);
if (child) {
child[fn](data, path);
return true;
}
}
return false;
};
//----------------------------------------------------------------------------
Registry.prototype._dispatch = function(message, path) {
this._withChild(message, path, '_dispatch');
// now handle self
this.items.forEach(function(item) {
item(message);
});
return this;
};
//----------------------------------------------------------------------------
Registry.prototype.add = function(item, path) {
path = Registry.parsePath(path);
if (!path.length) {
// add to self
this.items.add(item);
return this;
}
var thisName = path.shift();
var child = this.registries.get(thisName);
if (!child) {
this.registries.set(thisName, child = new Registry());
}
child.add(item, path);
return this;
};
//----------------------------------------------------------------------------
Registry.prototype.delete = function(item, path) {
if ('undefined' === typeof path) {
this.registries.forEach(function(registry) {
registry.delete(item);
});
}
else {
path = Registry.parsePath(path);
if (!this._withChild(item, path, 'delete')) {
this.items.delete(item);
}
}
return this;
};
//----------------------------------------------------------------------------
Registry.prototype.dispatch = function(message) {
return this._dispatch(message,Registry.parsePath(message.name));
}
//============================================================================
function _onPostMessageTimer() {
while(this._messageQueue.length) {
this._registry.dispatch(this._messageQueue.shift());
}
// It's important that the timer id is reset AFTER we processed all messages.
// Otherwise, when new messages get posted while processing the queue, a new
// timer event would be generated.
this._postMessageTimerId = 0;
};
//============================================================================
function MessageLoop() {
var _privates = {
_registry: new Registry(),
_messageQueue: [],
_postMessageTimerId: 0
};
Object.keys(MessageLoopPrototype).forEach(function(key) {
this[key] = _privates[key] = MessageLoopPrototype[key].bind(_privates);
}.bind(this));
_privates._onPostMessageTimer = _onPostMessageTimer.bind(_privates);
}
MessageLoop.prototype = {};
var MessageLoopPrototype = {};
//----------------------------------------------------------------------------
MessageLoopPrototype.on = function(name, fn) {
if ('function' === typeof name) {
fn = name;
name = undefined;
}
this._registry.add(fn, name);
};
//----------------------------------------------------------------------------
MessageLoopPrototype.off = function(name, fn) {
this._registry.delete(fn, name);
};
//----------------------------------------------------------------------------
MessageLoopPrototype.post = function(name, data) {
var message = {name: name, data: data || {}};
this._messageQueue.push(message);
if (!this._postMessageTimerId) {
this._postMessageTimerId = setTimeout(this._onPostMessageTimer, 1);
}
};
//----------------------------------------------------------------------------
MessageLoopPrototype.send = function(name, data) {
var message = {name: name, data: data || {}};
this._registry.dispatch(message);
};
return MessageLoop;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment