$sum and $val also supports bind.

This commit is contained in:
Julien Fontanet 2014-01-10 17:47:22 +01:00
parent 37c11bfcb2
commit 084ba95b6a

View File

@ -22,12 +22,41 @@ $watch = (collection, {
if: cond
bind
# Initial value.
init
}, fn) ->
# The default value is simply the value of the item.
val ?= -> @val
watcher = {
# Method allowing the cleanup when the helper is no longer used.
cleanUp: -> # TODO: noop for now.
# Keys of items using the current helper.
consumers: Object.create null
generator: ->
{key} = this
# Register this item has a consumer.
watcher.consumers[@key] = true
# Returns the value for this item if any or the common value.
values = watcher.values
if key of values
values["$#{key}"]
else
values.common
# Current values.
values: Object.create null
}
watcher.values.common = init
process = (event, items) ->
values = []
# Values are grouped by namespace.
valuesByNamespace = Object.create null
$_.each items, (item) ->
return unless not cond? or cond.call item
@ -40,10 +69,29 @@ $watch = (collection, {
else
'common'
values.push [event, value, namespace]
(valuesByNamespace[namespace] ?= []).push value
# If something has been processed, call `fn`.
fn values unless $_.isEmpty values
# For each namespace.
for namespace, values of valuesByNamespace
# Updates the value.
value = watcher.values[namespace]
ctx = {
value: if value is undefined then init else value
}
changed = if event is 'enter'
fn.call ctx, values, []
else
fn.call ctx, [], values
# Notifies watchers unless it is known the value has not
# changed.
unless changed is false
watcher.values[namespace] = ctx.value
if namespace is 'common'
collection.touch watcher.consumers
else
collection.touch (namespace.substr 1)
processOne = (event, item) ->
process event, [item]
@ -70,6 +118,9 @@ $watch = (collection, {
collection.on 'any', updateMultiple
# Returns the watcher object.
watcher
#=====================================================================
# Creates a set of value from various items.
@ -77,104 +128,67 @@ $set = (options) ->
# Contrary to other helpers, the default value is the key.
options.val ?= -> @key
# Keys of items using this value.
users = Object.create null
options.init = []
#
sets = {
common: []
}
watcher = $watch this, options, (entered, exited) ->
changed = false
$watch this, options, (values) =>
# Marks changed namespaces to avoid unnecessary updates.
changed = {}
for value in entered
if @value.indexOf value is -1
@value.push value
changed = true
$_.each values, ([event, value, namespace]) ->
set = (sets[namespace] ?= [])
if event is 'enter'
return unless set.indexOf value is -1
set.push value
changed[namespace] = true
else
changed[namespace] = true if $removeValue set, value
for value in exited
changed = true if $removeValue @value, value
if changed.common
@touch users
delete changed.common
for key in changed
@touch (key.substr 1) # Remove the leading $.
changed
# This function both allows users to register to this set and gives
# them the current value.
->
# Registers this item as a consumer.
users[@key] = true
# Returns its dedicated value or the common one.
sets["$#{@key}"] ? sets.common
watcher.generator
#---------------------------------------------------------------------
$sum = (options) ->
# Keys of items using this value.
users = Object.create null
options.init ?= 0
# The current sum.
if options.init
sum = options.init
delete options.init
else
sum = 0
watcher = $watch this, options, (entered, exited) ->
prev = @value
$watch this, options, (values) =>
prev = sum
@value += value for value in entered
@value -= value for value in exited
$_.each values, ([event, value]) ->
sum += if event is 'enter' then value else -value
@value isnt prev
@touch users if sum isnt prev
# This function both allows users to register to this sum and gives
# them the current value.
->
users[@key] = true
sum
watcher.generator
#---------------------------------------------------------------------
# Uses a value from another item.
#
# Important note: This helper is badly specified when binding to
# multiple items.
# Important note: Behavior is not specified when binding to multiple
# items.
$val = (options) ->
# Keys of items using this value.
users = Object.create null
# The default value.
def = options.default
delete options.default
options.init ?= def
# Should the last value be kept instead of returning to the default
# value when no items are available!
keepLast = !!options.keepLast
delete options.keepLast
# The current value.
value = def
watcher = $watch this, options, (entered, exited) ->
prev = @value
$watch this, options, (values) =>
prev = value
if not $_.isEmpty entered
@value = entered[0]
else
@value = def unless keepLast
for [event, value] in values
break if event is 'enter'
@value isnt prev
value = def unless keepLast
@touch users if value isnt prev
->
users[@key] = true
value
watcher.generator
#=====================================================================