Created
August 13, 2015 09:56
-
-
Save IUnknown68/8ec47268c3739dd1ec4e to your computer and use it in GitHub Desktop.
A hierarchical message loop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//============================================================================== | |
//============================================================================== | |
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