JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
factor out svg element creation
[crayon_mockup.git] / auto.coffee
1 # settings
2 width = 500
3 height = 500
4
5 # globals
6 $svg = null # jquery object for svg element
7 svg = null # dom object for svg element
8 $tool_options = null # jquery object for tool options line
9 selection = []
10 svg_ns = 'http://www.w3.org/2000/svg'
11 cur_tool = null
12 cur_tool_name = null
13 mouse = x: 0, y: 0, buttons: [0,0,0]
14
15 class EditableThing
16         constructor: ->
17                 @el = null
18         click_proximity: (x, y) ->
19                 return null
20         clicked: ->
21         update: ->
22         destructor: ->
23                 if @el?
24                         svg.removeChild @el
25                         @el = null
26
27 class PolyLine extends EditableThing
28         constructor: (args) ->
29                 super args
30                 @drawing = args?.drawing ? false
31                 @el = document.createElementNS svg_ns, "path"
32                 svg.appendChild @el
33                 @points = args?.points ? []
34                 @update()
35         update: (args) ->
36                 if args?.drawing?
37                         @drawing = args.drawing
38                 d = ''
39                 l = ''
40                 sep = 'M'
41                 for loc, i in @points
42                         d += l = "#{sep} #{loc[0]} #{loc[1]}"
43                         sep = ' L'
44                 if args?.to_mouse?
45                         d += l = "#{sep} #{args.mouse_x} #{args.mouse_y}"
46                 if args?.close # FIXME ?remove
47                         d += l = ' z'
48                 if @points.length > 0
49                         if l is " L #{@points[0][0]} #{@points[0][1]}"
50                                 d = d.substr 0, d.length - l.length
51                                 d += ' z'
52                 @el.setAttribute "d", d
53         add_point: (x, y) ->
54                 @points.push [x, y]
55         close_loop: ->
56                 if @points.length > 2
57                         @add_point @points[0][0], @points[0][1]
58
59 class Tool
60         constructor: (args) ->
61                 @button = args.button
62                 if @button?
63                         @button.addClass 'disabled'
64                 @tool_options = args.tool_options
65         click: (x, y) ->
66         mousemove: (x, y) ->
67         keydown: (keycode) ->
68         disable: ->
69                 if @button?
70                         @button.removeClass 'disabled'
71                 @tool_options.empty()
72
73 class TutorialTool extends Tool
74         constructor: (args) ->
75                 super args
76                 @paths = []
77                 choose = [
78                         [[219,34],[53,141],[96,143],[92,255],[365,257],[362,145],[407,144,'z']],
79                         [[161,118],[144,106],[130,115],[128,152],[140,160],[156,150]],
80                         [[173,107],[169,159],[180,137],[189,133],[193,160]],
81                         [[218,135],[205,144],[205,158],[215,164],[225,156],[225,144,'z']],
82                         [[242,135],[233,145],[233,157],[244,168],[256,158],[254,144,'z']],
83                         [[278,141],[269,135],[261,142],[264,151],[278,153],[281,162],[271,170],[264,163]],
84                         [[291,151],[305,151],[312,143],[299,135],[288,140],[293,160],[302,167],[313,158]],
85                         [[136,208],[121,206],[116,226],[128,233],[136,229],[137,209],[140,233]],
86                         [[160,207],[191,205]],
87                         [[176,184],[174,228],[180,238]],
88                         [[198,216],[187,223],[189,236],[200,242],[210,235],[209,224,'z']],
89                         [[227,216],[216,222],[216,236],[224,244],[237,240],[237,226,'z']],
90                         [[247,187],[249,241],[254,243]]
91                 ]
92                 for c in choose
93                         path = data: c, element: document.createElementNS svg_ns, "path"
94                         update_path path, close: c[c.length - 1][2] is 'z'
95                         svg.appendChild path.element
96                         @paths.push path
97                 @tip = $ "<span>&nbsp;</span>"
98                 @tool_options.append @tip
99         disable: ->
100                 super()
101                 for p in @paths
102                         svg.removeChild p.element
103
104 class DrawTool extends Tool
105         constructor: (args) ->
106                 super args
107                 @tool_options.append $ "<span>Draw tool helpers:</span>"
108                 @stop_button = $ '<span class="button" title="keyboard shortcut: space">finish line</span>'
109                 @stop_close_button = $ '<span class="button" title="keyboard shortcut: O">close (loop)</span>'
110                 @cancel_button = $ '<span class="button" title="keyboard shortcut: Esc">cancel line</span>'
111                 @tool_options.append @stop_button
112                 @tool_options.append @stop_close_button
113                 @tool_options.append @cancel_button
114                 @stop_button.click @stop_drawing.bind @
115                 @stop_close_button.click @stop_close_drawing.bind @
116                 @cancel_button.click @cancel_drawing.bind @
117                 @update_helper_buttons()
118         click: (x, y) ->
119                 if selection.length and not selection[0].drawing?
120                         selection = []
121                 unless selection.length
122                         selection.push new PolyLine drawing: true
123                 selection[0].add_point x, y
124                 selection[0].update()
125                 @update_helper_buttons()
126         update_helper_buttons: ->
127                 if selection[0]?.drawing? and selection[0]?.points.length > 0
128                         @cancel_button.removeClass 'disabled'
129                 else
130                         @cancel_button.addClass 'disabled'
131                 if selection[0]?.drawing? and selection[0]?.points.length > 1
132                         @stop_button.removeClass 'disabled'
133                 else
134                         @stop_button.addClass 'disabled'
135                 if selection[0]?.drawing? and selection[0]?.points.length > 2
136                         @stop_close_button.removeClass 'disabled'
137                 else
138                         @stop_close_button.addClass 'disabled'
139         cancel_drawing: ->
140                 if selection[0]?.drawing?
141                         selection[0].destructor()
142                 selection = []
143                 @update_helper_buttons()
144                 return false
145         stop_drawing: ->
146                 if selection[0]?.drawing?
147                         if selection[0]?.points.length < 2
148                                 return @cancel_drawing()
149                         if selection[0]?
150                                 selection[0].update()
151                 selection = []
152                 @update_helper_buttons()
153                 return false
154         stop_close_drawing: ->
155                 if selection[0]?.drawing?
156                         if selection[0]?.points.length < 3
157                                 return @stop_drawing()
158                         selection[0].close_loop()
159                         selection[0].update drawing: false
160                 selection = []
161                 @update_helper_buttons()
162                 return false
163         mousemove: (x, y) ->
164                 mouse.x = x
165                 mouse.y = y
166                 if selection[0]?.drawing?
167                         selection[0].update to_mouse: true, mouse_x: x, mouse_y: y
168         keydown: (keycode) ->
169                 switch keycode
170                         when ('O'.charCodeAt 0), ('0'.charCodeAt 0)
171                                 return @stop_close_drawing()
172                         when (' '.charCodeAt 0), 13, 10
173                                 return @stop_drawing()
174                         when 27
175                                 return @cancel_drawing()
176         disable: ->
177                 super()
178                 @stop_drawing()
179
180 class EditTool extends Tool
181         constructor: (args) ->
182                 super args
183                 args.tool_options.append $ "<span>Oops, the edit tool isn't implemented yet</span>"
184
185 class DeleteTool extends Tool
186         constructor: (args) ->
187                 super args
188                 args.tool_options.append $ "<span>To delete: click on a line below</span>"
189         click: (x, y) ->
190                 $svg.find('path:hover').remove()
191                 return false
192
193 update_path = (path, flags) ->
194         d = ''
195         sep = 'M'
196         for loc, i in path.data
197                 d += "#{sep} #{loc[0]} #{loc[1]}"
198                 sep = ' L'
199         if flags?.to_mouse?
200                 d += "#{sep} #{mouse.x} #{mouse.y}"
201         if flags?.close
202                 d += " z"
203         path.element.setAttribute "d", d
204
205 switch_to_tool = (tool_class) ->
206
207 # json (compiled to javascript and minified) is ~8% smaller than the resulting xml
208 json_to_svg = (json) ->
209         for tag, attrs of json
210                 el = document.createElementNS svg_ns, tag
211                 for k, v of attrs
212                         if k is 'children'
213                                 for child in v
214                                         el.appendChild json_to_svg child
215                         else
216                                 el.setAttribute k, v
217         return el
218
219 # called automatically on domcontentloaded
220 init = ->
221         $container = $ '.crayon_mockup'
222         $toolbar = $ '<div class="toolbar"><span>Tools:</span></div>'
223         $tool_options = $ '<div class="tool_options"></div>'
224         $container.append $toolbar
225         $container.append $tool_options
226         svg = json_to_svg svg: width: width, height: height, viewBox: "0 0 #{width} #{height}"
227         $svg = $ svg
228         $container.append $svg
229         svg.appendChild json_to_svg filter:
230                 id: 'crayon', filterUnits: 'userSpaceOnUse'
231                 x: '-5%', y: '-5%', height: '110%', width: '110%'
232                 children: [
233                         { feTurbulence: baseFrequency: '.3', numOctaves: '2', type: 'fractalNoise' }
234                         { feDisplacementMap: scale: '6', xChannelSelector: 'R', in: 'SourceGraphic' }
235                 ]
236
237         tool_buttons =
238                 tutorial: default: true, factory: TutorialTool
239                 draw: button_text: 'draw', factory: DrawTool
240                 edit: button_text: 'edit', factory: EditTool
241                 delete: button_text: 'delete', factory: DeleteTool
242         for k, t of tool_buttons
243                 if t.button_text?
244                         t.button = $ "<span class=\"button\"></span>"
245                         t.button.text t.button_text
246                         $toolbar.append t.button
247                 do (k, t) ->
248                         activate = ->
249                                 if cur_tool?
250                                         cur_tool.disable()
251                                         $container.removeClass "#{cur_tool_name}_tool"
252                                         ($toolbar.find '.button').removeClass 'disabled'
253                                 cur_tool_name = k
254                                 $container.addClass "#{cur_tool_name}_tool"
255                                 $tool_options.empty()
256                                 cur_tool = new t.factory button: t.button, tool_options: $tool_options
257                         if t.button?
258                                 t.button.click activate
259                         if t.default
260                                 activate()
261
262         $svg.mousedown (e) ->
263                 offset = $svg.offset()
264                 if cur_tool?
265                         mouse_x = Math.round(e.pageX - offset.left)
266                         mouse_y = Math.round(e.pageY - offset.top)
267                         return cur_tool.click mouse_x, mouse_y
268         $svg.mousemove (e) ->
269                 offset = $svg.offset()
270                 if cur_tool?
271                         mouse_x = Math.round(e.pageX - offset.left)
272                         mouse_y = Math.round(e.pageY - offset.top)
273                         return cur_tool.mousemove mouse_x, mouse_y
274         ($ document).keydown (e) ->
275                 if cur_tool?
276                         return cur_tool.keydown e.keyCode
277
278 $ init