 			@y = _.clamp @y, r?.y?[0], r?.y?[1]
 
 
+	Dispatcher = class @Dispatcher
+
+		@errors:
+			invalidEventType: 'event type must be a non-empty string'
+			invalidListener: 'event listener must be a function'
+
+		constructor: (@owner) ->
+			@listeners = {}
+
+		trigger: (type, data) ->
+			throw Dispatcher.errors.invalidEventType if typeof type isnt 'string' or not type
+			time = new Date().getTime()
+			data = _.extend data ? {}, {type, time}
+			setTimeout =>
+				listener?.call? @owner, data for listener in @listeners?[type] ? []
+
+		on: (type, listener) ->
+			throw Dispatcher.errors.invalidEventType if typeof type isnt 'string' or not type
+			throw Dispatcher.errors.invalidListener if typeof listener isnt 'function'
+			@listeners[type] ?= []
+			if 0 > @listeners[type].indexOf listener
+				@listeners[type].push listener
+
+		off: (type, listener) ->
+			if typeof type is 'function'
+				listener = type
+				type = undefined
+
+			if type
+				if listener
+					index = @listeners[type].indexOf listener
+					@listeners[type].splice index, 1 if index >= 0
+				else
+					delete @listeners[type]
+			else
+				if listener
+					_.each @listeners, (listeners) =>
+						index = listeners.indexOf listener
+						listeners.splice index, 1 if index >= 0
+
+		once: (type, listener) ->
+			throw Dispatcher.errors.invalidEventType if typeof type isnt 'string' or not type
+			throw Dispatcher.errors.invalidListener if typeof listener isnt 'function'
+			once = (listener) =>
+				harness = (data) =>
+					@off type, harness
+					listener?.call? @owner, data
+			@on type, once listener
+
+
+
 	@errors:
 		selectorEmpty: 'slider element selector must return at least one element'
 		elementInvalid: 'slider first argument must be a selector or an element'

		isMobile: 'ontouchstart' of window

		delay: (d, f) -> setTimeout f, d

		addClass: (e, c) -> e?.classList?.add c



		Slider.instances?.push? @

		@events = new Dispatcher @

		_.addClass @element, 'slider'
		_.addClass @element, @options.orientation

