Skip to content

Instantly share code, notes, and snippets.

@alextkachman
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alextkachman/8d0ad2bc21307b689819 to your computer and use it in GitHub Desktop.
Save alextkachman/8d0ad2bc21307b689819 to your computer and use it in GitHub Desktop.
Any object can be event target
angular.module('EventTarget',[])
.run(['$injector',function($injector){
function removeFromList(list,item) {
var next = item.next || null
var prev = item.prev || null
if (prev) {
prev.next = next
}
else {
list.first = next
}
if (next) {
next.prev = prev
}
else {
list.last = prev
}
item.next = item.prev = null
return next
}
Object.defineProperties(Object.prototype, {
$event: {
value: function(name) {
var that = this,
capitalized = name.charAt(0).toUpperCase() + name.substr(1)
that['on' + capitalized] = function(handler,context) {
// NOTICE: if context is scope handler life cycle will be bound to it
return this.$addEventListener(name,function(){
handler.apply(this, Array.prototype.slice.call(arguments, 1, arguments.length))
},context)
}
that['fireOn' + capitalized] = function() {
var args = Array.prototype.slice.call(arguments)
args.unshift(name);
return this.$dispatchEvent.apply(this, args)
}
},
writable: true
},
$dispatchEvent: {
value: function(ev) {
var listeners = this.$fire$listeners,
evListeners
if (listeners && (evListeners = listeners[ev])) {
var item
evListeners.inProgress++
for(item = evListeners.first; item; item = item.next) {
if (!item.removed) {
try {
item.handler.apply(item.context, arguments)
}
catch(e) {
$injector.get('$exceptionHandler')(e)
}
}
}
if (!--evListeners.inProgress) {
if (evListeners.hasRemoved) {
for(item = evListeners.first; item;) {
if (item.removed) {
item = removeFromList(evListeners, item)
if (!--evListeners.hasRemoved) {
break
}
}
else {
item = item.next
}
}
if (!evListeners.first) {
delete listeners[ev]
if (!--listeners._count) {
// no other event types
delete this.$fire$listeners
}
}
}
}
}
},
writable: true
},
$addEventListener: {
value: function(ev,handler,context) {
var that = this;
var listeners = that.$fire$listeners || (that.$fire$listeners = {_count:0});
var evListeners = (listeners[ev] || (listeners._count++, listeners[ev] = {
inProgress: 0,
hasRemoved: 0
}))
var item = {
handler: handler,
context: context || null
};
if (!evListeners.first) {
evListeners.last = evListeners.first = item
}
else {
evListeners.last.next = item
item.prev = evListeners.last
evListeners.last = item
}
// returns remove function
var remove = function () {
if (!item.removed) {
item.removed = true
if (!evListeners.inProgress) {
removeFromList(evListeners, item)
if (!evListeners.first) {
delete listeners[ev]
if (!--listeners._count) {
// no other event types
delete that.$fire$listeners
}
}
}
else {
evListeners.hasRemoved++
}
}
};
// if context is Scope bind to it's life cycle
context && context.$$asyncQueue && context.$on('$destroy',remove)
return remove
},
writable: true
}
});
}])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment