mercury-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +152 -0
  3. data/VERSION +1 -0
  4. data/app/assets/images/mercury/button.png +0 -0
  5. data/app/assets/images/mercury/clippy.png +0 -0
  6. data/app/assets/images/mercury/default-snippet.png +0 -0
  7. data/app/assets/images/mercury/loading-dark.gif +0 -0
  8. data/app/assets/images/mercury/loading-light.gif +0 -0
  9. data/app/assets/images/mercury/search-icon.png +0 -0
  10. data/app/assets/images/mercury/toolbar/editable/buttons.png +0 -0
  11. data/app/assets/images/mercury/toolbar/markupable/buttons.png +0 -0
  12. data/app/assets/images/mercury/toolbar/primary/_expander.png +0 -0
  13. data/app/assets/images/mercury/toolbar/primary/_pressed.png +0 -0
  14. data/app/assets/images/mercury/toolbar/primary/historypanel.png +0 -0
  15. data/app/assets/images/mercury/toolbar/primary/insertcharacter.png +0 -0
  16. data/app/assets/images/mercury/toolbar/primary/insertlink.png +0 -0
  17. data/app/assets/images/mercury/toolbar/primary/insertmedia.png +0 -0
  18. data/app/assets/images/mercury/toolbar/primary/inserttable.png +0 -0
  19. data/app/assets/images/mercury/toolbar/primary/inspectorpanel.png +0 -0
  20. data/app/assets/images/mercury/toolbar/primary/notespanel.png +0 -0
  21. data/app/assets/images/mercury/toolbar/primary/objectspanel.png +0 -0
  22. data/app/assets/images/mercury/toolbar/primary/preview.png +0 -0
  23. data/app/assets/images/mercury/toolbar/primary/redo.png +0 -0
  24. data/app/assets/images/mercury/toolbar/primary/save.png +0 -0
  25. data/app/assets/images/mercury/toolbar/primary/todospanel.png +0 -0
  26. data/app/assets/images/mercury/toolbar/primary/undo.png +0 -0
  27. data/app/assets/images/mercury/toolbar/snippetable/buttons.png +0 -0
  28. data/app/assets/javascripts/mercury.js +30 -0
  29. data/app/assets/javascripts/mercury/dialog.js.coffee +75 -0
  30. data/app/assets/javascripts/mercury/dialogs/backcolor.js.coffee +6 -0
  31. data/app/assets/javascripts/mercury/dialogs/forecolor.js.coffee +6 -0
  32. data/app/assets/javascripts/mercury/dialogs/formatblock.js.coffee +4 -0
  33. data/app/assets/javascripts/mercury/dialogs/objectspanel.js.coffee +10 -0
  34. data/app/assets/javascripts/mercury/dialogs/style.js.coffee +4 -0
  35. data/app/assets/javascripts/mercury/history_buffer.js.coffee +30 -0
  36. data/app/assets/javascripts/mercury/mercury.js.coffee +293 -0
  37. data/app/assets/javascripts/mercury/modal.js.coffee +177 -0
  38. data/app/assets/javascripts/mercury/modals/htmleditor.js.coffee +10 -0
  39. data/app/assets/javascripts/mercury/modals/insertcharacter.js.coffee +4 -0
  40. data/app/assets/javascripts/mercury/modals/insertlink.js.coffee +92 -0
  41. data/app/assets/javascripts/mercury/modals/insertmedia.js.coffee +72 -0
  42. data/app/assets/javascripts/mercury/modals/insertsnippet.js.coffee +11 -0
  43. data/app/assets/javascripts/mercury/modals/inserttable.js.coffee +56 -0
  44. data/app/assets/javascripts/mercury/native_extensions.js.coffee +47 -0
  45. data/app/assets/javascripts/mercury/page_editor.js.coffee +139 -0
  46. data/app/assets/javascripts/mercury/palette.js.coffee +29 -0
  47. data/app/assets/javascripts/mercury/panel.js.coffee +97 -0
  48. data/app/assets/javascripts/mercury/region.js.coffee +103 -0
  49. data/app/assets/javascripts/mercury/regions/editable.js.coffee +546 -0
  50. data/app/assets/javascripts/mercury/regions/markupable.js.coffee +380 -0
  51. data/app/assets/javascripts/mercury/regions/snippetable.js.coffee +127 -0
  52. data/app/assets/javascripts/mercury/select.js.coffee +40 -0
  53. data/app/assets/javascripts/mercury/snippet.js.coffee +92 -0
  54. data/app/assets/javascripts/mercury/snippet_toolbar.js.coffee +69 -0
  55. data/app/assets/javascripts/mercury/statusbar.js.coffee +25 -0
  56. data/app/assets/javascripts/mercury/table_editor.js.coffee +266 -0
  57. data/app/assets/javascripts/mercury/toolbar.button.js.coffee +152 -0
  58. data/app/assets/javascripts/mercury/toolbar.button_group.js.coffee +42 -0
  59. data/app/assets/javascripts/mercury/toolbar.expander.js.coffee +56 -0
  60. data/app/assets/javascripts/mercury/toolbar.js.coffee +72 -0
  61. data/app/assets/javascripts/mercury/tooltip.js.coffee +67 -0
  62. data/app/assets/javascripts/mercury/uploader.js.coffee +213 -0
  63. data/app/assets/javascripts/mercury/websocket.js.coffee +34 -0
  64. data/app/assets/stylesheets/mercury.css +31 -0
  65. data/app/assets/stylesheets/mercury/dialog.scss +178 -0
  66. data/app/assets/stylesheets/mercury/mercury.scss +119 -0
  67. data/app/assets/stylesheets/mercury/modal.scss +192 -0
  68. data/app/assets/stylesheets/mercury/statusbar.scss +23 -0
  69. data/app/assets/stylesheets/mercury/toolbar.scss +417 -0
  70. data/app/assets/stylesheets/mercury/tooltip.scss +26 -0
  71. data/app/assets/stylesheets/mercury/uploader.scss +109 -0
  72. data/app/controllers/images_controller.rb +19 -0
  73. data/app/controllers/mercury_controller.rb +20 -0
  74. data/app/models/image.rb +14 -0
  75. data/app/views/layouts/mercury.html.haml +12 -0
  76. data/app/views/mercury/modals/character.html.haml +252 -0
  77. data/app/views/mercury/modals/htmleditor.html.haml +8 -0
  78. data/app/views/mercury/modals/link.html.haml +31 -0
  79. data/app/views/mercury/modals/media.html.haml +33 -0
  80. data/app/views/mercury/modals/sanitizer.html.haml +4 -0
  81. data/app/views/mercury/modals/table.html.haml +49 -0
  82. data/app/views/mercury/palettes/backcolor.html.haml +79 -0
  83. data/app/views/mercury/palettes/forecolor.html.haml +79 -0
  84. data/app/views/mercury/panels/history.html.haml +0 -0
  85. data/app/views/mercury/panels/notes.html.haml +0 -0
  86. data/app/views/mercury/panels/snippets.html.haml +10 -0
  87. data/app/views/mercury/selects/formatblock.html.haml +10 -0
  88. data/app/views/mercury/selects/style.html.haml +4 -0
  89. data/app/views/mercury/snippets/example.html.haml +2 -0
  90. data/app/views/mercury/snippets/example_options.html.haml +16 -0
  91. data/config/engine.rb +6 -0
  92. data/config/routes.rb +15 -0
  93. data/db/migrate/20110526035601_create_images.rb +11 -0
  94. data/features/editing/basic.feature +11 -0
  95. data/features/step_definitions/debug_steps.rb +14 -0
  96. data/features/step_definitions/web_steps.rb +211 -0
  97. data/features/support/env.rb +46 -0
  98. data/features/support/paths.rb +35 -0
  99. data/features/support/selectors.rb +42 -0
  100. data/lib/mercury-rails.rb +4 -0
  101. data/log/.gitkeep +0 -0
  102. data/mercury-rails.gemspec +230 -0
  103. data/spec/javascripts/mercury/dialog_spec.js.coffee +258 -0
  104. data/spec/javascripts/mercury/history_buffer_spec.js.coffee +79 -0
  105. data/spec/javascripts/mercury/mercury_spec.js.coffee +52 -0
  106. data/spec/javascripts/mercury/native_extensions_spec.js.coffee +66 -0
  107. data/spec/javascripts/mercury/page_editor_spec.js.coffee +435 -0
  108. data/spec/javascripts/mercury/palette_spec.js.coffee +51 -0
  109. data/spec/javascripts/mercury/panel_spec.js.coffee +147 -0
  110. data/spec/javascripts/mercury/region_spec.js.coffee +261 -0
  111. data/spec/javascripts/mercury/regions/_editable_.js.coffee +0 -0
  112. data/spec/javascripts/mercury/regions/_markupable_.js.coffee +0 -0
  113. data/spec/javascripts/mercury/regions/snippetable_spec.js.coffee +368 -0
  114. data/spec/javascripts/mercury/select_spec.js.coffee +51 -0
  115. data/spec/javascripts/mercury/snippet_spec.js.coffee +246 -0
  116. data/spec/javascripts/mercury/snippet_toolbar_spec.js.coffee +186 -0
  117. data/spec/javascripts/mercury/statusbar_spec.js.coffee +78 -0
  118. data/spec/javascripts/mercury/table_editor_spec.js.coffee +192 -0
  119. data/spec/javascripts/mercury/toolbar.button_group_spec.js.coffee +92 -0
  120. data/spec/javascripts/mercury/toolbar.button_spec.js.coffee +341 -0
  121. data/spec/javascripts/mercury/toolbar.expander_spec.js.coffee +120 -0
  122. data/spec/javascripts/mercury/toolbar_spec.js.coffee +152 -0
  123. data/spec/javascripts/mercury/tooltip_spec.js.coffee +188 -0
  124. data/spec/javascripts/mercury/uploader_spec.js.coffee +512 -0
  125. data/spec/javascripts/responses/blank.html +1 -0
  126. data/spec/javascripts/spec_helper.js +513 -0
  127. data/spec/javascripts/templates/mercury/dialog.html +2 -0
  128. data/spec/javascripts/templates/mercury/page_editor.html +24 -0
  129. data/spec/javascripts/templates/mercury/palette.html +16 -0
  130. data/spec/javascripts/templates/mercury/panel.html +16 -0
  131. data/spec/javascripts/templates/mercury/region.html +2 -0
  132. data/spec/javascripts/templates/mercury/regions/snippetable.html +4 -0
  133. data/spec/javascripts/templates/mercury/select.html +16 -0
  134. data/spec/javascripts/templates/mercury/snippet.html +1 -0
  135. data/spec/javascripts/templates/mercury/snippet_toolbar.html +16 -0
  136. data/spec/javascripts/templates/mercury/statusbar.html +7 -0
  137. data/spec/javascripts/templates/mercury/table_editor.html +65 -0
  138. data/spec/javascripts/templates/mercury/toolbar.button.html +64 -0
  139. data/spec/javascripts/templates/mercury/toolbar.button_group.html +9 -0
  140. data/spec/javascripts/templates/mercury/toolbar.expander.html +18 -0
  141. data/spec/javascripts/templates/mercury/toolbar.html +10 -0
  142. data/spec/javascripts/templates/mercury/tooltip.html +12 -0
  143. data/spec/javascripts/templates/mercury/uploader.html +11 -0
  144. data/vendor/assets/javascripts/jquery-1.6.js +8865 -0
  145. data/vendor/assets/javascripts/jquery-ui-1.8.13.custom.min.js +249 -0
  146. data/vendor/assets/javascripts/jquery-ui-1.8.13.sortable.custom.js +1078 -0
  147. data/vendor/assets/javascripts/jquery.easing.js +173 -0
  148. data/vendor/assets/javascripts/jquery.json2.js +178 -0
  149. data/vendor/assets/javascripts/jquery.serialize_object.js +16 -0
  150. data/vendor/assets/javascripts/jquery.ujs.js +289 -0
  151. data/vendor/assets/javascripts/liquidmetal.js +88 -0
  152. data/vendor/assets/javascripts/showdown.js +1362 -0
  153. metadata +364 -0
@@ -0,0 +1,368 @@
1
+ require '/assets/mercury/mercury.js'
2
+
3
+ describe "Mercury.Regions.Snippetable", ->
4
+
5
+ template 'mercury/regions/snippetable.html'
6
+
7
+ beforeEach ->
8
+ @regionElement = $('#snippetable_region1')
9
+
10
+ afterEach ->
11
+ @region = null
12
+ delete(@region)
13
+
14
+ describe "constructor", ->
15
+
16
+ beforeEach ->
17
+ @buildSpy = spyOn(Mercury.Regions.Snippetable.prototype, 'build').andCallFake(=>)
18
+ @bindEventsSpy = spyOn(Mercury.Regions.Snippetable.prototype, 'bindEvents').andCallFake(=>)
19
+ @makeSortableSpy = spyOn(Mercury.Regions.Snippetable.prototype, 'makeSortable').andCallFake(=>)
20
+
21
+ it "expects an element, window", ->
22
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
23
+ expect(@region.element.get(0)).toEqual($('#snippetable_region1').get(0))
24
+ expect(@region.window).toEqual(window)
25
+
26
+ it "accepts options", ->
27
+ @region = new Mercury.Regions.Snippetable(@regionElement, window, {foo: 'something'})
28
+ expect(@region.options).toEqual({foo: 'something'})
29
+
30
+ it "calls build", ->
31
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
32
+ expect(@buildSpy.callCount).toEqual(1)
33
+
34
+ it "calls bindEvents", ->
35
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
36
+ expect(@bindEventsSpy.callCount).toEqual(1)
37
+
38
+ it "makes the snippets sortable", ->
39
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
40
+ expect(@makeSortableSpy.callCount).toEqual(1)
41
+
42
+
43
+ describe "#build", ->
44
+
45
+ beforeEach ->
46
+ spyOn(Mercury.Regions.Snippetable.prototype, 'bindEvents').andCallFake(=>)
47
+
48
+ it "sets the element min-height to 20 if it's min-height is 0 (or not set)", ->
49
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
50
+ expect($('#snippetable_region1').css('minHeight')).toEqual('20px')
51
+
52
+
53
+ describe "observed events", ->
54
+
55
+ beforeEach ->
56
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
57
+ Mercury.region = @region
58
+
59
+ describe "custom event: unfocus:regions", ->
60
+
61
+ it "removes the focus class", ->
62
+ @region.element.addClass('focus')
63
+ Mercury.trigger('unfocus:regions')
64
+ expect(@region.element.hasClass('focus')).toEqual(false)
65
+
66
+ it "destroys the sortable", ->
67
+ spy = spyOn($.fn, 'sortable').andCallFake(=>)
68
+ Mercury.trigger('unfocus:regions')
69
+ expect(spy.argsForCall[0]).toEqual(['destroy'])
70
+
71
+ it "triggers the region:blurred event", ->
72
+ spy = spyOn(Mercury, 'trigger').andCallThrough()
73
+ Mercury.trigger('unfocus:regions')
74
+ expect(spy.callCount).toEqual(2)
75
+ expect(spy.argsForCall[1]).toEqual(['region:blurred', {region: @region}])
76
+
77
+ it "does nothing if previewing", ->
78
+ @region.previewing = true
79
+ @region.element.addClass('focus')
80
+ Mercury.trigger('unfocus:regions')
81
+ expect(@region.element.hasClass('focus')).toEqual(true)
82
+
83
+ it "does nothing if it's not the active region", ->
84
+ Mercury.region = null
85
+ @region.element.addClass('focus')
86
+ Mercury.trigger('unfocus:regions')
87
+ expect(@region.element.hasClass('focus')).toEqual(true)
88
+
89
+ describe "custom event: focus:window", ->
90
+
91
+ it "removes the focus class", ->
92
+ @region.element.addClass('focus')
93
+ Mercury.trigger('focus:window')
94
+ expect(@region.element.hasClass('focus')).toEqual(false)
95
+
96
+ it "destroys the sortable", ->
97
+ spy = spyOn($.fn, 'sortable').andCallFake(=>)
98
+ Mercury.trigger('focus:window')
99
+ expect(spy.argsForCall[0]).toEqual(['destroy'])
100
+
101
+ it "triggers the region:blurred event", ->
102
+ spy = spyOn(Mercury, 'trigger').andCallThrough()
103
+ Mercury.trigger('focus:window')
104
+ expect(spy.callCount).toEqual(2)
105
+ expect(spy.argsForCall[1]).toEqual(['region:blurred', {region: @region}])
106
+
107
+ it "does nothing if previewing", ->
108
+ @region.previewing = true
109
+ @region.element.addClass('focus')
110
+ Mercury.trigger('focus:window')
111
+ expect(@region.element.hasClass('focus')).toEqual(true)
112
+
113
+ it "does nothing if it's not the active region", ->
114
+ Mercury.region = null
115
+ @region.element.addClass('focus')
116
+ Mercury.trigger('focus:window')
117
+ expect(@region.element.hasClass('focus')).toEqual(true)
118
+
119
+ describe "keydown on document (for undo / redo)", ->
120
+
121
+ it "calls execCommand with undo on meta+z", ->
122
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'execCommand')
123
+ jasmine.simulate.keydown(document, {shiftKey: false, metaKey: true, keyCode: 90})
124
+ expect(spy.callCount).toEqual(1)
125
+ expect(spy.argsForCall[0]).toEqual(['undo'])
126
+
127
+ it "calls execCommand with redo on shift+meta+z", ->
128
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'execCommand')
129
+ jasmine.simulate.keydown(document, {shiftKey: true, metaKey: true, keyCode: 90})
130
+ expect(spy.callCount).toEqual(1)
131
+ expect(spy.argsForCall[0]).toEqual(['redo'])
132
+
133
+ it "does nothing if previewing", ->
134
+ @region.previewing = true
135
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'execCommand')
136
+ jasmine.simulate.keydown(document, {shiftKey: true, metaKey: true, keyCode: 90})
137
+ expect(spy.callCount).toEqual(0)
138
+
139
+ it "does nothing if it's not the active region", ->
140
+ Mercury.region = null
141
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'execCommand')
142
+ jasmine.simulate.keydown(document, {shiftKey: true, metaKey: true, keyCode: 90})
143
+ expect(spy.callCount).toEqual(0)
144
+
145
+ describe "mouseup", ->
146
+
147
+ it "calls focus", ->
148
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'focus')
149
+ jasmine.simulate.mouseup(@region.element.get(0))
150
+ expect(spy.callCount).toEqual(1)
151
+
152
+ it "triggers the region:focused event", ->
153
+ spy = spyOn(Mercury, 'trigger')
154
+ jasmine.simulate.mouseup(@region.element.get(0))
155
+ expect(spy.callCount).toEqual(1)
156
+
157
+ it "does nothing if previewing", ->
158
+ @region.previewing = true
159
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'focus')
160
+ jasmine.simulate.mouseup(@region.element.get(0))
161
+ expect(spy.callCount).toEqual(0)
162
+
163
+ describe "dragover", ->
164
+
165
+ # todo: I'd like to find a nice way to test these things
166
+ it "prevents the default event", ->
167
+ it "does nothing if previewing", ->
168
+
169
+ describe "drop", ->
170
+
171
+ # todo: I'd like to find a nice way to test these things
172
+ it "calls focus", ->
173
+ it "prevents the default event", ->
174
+ it "displays the options for the snippet that was dropped", ->
175
+ it "does nothing if previewing", ->
176
+ it "does nothing if there's no active snippet", ->
177
+
178
+
179
+ describe "#focus", ->
180
+
181
+ beforeEach ->
182
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
183
+
184
+ it "sets the active mercury region", ->
185
+ Mercury.region = null
186
+ @region.focus()
187
+ expect(Mercury.region).toEqual(@region)
188
+
189
+ it "makes the snippets sortable again", ->
190
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'makeSortable')
191
+ @region.focus()
192
+ expect(spy.callCount).toEqual(1)
193
+
194
+ it "adds the focus class to the element", ->
195
+ @region.focus()
196
+ expect($('#snippetable_region1').hasClass('focus')).toEqual(true)
197
+
198
+
199
+ describe "#togglePreview", ->
200
+
201
+ beforeEach ->
202
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
203
+
204
+ describe "when not previewing", ->
205
+
206
+ it "it destroys the sortable", ->
207
+ spy = spyOn($.fn, 'sortable').andCallFake(=>)
208
+ @region.togglePreview()
209
+ expect(spy.callCount).toEqual(1)
210
+ expect(spy.argsForCall[0]).toEqual(['destroy'])
211
+
212
+ it "removes the focus class", ->
213
+ @regionElement.addClass('focus')
214
+ @region.togglePreview()
215
+ expect($('#snippetable_region1').hasClass('focus')).toEqual(false)
216
+
217
+ describe "when previewing", ->
218
+
219
+ beforeEach ->
220
+ @region.previewing = true
221
+
222
+ it "makes the snippets sortable again", ->
223
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'makeSortable')
224
+ @region.togglePreview()
225
+ expect(spy.callCount).toEqual(1)
226
+
227
+
228
+ describe "#execCommand", ->
229
+
230
+ beforeEach ->
231
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
232
+ Mercury.Regions.Snippetable.actions['foo'] = ->
233
+ @handlerSpy = spyOn(Mercury.Regions.Snippetable.actions, 'foo')
234
+
235
+ it "calls a handler (from the actions) if one exists", ->
236
+ @region.execCommand('foo', {value: 'something'})
237
+ expect(@handlerSpy.callCount).toEqual(1)
238
+ expect(@handlerSpy.argsForCall[0]).toEqual([{value: 'something'}])
239
+
240
+
241
+ describe "#makeSortable", ->
242
+
243
+ beforeEach ->
244
+ @region = new Mercury.Regions.Snippetable(@regionElement, window)
245
+ @sortableSpy = spyOn($.fn, 'sortable')
246
+
247
+ it "makes the snippets sortable", ->
248
+ @sortableSpy.andCallFake((arg) => return @region.element if arg == 'destroy' )
249
+ @region.makeSortable()
250
+ expect(@sortableSpy.callCount).toEqual(2)
251
+ expect(@sortableSpy.argsForCall[0]).toEqual(['destroy'])
252
+ expect(@sortableSpy.argsForCall[1][0]['document']).toEqual(@region.document)
253
+
254
+ it "triggers the hide:toolbar event at the end of the dragging", ->
255
+ spy = spyOn(Mercury, 'trigger').andCallFake(=>)
256
+ @sortableSpy.andCallFake((arg) => if arg == 'destroy' then return @region.element else arg.beforeStop())
257
+ @region.makeSortable()
258
+ expect(spy.callCount).toEqual(1)
259
+ expect(spy.argsForCall[0]).toEqual(['hide:toolbar', {type: 'snippet', immediately: true}])
260
+
261
+ it "pushes to the history after dragging", ->
262
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'pushHistory').andCallFake(=>)
263
+ spyOn(window, 'setTimeout').andCallFake((callback)=> callback())
264
+ @sortableSpy.andCallFake((arg) => if arg == 'destroy' then return @region.element else arg.stop())
265
+ @region.makeSortable()
266
+ expect(spy.callCount).toEqual(1)
267
+
268
+
269
+
270
+ describe "Mercury.Regions.Snippetable.actions", ->
271
+
272
+ template 'mercury/regions/snippetable.html'
273
+
274
+ beforeEach ->
275
+ @region = new Mercury.Regions.Snippetable($('#snippetable_region2'), window)
276
+ @actions = Mercury.Regions.Snippetable.actions
277
+
278
+ describe ".undo", ->
279
+
280
+ it "calls undo on the history buffer and sets the content", ->
281
+ htmlSpy = spyOn(Mercury.Regions.Snippetable.prototype, 'html').andCallFake(=>)
282
+ historySpy = spyOn(@region.history, 'undo').andCallFake(=> 'history -1')
283
+ @actions['undo'].call(@region)
284
+ expect(historySpy.callCount).toEqual(1)
285
+ expect(htmlSpy.callCount).toEqual(1)
286
+ expect(htmlSpy.argsForCall[0]).toEqual(['history -1'])
287
+
288
+
289
+ describe ".redo", ->
290
+
291
+ it "calls redo on the history buffer and sets the content", ->
292
+ htmlSpy = spyOn(Mercury.Regions.Snippetable.prototype, 'html').andCallFake(=>)
293
+ historySpy = spyOn(@region.history, 'redo').andCallFake(=> 'history +1')
294
+ @actions['redo'].call(@region)
295
+ expect(historySpy.callCount).toEqual(1)
296
+ expect(htmlSpy.callCount).toEqual(1)
297
+ expect(htmlSpy.argsForCall[0]).toEqual(['history +1'])
298
+
299
+
300
+ describe ".insertsnippet", ->
301
+
302
+ beforeEach ->
303
+ Mercury.Snippet.load({
304
+ 'snippet_1': {name: 'example', options: {'foo': 'bar'}},
305
+ 'snippet_2': {name: 'example', options: {'foo': 'bar'}},
306
+ })
307
+ spyOn(Mercury.Snippet.prototype, 'loadPreview').andCallFake(=>)
308
+
309
+ describe "updating a snippet", ->
310
+
311
+ it "finds the snippet by it's identity and replaces it with the new snippet", ->
312
+ @actions['insertsnippet'].call(@region, {value: Mercury.Snippet.find('snippet_1')})
313
+ expect($('#snippetable_region2').html()).toContain('class="mercury-snippet"')
314
+ expect($('#snippetable_region2').html()).toContain('contenteditable="false"')
315
+ expect($('#snippetable_region2').html()).toContain('data-version="1"')
316
+ expect($('#snippetable_region2').html()).toContain('data-snippet="snippet_1"')
317
+ expect($('#snippetable_region2').html()).toContain('[snippet_1]')
318
+
319
+ it "pushes to the history after it's been rendered", ->
320
+ spyOn(Mercury.Snippet.prototype, 'getHTML').andCallFake((x, callback) => callback() if callback)
321
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'pushHistory').andCallFake(=>)
322
+ @actions['insertsnippet'].call(@region, {value: Mercury.Snippet.find('snippet_1')})
323
+ expect(spy.callCount).toEqual(1)
324
+
325
+ describe "inserting a snippet", ->
326
+
327
+ it "appends the new snippet html to the element", ->
328
+ @actions['insertsnippet'].call(@region, {value: Mercury.Snippet.find('snippet_2')})
329
+ expect($('#snippetable_region2 .mercury-snippet').length).toEqual(2)
330
+
331
+ it "pushes to the history after it's been rendered", ->
332
+ spyOn(Mercury.Snippet.prototype, 'getHTML').andCallFake((x, callback) => callback() if callback)
333
+ spy = spyOn(Mercury.Regions.Snippetable.prototype, 'pushHistory').andCallFake(=>)
334
+ @actions['insertsnippet'].call(@region, {value: Mercury.Snippet.find('snippet_2')})
335
+ expect(spy.callCount).toEqual(1)
336
+
337
+
338
+ describe ".editsnippet", ->
339
+
340
+ beforeEach ->
341
+ @region.snippet = $('#snippetable_region2 .mercury-snippet')
342
+
343
+ it "finds and displays the options for the given snippet", ->
344
+ spy = spyOn(Mercury.Snippet.prototype, 'displayOptions')
345
+ @actions['editsnippet'].call(@region)
346
+ expect(spy.callCount).toEqual(1)
347
+
348
+ it "does nothing if there's no active snippet (eg. hovered over)", ->
349
+ @region.snippet = null
350
+ spy = spyOn(Mercury.Snippet.prototype, 'displayOptions')
351
+ @actions['editsnippet'].call(@region)
352
+ expect(spy.callCount).toEqual(0)
353
+
354
+
355
+ describe ".removesnippet", ->
356
+
357
+ beforeEach ->
358
+ @region.snippet = $('#snippetable_region2 .mercury-snippet')
359
+
360
+ it "removes the snippet if there's an active one", ->
361
+ @actions['removesnippet'].call(@region)
362
+ expect($('#snippetable_region2 .mercury-snippet').length).toEqual(0)
363
+
364
+ it "triggers the hide:toolbar event", ->
365
+ spy = spyOn(Mercury, 'trigger').andCallFake(=>)
366
+ @actions['removesnippet'].call(@region)
367
+ expect(spy.callCount).toEqual(1)
368
+ expect(spy.argsForCall[0]).toEqual(['hide:toolbar', {type: 'snippet', immediately: true}])
@@ -0,0 +1,51 @@
1
+ require '/assets/mercury/mercury.js'
2
+
3
+ describe "Mercury.Select", ->
4
+
5
+ template 'mercury/select.html'
6
+
7
+ beforeEach ->
8
+ $.fx.off = true
9
+
10
+ afterEach ->
11
+ @select = null
12
+ delete(@select)
13
+
14
+ describe "#build", ->
15
+
16
+ it "builds an element", ->
17
+ @select = new Mercury.Select('/evergreen/responses/blank.html', 'foo', {appendTo: '#test', for: $('#button')})
18
+ html = $('<div>').html(@select.element).html()
19
+ expect(html).toContain('class="mercury-select mercury-foo-select loading"')
20
+ expect(html).toContain('style="display:none"')
21
+
22
+ it "appends to any element", ->
23
+ @select = new Mercury.Select('/evergreen/responses/blank.html', 'foo', {appendTo: '#select_container', for: $('#button')})
24
+ expect($('#select_container .mercury-select').length).toEqual(1)
25
+
26
+
27
+ describe "observed events", ->
28
+
29
+ beforeEach ->
30
+ @select = new Mercury.Select('/evergreen/responses/blank.html', 'foo', {appendTo: '#test', for: $('#button')})
31
+
32
+ it "hides", ->
33
+ @select.element.css({display: 'block'})
34
+ Mercury.trigger('hide:dialogs')
35
+ expect(@select.element.css('display')).toEqual('none')
36
+
37
+ it "doesn't hide if it's the same dialog", ->
38
+ @select.element.css({display: 'block'})
39
+ Mercury.trigger('hide:dialogs', @select)
40
+ expect(@select.element.css('display')).toEqual('block')
41
+
42
+
43
+ describe "#position", ->
44
+
45
+ beforeEach ->
46
+ @select = new Mercury.Select('/evergreen/responses/blank.html', 'foo', {appendTo: '#test', for: $('#button')})
47
+
48
+ it "positions based on it's button", ->
49
+ @select.element.css({display: 'block'})
50
+ @select.position(true)
51
+ expect(@select.element.offset()).toEqual({top: 20, left: 42})
@@ -0,0 +1,246 @@
1
+ require '/assets/mercury/mercury.js'
2
+
3
+ describe "Mercury.Snippet", ->
4
+
5
+ template 'mercury/snippet.html'
6
+
7
+ afterEach ->
8
+ Mercury.Snippet.all = []
9
+
10
+ describe "constructor", ->
11
+
12
+ beforeEach ->
13
+ @setOptionsSpy = spyOn(Mercury.Snippet.prototype, 'setOptions').andCallFake(=>)
14
+ @snippet = new Mercury.Snippet('foo', 'identity', {foo: 'bar'})
15
+
16
+ it "expects name and identity", ->
17
+ expect(@snippet.name).toEqual('foo')
18
+ expect(@snippet.identity).toEqual('identity')
19
+
20
+ it "sets the version", ->
21
+ expect(@snippet.version).toEqual(0)
22
+ expect(@snippet.identity).toEqual('identity')
23
+
24
+ it "creates a history buffer", ->
25
+ expect(@snippet.history).toBeDefined()
26
+
27
+ it "calls setOptions", ->
28
+ expect(@setOptionsSpy.callCount).toEqual(1)
29
+
30
+
31
+ describe "#getHTML", ->
32
+
33
+ beforeEach ->
34
+ @loadPreviewSpy = spyOn(Mercury.Snippet.prototype, 'loadPreview').andCallFake(=>)
35
+ @snippet = new Mercury.Snippet('foo', 'identity', {foo: 'bar'})
36
+
37
+ it "builds an element (in whatever context is provided", ->
38
+ ret = @snippet.getHTML($(document))
39
+ html = $('<div>').html(ret).html()
40
+ expect(html).toContain('class="mercury-snippet"')
41
+ expect(html).toContain('contenteditable="false"')
42
+ expect(html).toContain('data-snippet="identity"')
43
+ expect(html).toContain('data-version="1"')
44
+
45
+ it "sets the default content to the identity", ->
46
+ ret = @snippet.getHTML($(document))
47
+ html = $('<div>').html(ret).html()
48
+ expect(ret.html()).toEqual('[identity]')
49
+
50
+ it "calls loadPreview", ->
51
+ @snippet.getHTML($(document))
52
+ expect(@loadPreviewSpy.callCount).toEqual(1)
53
+
54
+ it "passes callback method to loadPreview", ->
55
+ callback = =>
56
+ @snippet.getHTML($(document), callback)
57
+ expect(@loadPreviewSpy.argsForCall[0][1]).toEqual(callback)
58
+
59
+
60
+ describe "#getText", ->
61
+
62
+ beforeEach ->
63
+ @snippet = new Mercury.Snippet('foo', 'identity', {foo: 'bar'})
64
+
65
+ it "returns an identity string", ->
66
+ expect(@snippet.getText()).toEqual('[--identity--]')
67
+
68
+
69
+ describe "#loadPreview", ->
70
+
71
+ beforeEach ->
72
+ @ajaxSpy = spyOn($, 'ajax')
73
+ @snippet = new Mercury.Snippet('foo', 'identity', {foo: 'bar'})
74
+
75
+ it "makes an ajax request", ->
76
+ @ajaxSpy.andCallFake(=>)
77
+ @snippet.loadPreview()
78
+ expect(@ajaxSpy.callCount).toEqual(1)
79
+ expect(@ajaxSpy.argsForCall[0][0]).toEqual("/mercury/snippets/foo/preview")
80
+ expect(@ajaxSpy.argsForCall[0][1]['data']).toEqual({foo: 'bar'})
81
+ expect(@ajaxSpy.argsForCall[0][1]['type']).toEqual('POST')
82
+
83
+ describe "ajax success", ->
84
+
85
+ beforeEach ->
86
+ @ajaxSpy.andCallFake((url, options) => options.success('data'))
87
+
88
+ it "sets the data", ->
89
+ @snippet.loadPreview($('#snippet'))
90
+ expect(@snippet.data).toEqual('data')
91
+
92
+ it "puts the data into the element", ->
93
+ @snippet.loadPreview($('#snippet'))
94
+ expect($('#snippet').html()).toEqual('data')
95
+
96
+ it "calls the callback if one was provided", ->
97
+ callCount = 0
98
+ callback = => callCount += 1
99
+ @snippet.loadPreview($('#snippet'), callback)
100
+ expect(callCount).toEqual(1)
101
+
102
+ describe "ajax failure", ->
103
+
104
+ beforeEach ->
105
+ @ajaxSpy.andCallFake((url, options) => options.error())
106
+
107
+ it "alerts the error", ->
108
+ spy = spyOn(window, 'alert').andCallFake(=>)
109
+ @snippet.loadPreview($('#snippet'))
110
+ expect(spy.callCount).toEqual(1)
111
+ expect(spy.argsForCall[0]).toEqual(['Error loading the preview for the foo snippet.'])
112
+
113
+
114
+ describe "#displayOptions", ->
115
+
116
+ beforeEach ->
117
+ @modalSpy = spyOn(Mercury, 'modal').andCallFake(=>)
118
+ @snippet = new Mercury.Snippet('foo', 'identity', {foo: 'bar'})
119
+
120
+ it "sets the global snippet to itself", ->
121
+ @snippet.displayOptions()
122
+ expect(Mercury.snippet).toEqual(@snippet)
123
+
124
+ it "opens a modal", ->
125
+ @snippet.displayOptions()
126
+ expect(@modalSpy.callCount).toEqual(1)
127
+
128
+
129
+ describe "#setOptions", ->
130
+
131
+ beforeEach ->
132
+ @snippet = new Mercury.Snippet('foo', 'identity', {foo: 'bar'})
133
+
134
+ it "removes rails form default options that aren't for storing", ->
135
+ @snippet.setOptions({foo: 'baz', utf8: 'check', authenticity_token: 'auth_token'})
136
+ expect(@snippet.options).toEqual({foo: 'baz'})
137
+
138
+ it "increases the version", ->
139
+ @snippet.setOptions({foo: 'baz'})
140
+ expect(@snippet.version).toEqual(2)
141
+
142
+ it "pushes the options to the history buffer", ->
143
+ @snippet.setOptions({foo: 'baz'})
144
+ expect(@snippet.history.stack.length).toEqual(2)
145
+
146
+
147
+ describe "#setVersion", ->
148
+
149
+ beforeEach ->
150
+ @snippet = new Mercury.Snippet('foo', 'identity', {foo: 'bar'})
151
+ @snippet.history.stack = [1, 2, {foo: 'baz'}, 4, 5, 6, 7, 8, 9, 10]
152
+
153
+ it "sets the version", ->
154
+ @snippet.setVersion(5)
155
+ expect(@snippet.version).toEqual(4)
156
+
157
+ it "accepts a version (can be a string)", ->
158
+ @snippet.setVersion('2')
159
+ expect(@snippet.version).toEqual(1)
160
+
161
+ it "pulls the version out of the history buffer", ->
162
+ @snippet.setVersion(3)
163
+ expect(@snippet.options).toEqual({foo: 'baz'})
164
+
165
+ it "returns true if successful", ->
166
+ ret = @snippet.setVersion('2')
167
+ expect(ret).toEqual(true)
168
+
169
+ it "returns false if not successful", ->
170
+ ret = @snippet.setVersion('abc')
171
+ expect(ret).toEqual(false)
172
+
173
+
174
+ describe "#serialize", ->
175
+
176
+ beforeEach ->
177
+ @snippet = new Mercury.Snippet('foo', 'identity', {foo: 'bar'})
178
+
179
+ it "returns an object with name and options", ->
180
+ ret = @snippet.serialize()
181
+ expect(ret).toEqual({name: 'foo', options: {foo: 'bar'}})
182
+
183
+
184
+
185
+ describe "Mercury.Snippet class methods", ->
186
+
187
+ afterEach ->
188
+ Mercury.Snippet.all = []
189
+
190
+ describe ".displayOptionsFor", ->
191
+
192
+ beforeEach ->
193
+ @modalSpy = spyOn(Mercury, 'modal').andCallFake(=>)
194
+
195
+ it "opens a modal with the name in the url", ->
196
+ Mercury.Snippet.displayOptionsFor('foo')
197
+ expect(@modalSpy.callCount).toEqual(1)
198
+ expect(@modalSpy.argsForCall[0]).toEqual(["/mercury/snippets/foo/options", {title: 'Snippet Options', handler: 'insertsnippet'}])
199
+
200
+ it "sets the snippet back to nothing", ->
201
+ Mercury.snippet = 'foo'
202
+ Mercury.Snippet.displayOptionsFor('foo')
203
+ expect(Mercury.snippet).toEqual(null)
204
+
205
+
206
+ describe ".create", ->
207
+
208
+ beforeEach ->
209
+ Mercury.Snippet.all = []
210
+
211
+ it "creates a new instance of Mercury.Snippet", ->
212
+ ret = Mercury.Snippet.create('foo', {foo: 'bar'})
213
+ expect(ret.options).toEqual({foo: 'bar'})
214
+
215
+ it "generates an identity and passes it to the instance", ->
216
+ ret = Mercury.Snippet.create('foo', {foo: 'bar'})
217
+ expect(ret.identity).toEqual('snippet_0')
218
+
219
+ it "pushes into the collection array", ->
220
+ Mercury.Snippet.create('foo', {foo: 'bar'})
221
+ expect(Mercury.Snippet.all.length).toEqual(1)
222
+
223
+
224
+ describe ".find", ->
225
+
226
+ beforeEach ->
227
+ Mercury.Snippet.create('foo', {foo: 'bar'})
228
+
229
+ it "finds a snippet by identity", ->
230
+ expect(Mercury.Snippet.find('snippet_0').options).toEqual({foo: 'bar'})
231
+
232
+ it "returns null if no snippet was found", ->
233
+ expect(Mercury.Snippet.find('snippet_x')).toEqual(null)
234
+
235
+
236
+ describe ".load", ->
237
+
238
+ beforeEach ->
239
+ @snippets = {
240
+ snippet_1: {name: 'foo', options: {foo: 'bar'}}
241
+ snippet_2: {name: 'bar', options: {baz: 'pizza'}}
242
+ }
243
+
244
+ it "creates a new instance for each item in the collection", ->
245
+ Mercury.Snippet.load(@snippets)
246
+ expect(Mercury.Snippet.all.length).toEqual(2)