@danielkalen/simplybind
Version:
Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.
81 lines (59 loc) • 5.12 kB
text/coffeescript
Binding = (object, type, state)->
extendState(@, state)
@optionsDefault = if @saveOptions then @options else defaultOptions
@type = type # ObjectProp | Array | Func | Proxy | Event | 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
@subOpts = genObj() # Map subscribers' options by their ID
@pubsMap = genObj() # Map publishers (bindings that update this binding) by their ID
@subsPholders = genObj() # SubscriberID:sub-placeholder mapping
@myPholders = genObj() # SubscriberID:self-placeholder mapping indicating which subs will receive a value of a specific placeholder from this binding (if applicable)
@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
# @transforms = genObj() # SubscriberID:transformFn mapping
# @conditions = genObj() # SubscriberID:coniditionFn mapping
# @valueOriginal = undefined # Value on init
# @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
# @customEventMethod = {} # Names of the event emitter/trigger methods (if applicable)
# @pholderValues = {} # Placeholder value mapping
# @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
@value = @valueOriginal = subjectValue = @fetchDirectValue()
if @type is 'ObjectProp' and not checkIf.isDefined(subjectValue)
@object[@property] = subjectValue # Define the prop on the object if it non-existent
if @placeholder and not @pholderValues
@scanForPholders()
@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