<commit_before>assert = require 'assert'

wraparound = (n, max) ->
  return max - 0.5 if n < 0
  return 0.5 if n > max
  n
  
RX = "_RX"
LABEL = "_LABEL"
KIND = "_KIND"
CHILDREN = "_CHILDREN"
AUTHORITY = "_AUTHORITY"
EXPORTS = "_EXPORTS"
HANDLERS = "_HANDLERS"
CSS = "_CSS"

SETUP = '_SETUP'

class World
  constructor: (up, label, rx) ->
    assert up, "up always exists"
    assert _.isObject(up), "up is an object"
    @up = up
    cache_rx = rx or @up.get_raw(RX)
    assert cache_rx, "cache_rx"
    @doc = cache_rx.map()
    assert _.isString(label), "label not a string: #{label}"
    @doc.put LABEL, label
    @doc.put CHILDREN, cache_rx.array()
    @doc.put(RX, rx) if rx?
    
  # reactive-coffee tags and binding
  rx: () -> @get(RX)
  T: () -> @rx().rxt.tags
  SVG: () -> @rx().rxt.svg_tags
  bind: () -> @rx().bind
  
  put: (key, value) ->
    value = @rx().array value if _.isArray(value)
    @doc.put(key, value)

  get_local: (key) ->
    @doc.get(key)

  # TODO: make @up a list
  get_raw: (key, world) ->
    value = @get_local(key)
    return value if value?
    authority = @get_local(AUTHORITY)
    if authority
      #console.log "Find #{key} in #{authority}"
      value = authority.get_raw(key, @)
      return value if value?
    @up.get_raw(key, world or @)

  get: (key) ->
    value = @get_raw(key)
    return value(@,{}) if _.isFunction(value)
    value

  owner: (key) ->
    return @ if @get_local(key)?
    @up.owner(key) unless @is_root()
    
  handlers_for: (key) ->
    handlers = @get(HANDLERS)
    list = handlers.get(key)
    unless list?
      list = []
      handlers.put(key, list)
    list 

  handle: (key, callback) ->
    handlers = @handlers_for(key)
    handlers.push callback

  send: (key, args, callback) ->
    for handler in @handlers_for(key)
      console.log "send: #{key}, #{args}} to handler", handler
      value = handler(key, args)
      callback(value) if callback
    
  _export_events: ->
    return unless exports = @get_local(EXPORTS)
    for event in exports.all()
      assert _.isFunction @get_raw(event), "No function for #{event}"
      world = @
      @handle event, (key, args) -> world.call(key, args)
    
  update: (key, delta, max) ->
    result = @get(key) + delta
    result = wraparound(result, max) if max?
    @put(key, result)

  # TODO: Require mutating actions to be calls, not gets
  call: (key, args) ->
    closure = @get_raw(key)
    assert _.isFunction(closure), "#{key}: #{closure} is not a function"
    closure(@, args)
    
  # TODO: refactor import_dict methods somewhere
  _import_children: (children) ->
    result = @rx().array()
    children = children(@) if _.isFunction(children)
    for value in children<commit_msg>Allow send to return last value<commit_after>assert = require 'assert'

wraparound = (n, max) ->
  return max - 0.5 if n < 0
  return 0.5 if n > max
  n
  
RX = "_RX"
LABEL = "_LABEL"
KIND = "_KIND"
CHILDREN = "_CHILDREN"
AUTHORITY = "_AUTHORITY"
EXPORTS = "_EXPORTS"
HANDLERS = "_HANDLERS"
CSS = "_CSS"

SETUP = '_SETUP'

class World
  constructor: (up, label, rx) ->
    assert up, "up always exists"
    assert _.isObject(up), "up is an object"
    @up = up
    cache_rx = rx or @up.get_raw(RX)
    assert cache_rx, "cache_rx"
    @doc = cache_rx.map()
    assert _.isString(label), "label not a string: #{label}"
    @doc.put LABEL, label
    @doc.put CHILDREN, cache_rx.array()
    @doc.put(RX, rx) if rx?
    
  # reactive-coffee tags and binding
  rx: () -> @get(RX)
  T: () -> @rx().rxt.tags
  SVG: () -> @rx().rxt.svg_tags
  bind: () -> @rx().bind
  
  put: (key, value) ->
    value = @rx().array value if _.isArray(value)
    @doc.put(key, value)

  get_local: (key) ->
    @doc.get(key)

  # TODO: make @up a list
  get_raw: (key, world) ->
    value = @get_local(key)
    return value if value?
    authority = @get_local(AUTHORITY)
    if authority
      #console.log "Find #{key} in #{authority}"
      value = authority.get_raw(key, @)
      return value if value?
    @up.get_raw(key, world or @)

  get: (key) ->
    value = @get_raw(key)
    return value(@,{}) if _.isFunction(value)
    value

  owner: (key) ->
    return @ if @get_local(key)?
    @up.owner(key) unless @is_root()
    
  handlers_for: (key) ->
    handlers = @get(HANDLERS)
    list = handlers.get(key)
    unless list?
      list = []
      handlers.put(key, list)
    list 

  handle: (key, callback) ->
    handlers = @handlers_for(key)
    handlers.push callback

  send: (key, args, callback) ->
    for handler in @handlers_for(key)
      console.log "send: #{key}, #{args}} to handler", handler
      value = handler(key, args)
      callback(value) if callback
      value
    
  _export_events: ->
    return unless exports = @get_local(EXPORTS)
    for event in exports.all()
      assert _.isFunction @get_raw(event), "No function for #{event}"
      world = @
      @handle event, (key, args) -> world.call(key, args)
    
  update: (key, delta, max) ->
    result = @get(key) + delta
    result = wraparound(result, max) if max?
    @put(key, result)

  # TODO: Require mutating actions to be calls, not gets
  call: (key, args) ->
    closure = @get_raw(key)
    assert _.isFunction(closure), "#{key}: #{closure} is not a function"
    closure(@, args)
    
  # TODO: refactor import_dict methods somewhere
  _import_children: (children) ->
    result = @rx().array()
    children = children(@) if _.isFunction(children)
    for value in children
{'fraction_non_alphanumeric': 0.14965239663373583, 'fraction_numerical': 0.0018294914013904135, 'mean_word_length': 2.7938931297709924, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6500867', 'n_tokens_mistral': 1979, 'n_tokens_neox': 1960, 'n_words': 551}