@danielkalen/simplybind
Version:
Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.
444 lines (166 loc) • 6.84 kB
text/coffeescript
BindingInterfacePublic =
of: (object)->
if isnt 0 and isnt 2
throwErrorUnavail methodNames[0]
unless checkIf.isObject(object) or checkIf.isFunction(object)
throwErrorBadArg methodNames[0], object
if checkIf.isBindingInterface(object)
object = object.object
if is 2 # Indicates this binding is currently acting as a proxy for the another (proxied) binding
[.length-1] = proxied = .of(object)
.hasInitialBinding = true
.hasTransform = false
.addDep(proxied._)
maybeUpdateDep(, proxied._)
return
ofEvent: (eventName, customInMethod, customOutMethod)->
if isnt 0 or .hasEventName
throwErrorUnavail methodNames[1]
else if not eventName or not checkIf.isString(eventName)
throwErrorBadArg methodNames[1], eventName
else if isNaN parseInt()
throwWarning('badEventArg',1)
.hasEventName = true
= eventName
= +'#'+
=
'in': customInMethod
'out': customOutMethod
return @
to: (subject, specificOptions)->
if isnt 1 or .hasInitialBinding
throwErrorUnavail methodNames[2]
.push proxied = computeProxied(@, subject, specificOptions)
if proxied.stage is 0
newStage = 2
else
newStage = 3
.hasInitialBinding = true
return
and: (subject, specificOptions)->
if isnt 3 or not .hasInitialBinding or .hasMultiTransform
throwErrorUnavail methodNames[3]
.push proxied = computeProxied(@, subject, specificOptions)
if proxied.stage is 0
newStage = 2
else
newStage = 3
.hasTransform = false
return
toEvent: (eventName, customOutMethod, customInMethod, specificOptions)-> # Bad argument error handling will occur in .ofEvent
if isnt 1
throwErrorUnavail methodNames[4]
.push SimplyBind(0, specificOptions).ofEvent(eventName, customInMethod, customOutMethod)
return
chainTo: (subject, specificOptions)->
if isnt 3
throwErrorUnavail methodNames[5]
return SimplyBind().to(subject, specificOptions)
set: (newValue)->
if is 0 or is 2
throwErrorUnavail methodNames[6]
.setValue(newValue, )
return @
get: ()->
if is 0 or is 2
throwErrorUnavail methodNames[7]
return if then .pholderValues[] else .value
transformSelf: (transformFn)-> # Applied only to the last proxy
if isnt 1 or is 1 and .type is 'Array'
throwErrorUnavail methodNames[8]
if not checkIf.isFunction(transformFn)
throwWarning('fnOnly',1)
else
.selfTransform = transformFn
if .options.updateOnBind
currentValue = if .isMulti then [0] else
.setValue(currentValue)
return @
transform: (transformFn)-> # Applied only to the last proxy
if isnt 3 or .hasTransform or .hasMultiTransform
throwErrorUnavail methodNames[9]
.hasTransform = .processTransform(transformFn, .slice(-1)) or false
return
transformAll: (transformFn)-> # Applied to entrie proxies set
if isnt 3 or .hasTransform or .hasMultiTransform
throwErrorUnavail methodNames[10]
.hasMultiTransform = .processTransform(transformFn, ) or false
return
condition: (conditionFn)-> # Applied only to the last proxy
if isnt 3
throwErrorUnavail methodNames[11]
.processCondition(conditionFn, .slice(-1))
return
conditionAll: (conditionFn)-> # Applied to entrie proxies set
if isnt 3
throwErrorUnavail methodNames[12]
.processCondition(conditionFn, )
return
bothWays: (dontOrAltTransform)-> # Applied only to the last proxy
if isnt 3 or .hasMultiTransform
throwErrorUnavail methodNames[13]
proxied =
proxiedBinding = proxied._.addDep(, true)
originTransform = .transforms[proxied.ID]
originCondition = .conditions[proxied.ID]
if originTransform or dontOrAltTransform
transformToUse = if checkIf.isFunction(dontOrAltTransform) then dontOrAltTransform else originTransform
proxiedBinding.addTransform(, transformToUse) if transformToUse and dontOrAltTransform isnt false
if originCondition
proxiedBinding.addCondition(, originCondition)
.addDep(proxied._, true)
return @
unBind: (bothWays)-> # Applied to all proxies
if isnt 3
throwErrorUnavail methodNames[14]
.removeDep(proxied._, bothWays) for proxied in
return @
pollEvery: (time)->
if isnt 3 or .type is 'Event'
throwErrorUnavail methodNames[15]
.addPollInterval(time)
return @
stopPolling: ()->
if isnt 3
throwErrorUnavail methodNames[16]
.removePollInterval()
return @
updateDepsOnEvent: (eventName, customMethod)->
if isnt 3
throwErrorUnavail methodNames[17]
.registerEvent(eventName, customMethod)
return @
removeEvent: (eventName, customMethod)->
if isnt 3
throwErrorUnavail methodNames[18]
.unRegisterEvent(eventName, customMethod)
return @
throttle: (delay)->
if isnt 1 and isnt 3
throwErrorUnavail methodNames[19]
if delay and checkIf.isNumber(delay)
.throttleRate = delay
else if delay is false
delete .throttleRate
return @
setOption: (optionName, newValue)->
newOptions = "#{optionName}":newValue
setOptionsForBinding(, newOptions)
setOptionsForBinding(proxied._, newOptions) for proxied in
return @
# Aliases
BindingInterfacePublic.update = BindingInterfacePublic.set
BindingInterfacePublic.twoWay = BindingInterfacePublic.bothWays
BindingInterfacePublic.pipe = BindingInterfacePublic.chainTo
methodNames = Object.keys(BindingInterfacePublic)
BindingInterface::[methodName] = BindingInterfacePublic[methodName] for methodName in methodNames
# ==== Helpers =================================================================================
computeProxied = (instance, subject, specificOptions={})->
proxied = SimplyBind(subject, specificOptions, true)
if proxied.stage isnt 0
instance._.addDep(proxied._, instance)
maybeUpdateDep instance._, proxied._
return proxied
maybeUpdateDep = (instance, proxied)-> # Both arguments refer to the private Binding instances
if instance.options.updateOnBind or instance.type is 'Func'
instance.updateDep(proxied, instance)