mini-event-emitter
Version:
An easy javascript EventEmitter which has no dependencies and the possibility to group events, log errors and trace events
319 lines (197 loc) • 7.87 kB
text/coffeescript
class MiniEventEmitter
constructor: (@mini, obj) ->
# Define and store settings
@mini.settings =
name : obj?.name || "MiniEventEmitter"
error : obj?.error || false
trace : obj?.trace || false
# Store all events
@mini.events = {}
# Store all groups
@mini.groups = {}
# --------------------------------------------------
# Public available
# --------------------------------------------------
on: (event, group, fn) =>
# Make group optional
[group, fn] = @optional group, fn
# Check if the input is valid if not allow chaining
return @mini if not @valid "on", event, group, fn
# Check if the provided group exists
if (groups = @mini.groups)[group]
# Check if the provided event exists within this group and either push to or create an array with fns
if groups[group][event] then groups[group][event].push fn else groups[group][event] = [fn]
else
# Create the provided group
groups[group] = {}
# Create an array with fn for this group and event
groups[group][event] = [fn]
# Either add a new event or push it to a list with others
if (events = @mini.events)[event] then events[event].push fn else events[event] = [fn]
# Allow chaining
@mini
off: (event, group, fn) =>
# Remove all events, groups and eventListeners
return @removeAll() if arguments.length is 0
# Make group optional
[group, fn] = @optional group, fn
# Check if the input is valid if not allow chaining
return @mini if not @valid "off", event, group, fn
# Provided group does not exist
if not @mini.groups[group]
@error "off", 7, event, group
# Allow chaining
return @mini
# No event (event === null) is provided so remove all events and fns from the provided group
return @removeGroup group if not event
# Event name does not exist for the provided group
if not fns = @mini.groups[group][event]
@error "off", 8, event, group
# Allow chaining
return @mini
# If no fn to remove is provided, remove all fns of the event+group
return @removeFns event, group, fns if not fn
# Provided fn to remove is not found for the provided event and group
if -1 is index = fns.indexOf fn
@error "off", 2, event, group
# Allow chaining
return @mini
# Remove single fn
@removeFn event, group, fns, fn, index
# Allow chaining
@mini
emitIf: =>
@_emit arguments, true
emit: =>
@_emit arguments
_emit: (args, skip) ->
# Turn arguments into an array
args = Array.from args
# Retrieve event name from the provided arguments
event = args.shift()
# Check if the provided event exists
return @mini if not fns = @validEvent event, skip
# Trace the event
@trace event, args
# Excecute all fns of the event
fn.apply fn, args for fn in fns by -1
# Allow chaining
@mini
# --------------------------------------------------
# Helpers
# --------------------------------------------------
valid: (name, event, group, fn) ->
if name is "on"
# Event name MUST be a string
return @error name, 1 if not @isString event
# Group MUST be a string
return @error name, 5 if not @isString group
# Fn MUST be a function
return @error name, 6, event, group if not @isFunction fn
if name is "off"
# Event name MUST be a string OR null
return @error name, 1 if event isnt null and not @isString event
# Group MUST be a string
return @error name, 5 if not @isString group
# Fn MAY be a function
return @error name, 6, event, group if fn and not @isFunction fn
true
validEvent: (event, skip) ->
# Event was not provided
return @error "emit", 3 if not event
# Event name must be a string
return @error "emit", 1 if not @isString event
# Event name does not exist
if not fns = @mini.events[event]
# Send the error with emit but not with emitIf
@error "emit", 4, event if not skip
return null
# Return all found fn's to be excecuted
fns
error: (name, id, event, group) ->
# Prefix all error messages with the MiniEventEmitter text
msg = "#{name} ~ "
if id is 1 then msg += "Event name must be a string"
if id is 2 then msg += "Provided function to remove with event \"#{event}\" in group \"#{group}\" is not found"
if id is 3 then msg += "Event was not provided"
if id is 4 then msg += "EventListener for event \"#{event}\" does not exist"
if id is 5 then msg += "Provided group must be a string"
if id is 6 then msg += "The last param provided with event \"#{event}\" and group \"#{group}\" is expected to be a function"
if id is 7 then msg += "Provided group \"#{group}\" is not found"
if id is 8 then msg += "Event \"#{event}\" does not exist for the provided group \"#{group}\""
# Allow for external code to listen in
@mini.listen "error", event, [msg, name, id, group]
# Only log the message if they are required
return null if not @mini.settings.error
msg = "#{@mini.settings.name} ~ #{msg}"
# Log the message to the console (as a warning if available)
if console then (if console.warn then console.warn msg else console.log msg)
null
optional: (group, fn) ->
# Check if the "actual" group was provided or only an event and fucntion
if not fn? and @isFunction group
# Group contains the fn, so correct accordingly
fn = group
# Set group to default
group = ""
else
# Make sure the group is a string
group = "" if not group
# Return new group and fn
[group, fn]
removeAll: ->
# Removes all MiniEventEmitter's references to events, groups and eventListeners
@mini.events = {}
@mini.groups = {}
# Allow chaining
@mini
removeGroup: (group) ->
# Remove all events of the provided group
@removeFns event, group, fns for event, fns of @mini.groups[group]
# Allow chaining
@mini
removeFns: (event, group, fns) =>
# Loop over all found fns in the group in reverse order to maintain the correct indexes after splice has been performed
@removeFn event, group, fns, fn for fn in fns by -1
# Allow chaining
@mini
removeFn: (event, group, fns, fn, index) =>
# Get the index of the fn in the fns if no index is provided
index = fns.indexOf fn if not index
# Remove fn from groups list
fns.splice index, 1
# If no fns are left within the event of a group remove it (clean up)
delete @mini.groups[group][event] if fns.length is 0
# If no events are left within the group remove it (clean up)
delete @mini.groups[group] if 0 is @objLength @mini.groups[group]
# Get the index of the fn in the eventlist
index = @mini.events[event].indexOf fn
# Remove fn from the eventlist
@mini.events[event].splice index, 1
# If no fns are left within the eventlist remove it (clean up)
delete @mini.events[event] if @mini.events[event].length is 0
trace: (event, args) ->
# Allow for external code to listen in
@mini.listen "trace", event, args
# If traced, all *emited* events will be loged *before* the event is actually emitted
if @mini.settings.trace
# The trace message
msg = "#{@mini.settings.name} ~ trace ~ #{event}"
# Make trace messages blue if the log.debug is available (in browser)
if args.length is 0
# Log without arguments
if console.debug then console.log "%c #{msg}", "color: #13d" else console.log msg
else
# Log arguments as well
if console.debug then console.log "%c #{msg}", "color: #13d", args else console.log msg, args
# Shortcuts
isString: (event) -> typeof event is "string" or event instanceof String
objLength: (obj) -> Object.keys(obj).length
isFunction: (fn) -> typeof fn is "function"
# --------------------------------------------------
# Expose
# --------------------------------------------------
# Require | Browserify, Node
module.exports = MiniEventEmitter
# Distribution | Browser
# window.MiniEventEmitter = MiniEventEmitter