diff --git a/src/helpers.coffee b/src/helpers.coffee index 3820432bb..c02f5a5a7 100644 --- a/src/helpers.coffee +++ b/src/helpers.coffee @@ -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 #=====================================================================