width = 800
height = 600
supply_height = 96
+CLICK_FUZ = 10 # this far away from things is close enough to be "clicked on"
+PROX_MAX = CLICK_FUZ * CLICK_FUZ
+
+# constants
+STYLE_NORMAL = 0
+STYLE_SELECTED = 1
+STYLE_HOVER = 2
+STYLE_EDITING = 3
+STYLE_DRAGGING = 4
# json (compiled to javascript and minified) is ~8% smaller than the resulting xml
json_to_svg = (json) ->
# public vars: x, y, width, height, el
class Widget
constructor: (args) ->
+ @style = STYLE_NORMAL
@x = args?.x ? 1
@y = args?.y ? 1
@width = args?.width ? 50
@height = args?.height ? 34
+ clone: ->
+ return new Widget @
move: (args) ->
@x = args.x
@y = args.y
+ proximity: (xy) -> # return the square of the distance to your visible bits
+ return PROX_MAX + 1
+ set_style: (style) ->
+ return if @style is style
+ if style is STYLE_NORMAL
+ @el.setAttribute 'style', 'filter: url(#crayon)'
+ else
+ if @style is STYLE_NORMAL
+ @el.setAttribute 'style', ''
+ switch style
+ when STYLE_NORMAL
+ @el.setAttribute 'class', "#{@css_class}"
+ when STYLE_SELECTED
+ @el.setAttribute 'class', "#{@css_class} selected"
+ when STYLE_HOVER
+ @el.setAttribute 'class', "#{@css_class} hover"
+ when STYLE_DRAGGING
+ @el.setAttribute 'class', "#{@css_class} dragging"
+ # FIXME when STYLE_EDITING
+ @style = style
+ controls: -> # create controls, return them
+ return []
+ hide_controls: ->
+
class RectWidget extends Widget
constructor: (args) ->
super args
+ @css_class = 'box'
@el = json_to_svg rect:
x: @x + 1
y: @x + 1
height: @height - 2
class: 'box'
style: 'filter: url(#crayon)'
+ clone: ->
+ return new RectWidget @
move: (args) ->
super args
@el.setAttribute 'x', @x + 1
@el.setAttribute 'y', @y + 1
- clone: ->
- return new RectWidget @
+ proximity: (xy) -> # return the square of the distance to your visible bits
+ x = xy.x
+ y = xy.y
+ prox = PROX_MAX + 1
+ in_x = false
+ in_y = false
+ if x > @x - CLICK_FUZ and x < @x + @width + CLICK_FUZ
+ in_x = true
+ if y < @y + @height / 2
+ new_prox = @y - y
+ else
+ new_prox = @y + @height - y
+ new_prox *= new_prox
+ if new_prox < prox
+ prox = new_prox
+ if y > @y - CLICK_FUZ and y < @y + @height + CLICK_FUZ
+ in_y = true
+ if x < @x + @width / 2
+ new_prox = @x - x
+ else
+ new_prox = @x + @width - x
+ new_prox *= new_prox
+ if new_prox < prox
+ prox = new_prox
+ if in_x and in_y and prox > PROX_MAX
+ prox = PROX_MAX - 1
+ return prox
+ controls: -> # create controls, return them
+ return []
+ hide_controls: ->
# called automatically on domcontentloaded
init = ->
- offset = null
- selected = []
- on_canvas = []
- dragging = false
+ svg_offset = null
$container = $ '.crayon_mockup'
svg = json_to_svg svg: width: width, height: height, viewBox: "0 0 #{width} #{height}"
$svg = $ svg
}
svg.appendChild widget.el
+ # editor state
+ on_canvas = []
+ selected = []
+ editing = [] # has controls
+ dragging = false
+ dragging_offset = x: 0, y: 0 # from mouse x,y -> widget x,y
+
# todo ask widgets
closest_widget = (widgets, xy) ->
- closest = [10000, null]
+ prox = PROX_MAX + 1
+ closest = null
for w in widgets
- x = w.x + w.width / 2 - xy.x
- y = w.y + w.height / 2 - xy.y
- dist2 = x * x + y * y
- if dist2 < closest[0]
- closest = [dist2, w]
- if closest[1]?
- return closest
+ new_prox = w.proximity xy
+ if new_prox < prox
+ prox = new_prox
+ closest = w
+ if prox < PROX_MAX
+ return [prox, closest]
return null
svg_event_to_xy = (e) ->
- unless offset?
- offset = $svg.offset()
+ unless svg_offset?
+ svg_offset = $svg.offset()
return {
- x: Math.round(e.pageX - offset.left)
- y: Math.round(e.pageY - offset.top)
+ x: Math.round(e.pageX - svg_offset.left)
+ y: Math.round(e.pageY - svg_offset.top)
}
$svg.mousedown (e) ->
xy = svg_event_to_xy e
if xy.y < supply_height
closest = closest_widget supply, xy
- console.log closest
if closest?
closest[1] = closest[1].clone()
svg.appendChild closest[1].el
closest = closest_widget on_canvas, xy
if closest?
selected = [closest[1]]
+ closest[1].set_style STYLE_DRAGGING
dragging = true
+ dragging_offset.x = closest[1].x - xy.x
+ dragging_offset.y = closest[1].y - xy.y
mousemove e
return false
$svg.mouseup (e) ->
selected = []
return false
mousemove = (e) ->
+ xy = svg_event_to_xy e
if dragging
- xy = svg_event_to_xy e
- xy.x -= selected[0].width / 2
- xy.y -= selected[0].height / 2
+ xy.x += dragging_offset.x
+ xy.y += dragging_offset.y
selected[0].move xy
+ else
+ closest = closest_widget on_canvas, xy
+ if closest?
+ hover = closest[1]
+ else
+ hover = null
+ for w in on_canvas
+ if w is hover
+ w.set_style STYLE_HOVER
+ else
+ w.set_style STYLE_NORMAL
return false
$svg.mousemove mousemove
#($ document).keydown (e) ->