coffeescript-ui
Version:
Coffeescript User Interface System
290 lines (227 loc) • 7.96 kB
text/coffeescript
###
* coffeescript-ui - Coffeescript User Interface System (CUI)
* Copyright (c) 2013 - 2016 Programmfabrik GmbH
* MIT Licence
* https://github.com/programmfabrik/coffeescript-ui, http://www.coffeescript-ui.org
###
class CUI.Listener extends CUI.Element
initOpts: ->
super()
# the type of the event(s) to listen to
type:
mandatory: true
check: (v) ->
CUI.util.isString(v) or CUI.util.isArray(v)
# an optional element to bind this listener to
# if given, the event will only be triggered
node:
default: document.documentElement
mandatory: true
check: (v) ->
CUI.dom.isNode(v)
# call this function when event is triggered
call:
mandatory: true
check: (v) ->
CUI.util.isFunction(v)
# if set only listen once
only_once:
check: Boolean
# passthru for jquery selector
selector:
check: (v) ->
CUI.util.isString(v) or CUI.util.isFunction(v)
# abritrary object to match when "ignore" is called
instance: {}
# catch during capture phase
capture:
default: false
check: Boolean
readOpts: ->
super()
if CUI.util.isString()
= .split(/\s+/)
else
=
= CUI.dom.getNode()
for type in
ev = CUI.Events.getEventType(type)
if CUI.util.isString()
CUI.util.assert( instanceof HTMLElement or == document, "new CUI.Listener", "opts.selector requires the node to be instance of HTMLElement.", opts: )
= (ev) =>
# console.debug "handleDOMEvent", ev.type, , @
if
if CUI.util.isString()
= (target, node) =>
CUI.dom.closestUntil(target, , node)
else
=
@
isCapture: ->
getNode: ->
getTypes: ->
__registerDOMEvent: ->
for _type in
for type in CUI.Events.getEventTypeAliases(_type)
.addEventListener(type, , )
@
__handleDOMEventInternal: (ev) ->
if
# filter this by the selector
# check if from the target up, we match a selector
currentTarget =
if not currentTarget
return false
else
currentTarget =
# this event was synthecially created by CUI.Events.trigger
# or previously handled by an CUI.Listener
# or dispatched by Event.dispatch
if ev.__cui_event
event = ev.__cui_event
# console.error "use event from eventsevent", ev, eventsEvent.getType(), eventsEvent.getUniqueId()
else
event = CUI.Event.createFromDOMEvent(ev)
# store this for the bubble phase, so that this events stays
# the same, throughout its livetime
ev.__cui_event = event
# console.error "create event from DOM event", ev, eventsEvent.getType(), eventsEvent.getUniqueId()
event.setCurrentTarget(currentTarget)
# console.debug "handleDOMEvent", , ev.type, eventsEvent.__cls
if
ret =
else
ret =
return ret
isOnlyOnce: ->
destroy: ->
if
return
CUI.Events.unregisterListener(@)
for _type in
# .removeClass("cui-debug-listen-#{type}")
#
for type in CUI.Events.getEventTypeAliases(_type)
.removeEventListener(type, , )
super()
# returns the distance of the listener node
# to the events node. with no node, return -1
# returns null if no match
matchesEvent: (event) ->
CUI.util.assert(event instanceof CUI.Event, "CUI.Listener.matchesEvent", "event needs to be instance of CUI.Event.")
delete()
if event.getType() not in
return null
# if not
# = -1
# return
ev_node = event.getNode()
= 0
if
# ignore sink events
return null
# if bubble is set to false the event needs to happen inside DOM
# tree in order for us to consider ourselves. if it happened outside
# the DOM tree, our node has already been triggered by the native
# event
if not event.isExcludeSelf() and not event.isBubble() and event.isInDOM()
# if the event bubbles, we are touched by the native event already
# so we can ignore us
if == ev_node
return
# if == document
# console.debug "listener", , DOM.parents(), ev_node
if event.isSink()
for parent in CUI.dom.parents()
++
if parent == ev_node
return
delete()
return null
getDepthFromLastMatchedEvent: ->
# if the calls return promises, we
# return a promise, otherwise we return the last ret
# nothing
handleEvent: (event, phase) ->
CUI.util.assert(event instanceof CUI.Event, "CUI.Listener.handleEvent", "event needs to be instance of CUI.Event", event: event)
event.__setPhase(phase)
event.__setListener(@)
if
# console.debug "destroying one time event listener...", event, @
# this calls "destroy" on us
inst =
if inst and inst instanceof CUI.Element and inst.isDestroyed()
return
# try
ret = .call(@, event, event.getInfo())
# catch ex
# console.error("Handle Event error \"#{ex}\". Type: ", event.getType(), @)
# throw(ex)
# console.debug "CUI.Listener.handleEvent", event, info
if CUI.util.isPromise(ret)
info = event.getInfo()
if not info.__waits
CUI.util.assert(false, "CUI.Listener.handleEvent", "Event \"#{event.getType()}\" to handle was not triggered by CUI.Events.trigger, but instead by a regular DOMEvent.\n\nMake sure that, if your handler returns a Promise, the event is triggered by CUI.Events.trigger.", event: event, listener: @, return: ret)
info.__waits.push(ret)
ret
getInstance: ->
matchesFilter: (filter) ->
if filter instanceof CUI.Listener
return filter == @
CUI.util.assert(CUI.util.isPlainObject(filter), "CUI.Listener.matchesFilter", "filter needs to be PlainObject.")
match = true
filtered = false
if filter.node
filter_node = CUI.dom.getNode(filter.node)
filtered = true
match = !!CUI.dom.closestUntil(, filter_node)
if match and filter.type
filtered = true
if CUI.util.isArray(filter.type)
match = false
for _type in filter.type
match = _type in
if match
break
else
match = filter.type in
if match and filter.call
filtered = true
match = filter.call ==
if match and filter.instance
filtered = true
match = filter.instance ==
CUI.util.assert(filtered, "Listener.matchesFilter", "Filter did not filter anything, make sure you have 'node', 'type', 'call', or 'instance' set.", filter: filter)
return match
: (listener, func) ->
if CUI.util.isPlainObject(listener)
listenerFunc = null
if listener.type not instanceof Array
types = [listener.type]
else
types = listener.type
for type in types
ev = CUI.Events.getEventType(type)
CUI.util.assert(ev, "#{func}", "listener.type needs to be registered", listener: listener)
if ev.listenerClass
CUI.util.assert(not listenerFunc or listenerFunc == ev.listenerClass, "#{func}", "listenerFunction differs for different listener types.", listener: listener)
listenerFunc = ev.listenerClass
else
CUI.util.assert(not listenerFunc or listenerFunc == CUI.Listener, "#{func}", "listenerFunction differs for different listener types.", listener: listener)
listenerFunc = CUI.Listener
listen = new listenerFunc(listener)
else
listen = listener
CUI.util.assert(listen instanceof CUI.Listener, "#{func}", "listener needs to be PlainObject or instance of CUI.Listener.")
listen