neft
Version:
Universal Platform
242 lines (197 loc) • 5.92 kB
text/coffeescript
'use strict'
utils = require 'src/utils'
log = require 'src/log'
assert = require 'src/assert'
Dict = require 'src/dict'
List = require 'src/list'
log = log.scope 'Binding'
{isArray} = Array
MAX_LOOPS = 50
getPropHandlerName = do ->
cache = Object.create null
(prop) ->
cache[prop] ||= "on#{utils.capitalize(prop)}Change"
class Connection
pool = []
= (binding, item, prop, parent=null) ->
if pool.length > 0 and (elem = pool.pop())
Connection.call elem, binding, item, prop, parent
elem
else
new Connection binding, item, prop, parent
constructor: (, item, , ) ->
= getPropHandlerName
= false
if isArray(item)
= ''
= Connection.factory , item[0], item[1], @
= .getValue()
else
= item
= null
= .getItemById item
Object.seal @
getSignalChangeListener: do ->
withParent = (prop, val) ->
# TODO: provide special signal emitted with property changed
if val is undefined or typeof prop isnt 'string' or .prop is prop
.updateItem()
return
noParent = ->
.update()
return
withParentOnDict = (prop) ->
if is prop
.updateItem()
return
noParentOnDict = (prop) ->
if is prop
.update()
return
->
if instanceof Dict
if
withParentOnDict
else
noParentOnDict
else
if
withParent
else
noParent
update: ->
.call @
connect: ->
{item} = @
if item
if item instanceof Dict
= true
item.onChange , @
else if item instanceof List
= true
handler =
item.onChange handler, @
item.onInsert handler, @
item.onPop handler, @
else if handler = item[]
= true
handler , @
return
disconnect: ->
{item} = @
if item and
handler =
if item instanceof Dict
item.onChange.disconnect handler, @
else if item instanceof List
item.onChange.disconnect handler, @
item.onInsert.disconnect handler, @
item.onPop.disconnect handler, @
else
item[].disconnect handler, @
= false
return
updateItem: ->
oldVal =
if
val = .getValue()
else
val = .getItemById
if oldVal and not
oldVal = null
if oldVal isnt val
= val
unless
.update()
if
.updateItem()
return
getValue: ->
if
[]
else
null
destroy: ->
?.destroy()
pool.push @
return
module.exports =
class Binding
= (binding, ctx, target) ->
target ?= new Binding binding, ctx
Object.seal target
# connections
{connections} = target
for elem in binding[1]
if isArray(elem)
connections.push Connection.factory target, elem[0], elem[1]
target
constructor: (binding, ) ->
assert.lengthOf binding, 2
assert.isFunction binding[0]
assert.isArray binding[1]
# properties
= binding[0]
= null
||= []
# update
`//<development>`
= false
= 0
`//</development>`
getItemById: (item) ->
throw "Not implemented"
getValue: ->
throw "Not implemented"
getDefaultValue: ->
switch typeof
when 'string'
''
when 'number'
0
when 'boolean'
false
else
null
setValue: (val) ->
throw "Not implemented"
onError: (err) ->
update: ->
unless
return
`//<development>`
if
if > MAX_LOOPS
return
if ++ is MAX_LOOPS
log.error
return
else
= 0
`//</development>`
result = utils.tryFunction , ,
if result instanceof Error
result
result =
`//<development>`
= true
`//</development>`
result
`//<development>`
= false
`//</development>`
return
getLoopDetectedErrorMessage: ->
"Potential loop detected"
destroy: ->
# destroy connections
while connection = .pop()
connection.destroy()
# clear props
= null
return