UNPKG

luda

Version:

A library helps to build cross-framework UI components.

120 lines (105 loc) 4.47 kB
import arrayEqual from '../../base/array-equal.coffee' import expando from '../../base/expando.coffee' import find from '../../base/find.coffee' import findAll from '../../base/find-all.coffee' import matches from '../../base/matches.coffee' import splitValues from '../../base/split-values.coffee' import Type from '../../base/type.coffee' import unique from '../../base/unique.coffee' import log from '../../log/log.coffee' import unnested from './unnested.coffee' config = childList: true subtree: true attributes: true attributeOldValue: true cur = (ins, callback, target) -> proto = ins.constructor.prototype isInProto = Object.values(proto).includes callback if isInProto then ins else target runNodeCallbacks = (type, mutation, watches, nestable) -> ins = mutation.ins C = ins.constructor mu = mutation.mu nodes = Array.from mu["#{type}Nodes"] watches.node.forEach (node) -> els = [] nodes.forEach (n) -> els = els.concat(findAll node.selector, n) return unless els.length not nestable and els = unnested ins, unique(els) els.length and node.callbacks.forEach (callback) -> ctx = cur ins, callback, els unless callback is C.prototype.cleanTraversal log "#{C.id} ID: #{ins.id} executes nodes #{type} callback.", 'Root element', ins.root.els[0], 'Cache', C.instances[ins.id], "Nodes #{type}", els, "Callback #{callback.name or ''}", callback, 'Context', ctx, 'Arguments', [els, type] callback.call ctx, els, type runAttrCallbacks = (mutation, watches, nestable) -> ins = mutation.ins mu = mutation.mu C = ins.constructor name = mu.attributeName target = mu.target oldVal = mu.oldValue return unless name and Type.isElement target return if not nestable and not unnested(ins, [target]).length watches.attr.forEach (attr) -> return unless attr.name.includes name return unless matches target, attr.selector attr.callbacks.forEach (callback) -> ctx = cur(ins, callback, target) log "#{C.id} ID: #{ins.id} executes #{name} changed callback.", 'Root element', ins.root.els[0], 'Cache', C.instances[ins.id], 'Changed target', target, "Callback #{callback.name or ''}", callback, 'Context', ctx, 'Arguments', [target, oldVal] callback.call ctx, target, oldVal executeMutations = (C, mutations, nestable) -> mutations.forEach (mutation) -> runNodeCallbacks 'added', mutation, C.watches, nestable runNodeCallbacks 'removed', mutation, C.watches, nestable runAttrCallbacks mutation, C.watches, nestable nodesEqual = (nodesOne, nodesTwo) -> arrayEqual Array.from(nodesOne), Array.from(nodesTwo), true findSameMutation = (mutations, mu) -> theSameMutation = null mutations.some (mutation) -> return theSameMutation = mutation if mu is mutation.mu return unless mu.type is mutation.mu.type return unless mu.target is mutation.mu.target return unless mu.attributeName is mutation.mu.attributeName return unless mu.oldValue is mutation.mu.oldValue return unless nodesEqual mu.addedNodes, mutation.mu.addedNodes return unless nodesEqual mu.removedNodes, mutation.mu.removedNodes theSameMutation = mutation theSameMutation createObserver = (C, instance) -> inses = C.instances rootEl = instance.root.els[0] nestable = Type.isDocument C.root observer = new MutationObserver (mus) -> mutations = mus.map (mu) -> {ins: instance, mu: mu} not nestable and find(C.root, rootEl).forEach (el) -> return unless cached = inses[el[expando]] return unless ins = cached.instance return unless watcher = cached.watcher watcher.takeRecords().forEach (mu) -> nestedMutation = findSameMutation mutations, mu nestedMutation.ins = ins if nestedMutation executeMutations C, mutations, nestable observer.observe rootEl, config observer watch = (C, ins) -> unless C.watches conf = C.helpers.watch.call C.prototype C.watches = node: (conf.node or []).map (d) -> selector: if Type.isFunction d[0] then '*' else d[0] callbacks: if Type.isFunction d[0] then d else d.slice 1 attr: (conf.attr or []).map (a) -> name: splitValues a[0] selector: if Type.isFunction a[1] then '*' else a[1] callbacks: if Type.isFunction a[1] then a.slice 1 else a.slice 2 createObserver C, ins stopWatch = (ins, watcher) -> watcher.disconnect() export {watch, stopWatch}