+ svg.appendChild json_to_svg filter:
+ id: 'crayon', filterUnits: 'userSpaceOnUse'
+ x: '-5%', y: '-5%', height: '110%', width: '110%'
+ children: [
+ { feTurbulence: baseFrequency: '.3', numOctaves: '2', type: 'fractalNoise' }
+ { feDisplacementMap: scale: '6', xChannelSelector: 'R', in: 'SourceGraphic' }
+ ]
+
+ # create canvas border
+ svg.appendChild json_to_svg rect:
+ x: 1
+ y: supply_height + 1
+ width: width - 2
+ height: height - 2 - supply_height
+ class: 'canvas_border'
+
+ supply = [
+ new RectWidget svg: svg
+ new RectWidget svg: svg, width: 12, height: 50
+ new RectWidget svg: svg, width: 70, height: 12
+ ]
+ for widget, i in supply
+ widget.move {
+ x: 30 + i * 90 + (70 - widget.width) / 2
+ y: (supply_height - widget.height) / 2
+ }
+
+ # editor state
+ on_canvas = []
+ selected = []
+ editing = [] # has controls
+ dragging = false
+ dragging_offset = x: 0, y: 0 # from mouse x,y -> widget x,y
+
+ deselect_all = (args) ->
+ except = args?.except ? null
+ found = false
+ for w in selected
+ if w is except
+ found = true
+ else
+ w.set_style STYLE_NORMAL
+ if found
+ selected = [except]
+ else
+ selected = []
+ closest_widget = (widgets, xy) ->
+ prox = PROX_MAX + 1
+ closest = null
+ for w in widgets
+ new_prox = w.proximity xy
+ if new_prox < prox
+ prox = new_prox
+ closest = w
+ if prox < PROX_MAX
+ return closest
+ return null
+ svg_event_to_xy = (e) ->
+ unless svg_offset?
+ svg_offset = $svg.offset()
+ return {
+ x: Math.round(e.pageX - svg_offset.left)
+ y: Math.round(e.pageY - svg_offset.top)
+ }
+ mousedown = (e) ->
+ mousemove e
+ if dragging # two mousedowns in a row?! it happens
+ return mouseup e
+ xy = svg_event_to_xy e
+ if xy.y < supply_height
+ closest = closest_widget supply, xy
+ if closest?
+ closest = closest.clone()
+ on_canvas.push closest
+ else
+ closest = closest_widget on_canvas, xy
+ if closest?
+ deselect_all except: closest
+ selected = [closest]
+ closest.set_style STYLE_DRAGGING
+ dragging = true
+ dragging_offset.x = closest.x - xy.x
+ dragging_offset.y = closest.y - xy.y
+ else
+ deselect_all()
+ return false
+ mouseup = (e) ->
+ mousemove e
+ if dragging
+ for w in selected
+ if w.y < supply_height
+ w.destruct()
+ else
+ w.set_style STYLE_SELECTED
+ dragging = false
+ return false
+ prev_hover = null
+ mousemove = (e) ->
+ xy = svg_event_to_xy e
+ if dragging
+ xy.x += dragging_offset.x
+ xy.y += dragging_offset.y
+ selected[0].move xy
+ else
+ hover = closest_widget on_canvas, xy
+ unless hover?
+ hover = closest_widget supply, xy
+ if hover != prev_hover
+ prev_hover = hover
+ for w in selected
+ if w.style is STYLE_HOVER and w isnt hover
+ w.set_style STYLE_SELECTED
+ for w in supply
+ if w.style is STYLE_HOVER and w isnt hover
+ w.set_style STYLE_NORMAL
+ for w in on_canvas
+ if w.style is STYLE_HOVER and w isnt hover
+ w.set_style STYLE_NORMAL
+ if hover
+ hover.set_style STYLE_HOVER