@danielkalen/simplybind
Version:
Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.
81 lines (60 loc) • 5.09 kB
text/coffeescript
Binding = (object, type, state)->
extendState(@, state)
@optionsDefault = if @saveOptions then @options else defaultOptions
@type = type # ObjectProp | Array | Func | Proxy | Event | Pholder | DOMAttr | DOMValue | DOMCheckbox | DOMRadio
@object = object # The subject object of this binding, i.e. function, array, {}, DOM el, etc.
@ID = genID() # Assigned only after passing a valid object to .of()
@subs = [] # Subscribers array listing all of the objects that will be updated upon value update
@subsMeta = genObj() # Map subscribers' ID to their metadata (i.e. options, transform, condition, one-time-binding, etc.)
@pubsMap = genObj() # Map publishers (bindings that update this binding) by their ID
@attachedEvents = [] # Array listing all of the events currently listened on @object
@setValue = noop if @type is 'Array' or @type is 'Proxy'
# ==== Properties declared later or inherited from binding interface =================================================================================
# @options = options
# @value = undefined # Will represent the actual current value of the binding/object
# @property = property # The property name or array index or event callback argument
# @selector = selector # The property name or array index or event callback argument
# @origFn = Function # The original proxied function passed to Proxy bindings
# @customEventMethod = {} # Names of the event emitter/trigger methods (if applicable)
# @pholderContexts = {} # Placeholder surroundings (original binding value split by the placeholder regEx)
# @pholderIndexMap = {} # Placeholder occurence mapping, i.e. the placeholder name for each placeholder occurence
# @placeholder = "" # The last specified placeholder to bind the value to
# @descriptor = "" # Describes the type of property, i.e. 'attr:data-name' to indicate a DOMAttr type binding
# @isLiveProp = Boolean # Indicates whether or not the Object/Object's propety have been modified to be a live property
# @isDom = Boolean # Indicates whether or not the binding's object is a DOM object
# @pollInterval = ID # The interval ID of the timer that manually polls the object's value at a set interval
# @arrayBinding = Binding # Reference to the parent array binding (if exists) for an index-of-array binding (i.e. SimplyBind(array))
# @trackedChildren = [] # For Array type bindings - indicates which indicies of the array are tracked for changes (applicable when @options.trackArrayChildren) is true
# @eventName = "" # The name of the event this binding is listening to (for Event type bindings)
# @isEmitter = Boolean # Tracker to let us know we shouldn't handle the event update we received as it is the event this binding just emitted
# @eventHandler = Function # The callback that gets triggered upon an event emittance (for Even type bindings)
# @eventObject = Event # The dispatched event object (for Event type bindings)
# @selfTransform = Function # The transform function that new values being set to this binding are being passed through during @setValue (if applicable)
# @throttleRate = milliseconds # The rate in which the binding's subscribers will be updated only once in
# @throttleTimeout = ID # The timeout ID of the delay timer to update a throttled binding
# @lastUpdate = epoch timestamp # Last time the subs have been updated; used for throttle functions
# @isAsync = Boolean # Indicates if this is an async binding (currently only used for Event bindings)
### ========================================================================== ###
import [browserOnly] -DOMChoice_group.coffee
unless @type is 'Event' or (@type is 'Func' and @parentInterface) # the second condition will prevent function subscribers from being invoked on this binding creation
if @type is 'Pholder'
import [browserOnly] -Pholder-parentProperty-browser.coffee
import [nodeOnly] -Pholder-parentProperty-node.coffee
parentBinding = @parentBinding = SimplyBind(parentProperty).of(object)._
parentBinding.scanForPholders()
@value = parentBinding.pholderValues[@pholder]
import [browserOnly] -Pholder-textNodes_definition.coffee
else
@value = subjectValue = @fetchDirectValue()
if @type is 'ObjectProp' and not checkIf.isDefined(subjectValue)
@object[@property] = subjectValue # Define the prop on the object if it non-existent
@convertToLive()
@attachEvents()
# IF this is a binding to a specific index of an array then we must make this binding update the array on change if the array binding is tracking its children
if @object instanceof Array and @type isnt 'Array'
@arrayBinding = arrayBinding = cache.get(@object, true)
if arrayBinding and arrayBinding.trackedChildren and not targetIncludes(arrayBinding.trackedChildren, @property)
arrayBinding.trackedChildren.push(@property)
SimplyBind(@property).of(@object).to arrayBinding.updateSelf
return boundInstances[@ID] = @
import prototype.coffee