Skip to content

Instantly share code, notes, and snippets.

@eizenberg
Created September 19, 2013 08:45
Show Gist options
  • Save eizenberg/6620722 to your computer and use it in GitHub Desktop.
Save eizenberg/6620722 to your computer and use it in GitHub Desktop.
A reactive svg implementation, based on reactive-coffee by @yang
bind = rx.bind
ObsCell = rx.ObsCell
ObsArray = rx.ObsArray
@rxv = {}
events = ["click", "mousedown", "mouseup"]
specialAttrs = rxv.specialAttrs = {
init: (elt, fn) -> fn.call(elt)
}
for ev in events
do (ev) ->
specialAttrs[ev] = (elt, fn) -> elt.addEventListener ev, fn
setProp = (elt, prop, val) ->
elt.setAttribute prop, val
rxv.mktag = mktag = (tag) ->
(arg1, arg2) ->
# arguments are either (), (attrs: Object), (contents: non-Object), or
# (attrs: Object, contents: non-Object)
[attrs, contents] =
if not arg1? and not arg2?
[{}, null]
else if arg2?
[arg1, arg2]
else if _.isString(arg1) or arg1 instanceof SVGElement or _.isArray(arg1) or arg1 instanceof ObsCell or arg1 instanceof ObsArray
[{}, arg1]
else
[arg1, null]
elt = document.createElementNS('http://www.w3.org/2000/svg', tag)
for name, value of _.omit(attrs, _.keys(specialAttrs))
if value instanceof ObsCell
do (name) ->
value.onSet.sub ([old, val]) -> setProp(elt, name, val)
else
setProp(elt, name, value)
if contents?
toNodes = (contents) ->
for child in contents
if _.isString(child)
document.createTextNode(child)
else if child instanceof SVGElement
child
else
throw 'Unknown element type in array: ' + child.constructor.name
updateContents = (contents) ->
(elt.removeChild elt.firstChild) while elt.firstChild
if _.isArray(contents)
(elt.appendChild node) for node in toNodes(contents)
else if _.isString(contents)
updateContents([contents])
else
throw 'Unknown type for contents: ' + contents.constructor.name
if contents instanceof ObsArray
contents.onChange.sub ([index, removed, added]) ->
(elt.removeChild elt.childNodes[index]) for i in [0...removed.length]
toAdd = toNodes(added)
if index == elt.childNodes.length
(elt.appendChild node) for node in toAdd
else
(elt.childNodes[index].insertBefore node) for node in toAdd
else if contents instanceof ObsCell
contents.onSet.sub(([old, val]) -> updateContents(val))
else
updateContents(contents)
for key of attrs when key of specialAttrs
specialAttrs[key](elt, attrs[key], attrs, contents)
elt
tags = ['svg', 'rect', 'text', 'g']
rxv.tags = _.object([tag, rxv.mktag(tag)] for tag in tags)
rxv.importTags = (x) => _(x ? this).extend(rxv.tags)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment