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,79 @@
1
+ Mercury = {}
2
+ require '/assets/mercury.js'
3
+
4
+ describe "Mercury.HistoryBuffer", ->
5
+
6
+ beforeEach ->
7
+ @buffer = new Mercury.HistoryBuffer(5)
8
+
9
+ afterEach ->
10
+ @buffer = null
11
+ delete(@buffer)
12
+
13
+ describe "constructor", ->
14
+
15
+ it "accepts a max length", ->
16
+ expect(@buffer.maxLength).toEqual(5)
17
+
18
+ it "initializes an empty stack", ->
19
+ expect(@buffer.index).toEqual(0)
20
+ expect(@buffer.stack).toEqual([])
21
+
22
+
23
+ describe "#push", ->
24
+
25
+ it "won't duplicate items if the content is the same", ->
26
+ @buffer.push('1')
27
+ expect(@buffer.stack).toEqual(['1'])
28
+
29
+ @buffer.push('2<em class="mercury-marker"></em>')
30
+ expect(@buffer.stack).toEqual(['1', '2<em class="mercury-marker"></em>'])
31
+
32
+ it "pushes onto the stack where it should", ->
33
+ @buffer.push('1')
34
+ @buffer.push('2')
35
+ expect(@buffer.stack).toEqual(['1', '2'])
36
+
37
+ @buffer.index = 0
38
+ @buffer.push('3')
39
+ expect(@buffer.stack).toEqual(['1', '3'])
40
+
41
+ it "keeps the number of items within the max length by dropping the oldest items", ->
42
+ @buffer.push('1')
43
+ @buffer.push('2')
44
+ @buffer.push('3')
45
+ @buffer.push('4')
46
+ @buffer.push('5')
47
+ expect(@buffer.stack).toEqual(['1', '2', '3', '4', '5'])
48
+ @buffer.push('6')
49
+ expect(@buffer.stack).toEqual(['2', '3', '4', '5', '6'])
50
+
51
+
52
+ describe "#undo", ->
53
+
54
+ beforeEach ->
55
+ @buffer.push('1')
56
+ @buffer.push('2')
57
+
58
+ it "returns the correct item", ->
59
+ expect(@buffer.undo()).toEqual('1')
60
+
61
+ it "returns null if there are no more items to undo", ->
62
+ expect(@buffer.undo()).toEqual('1')
63
+ expect(@buffer.undo()).toEqual(null)
64
+
65
+
66
+ describe "#redo", ->
67
+
68
+ beforeEach ->
69
+ @buffer.push('1')
70
+ @buffer.push('2')
71
+
72
+ it "returns the correct item", ->
73
+ @buffer.undo()
74
+ expect(@buffer.redo()).toEqual('2')
75
+
76
+ it "returns null if there are no more items to redo", ->
77
+ @buffer.undo()
78
+ expect(@buffer.redo()).toEqual('2')
79
+ expect(@buffer.redo()).toEqual(null)
@@ -0,0 +1,52 @@
1
+ require '/assets/mercury/mercury.js'
2
+
3
+ describe "Mercury", ->
4
+
5
+ describe "supported:", ->
6
+
7
+ # this is here for documentation -- unable to test, because this is evaluated on script load
8
+ it "checks document.getElementById", ->
9
+ it "checks document.designMode", ->
10
+ it "disallows konqueror and msie", ->
11
+
12
+
13
+ describe "#bind", ->
14
+
15
+ it "binds an event prefixed with 'mercury:' to document", ->
16
+ callCount = 0
17
+ Mercury.bind('test', -> callCount += 1)
18
+ $(document).trigger("mercury:test")
19
+ expect(callCount).toEqual(1)
20
+
21
+
22
+ describe "#trigger", ->
23
+
24
+ it "triggers an event prefixed with 'mercury:' on document", ->
25
+ argsForCall = []
26
+ callCount = 0
27
+ Mercury.bind('test', -> argsForCall[callCount] = arguments; callCount += 1)
28
+ Mercury.trigger("test", {foo: 'bar'})
29
+ expect(callCount).toEqual(1)
30
+ expect(argsForCall[0][1]).toEqual({foo: 'bar'})
31
+
32
+
33
+ describe "#log", ->
34
+
35
+ beforeEach ->
36
+ window.console = {debug: -> ''}
37
+ @debugSpy = spyOn(window.console, 'debug').andCallFake(=>)
38
+ Mercury.debug = true
39
+
40
+ it "calls console.debug", ->
41
+ Mercury.log(1, 2)
42
+ expect(@debugSpy.callCount).toEqual(1)
43
+
44
+ it "does nothing if debug mode isn't on", ->
45
+ Mercury.debug = false
46
+ Mercury.log(1, 2)
47
+ expect(@debugSpy.callCount).toEqual(0)
48
+
49
+ it "does nothing if there's no console", ->
50
+ window.console = null
51
+ Mercury.log(1, 2)
52
+ expect(@debugSpy.callCount).toEqual(0)
@@ -0,0 +1,66 @@
1
+ require '/assets/mercury/mercury.js'
2
+
3
+ describe "String", ->
4
+
5
+ describe "#titleize", ->
6
+
7
+ it "should capitalize the first letter in a string", ->
8
+ expect('wow!'.titleize()).toEqual('Wow!')
9
+
10
+
11
+ describe "#toHex", ->
12
+
13
+ it "converts a rgb(0, 0, 0) type string to hex", ->
14
+ expect('rgb(0, 0, 0)'.toHex()).toEqual('#000000')
15
+ expect('rgb(255, 255, 0)'.toHex()).toEqual('#FFFF00')
16
+
17
+
18
+ describe "#singleDiff", ->
19
+
20
+ it "takes a string to compare against, and returns the first diff it comes to", ->
21
+ expect('abcdefg'.singleDiff('ab[diff]cdefg')).toEqual('[diff]')
22
+ expect('abcd/e\\f.g'.singleDiff('ab[diff]cd/e\\f.g')).toEqual('[diff]')
23
+
24
+
25
+ describe "#regExpEscape", ->
26
+
27
+ it "escapes characters used in regular expressions", ->
28
+ expect('/.*+?|()[]{}\\'.regExpEscape()).toEqual('\\/\\.\\*\\+\\?\\|\\(\\)\\[\\]\\{\\}\\\\')
29
+
30
+ describe "#sanitizeHTML", ->
31
+
32
+ it "removes style tags", ->
33
+ expect('123<style></style>456'.sanitizeHTML()).toEqual('123456')
34
+
35
+ it "removes comment tags", ->
36
+ expect('123<!--this is a comment-->456'.sanitizeHTML()).toEqual('123456')
37
+
38
+ it "replaces new lines with br tags", ->
39
+ expect('123\n456'.sanitizeHTML()).toEqual('123<br/>456')
40
+
41
+
42
+ describe "Number", ->
43
+
44
+ describe "#toHex", ->
45
+
46
+ it "converts a number to it's hex value", ->
47
+ expect(100.toHex()).toEqual('64')
48
+ expect(255.toHex()).toEqual('FF')
49
+
50
+ it "pads 0-F with a 0", ->
51
+ expect(0.toHex()).toEqual('00')
52
+ expect(15.toHex()).toEqual('0F')
53
+
54
+ describe "#toBytes", ->
55
+
56
+ it "converts a number to a readable byte representation (eg. 1.2 kb, 3.4 Mb)", ->
57
+ kb = 1024
58
+ expect(kb.toBytes()).toEqual('1.00 kb')
59
+ expect((kb + 100).toBytes()).toEqual('1.10 kb')
60
+ expect((kb * 1000).toBytes()).toEqual('1000.00 kb')
61
+ expect((kb * 1024).toBytes()).toEqual('1.00 Mb')
62
+ expect((kb * 1024 * 1024).toBytes()).toEqual('1.00 Gb')
63
+ expect((kb * 1024 * 1024 * 1024).toBytes()).toEqual('1.00 Tb')
64
+ expect((kb * 1024 * 1024 * 1024 * 1024).toBytes()).toEqual('1.00 Pb')
65
+ expect((kb * 1024 * 1024 * 1024 * 1024 * 1024).toBytes()).toEqual('1.00 Eb')
66
+
@@ -0,0 +1,435 @@
1
+ require '/assets/mercury/mercury.js'
2
+
3
+ describe "Mercury.PageEditor", ->
4
+
5
+ template 'mercury/page_editor.html'
6
+
7
+ afterEach ->
8
+ @pageEditor = null
9
+ delete(@pageEditor)
10
+ window.mercuryInstance = null
11
+ $(document).unbind('mercury:initialize:frame')
12
+ $(document).unbind('mercury:focus:frame')
13
+ $(document).unbind('mercury:focus:window')
14
+ $(document).unbind('mercury:action')
15
+ $(document).unbind('mousedown')
16
+ $(window).unbind('resize')
17
+
18
+ describe "constructor", ->
19
+
20
+ beforeEach ->
21
+ @initializeInterfaceSpy = spyOn(Mercury.PageEditor.prototype, 'initializeInterface').andCallFake(=>)
22
+
23
+ it "throws an error if it's not supported", ->
24
+ Mercury.supported = false
25
+ expect(=>
26
+ new Mercury.PageEditor()
27
+ ).toThrow('Mercury.PageEditor is unsupported in this client. Supported browsers are chrome 10+, firefix 4+, and safari 5+.')
28
+ Mercury.supported = true
29
+
30
+ it "throws an error if it's already instantiated", ->
31
+ window.mercuryInstance = true
32
+ expect(=>
33
+ new Mercury.PageEditor()
34
+ ).toThrow('Mercury.PageEditor can only be instantiated once.')
35
+ window.mercuryInstance = false
36
+
37
+ it "sets the mercuryInstance to window", ->
38
+ @pageEditor = new Mercury.PageEditor()
39
+ expect(window.mercuryInstance).toEqual(@pageEditor)
40
+
41
+ it "accepts a saveUrl, and options", ->
42
+ @pageEditor = new Mercury.PageEditor('/foo/1', {foo: 'bar'})
43
+ expect(@pageEditor.saveUrl).toEqual('/foo/1')
44
+ expect(@pageEditor.options).toEqual({foo: 'bar'})
45
+
46
+ it "calls initializeInterface", ->
47
+ @pageEditor = new Mercury.PageEditor()
48
+ expect(@initializeInterfaceSpy.callCount).toEqual(1)
49
+
50
+ it "gets the csrf token if there's one available", ->
51
+ new Mercury.PageEditor()
52
+ expect(Mercury.csrfToken).toEqual('K6JhyfOVKJX8X2ZkiJXSf491fc1fF+k79wzrChHQa0g=')
53
+
54
+
55
+ describe "#initializeInterface", ->
56
+
57
+ beforeEach ->
58
+ Mercury.Toolbar = -> {toolbar: true}
59
+ Mercury.Statusbar = -> {statusbar: true}
60
+ @iframeSrcSpy = spyOn(Mercury.PageEditor.prototype, 'iframeSrc').andCallFake(=> '/foo')
61
+
62
+ it "builds a focusable element (so we can get focus off the iframe)", ->
63
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
64
+ expect($('input[type=text]').length).toEqual(1)
65
+
66
+ it "builds an iframe", ->
67
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
68
+ expect($('iframe.mercury-iframe').length).toEqual(1)
69
+ expect($('iframe.mercury-iframe').attr('src')).toEqual('/foo')
70
+
71
+ it "appends the elements to any node", ->
72
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#page_editor_container')})
73
+ expect($('#page_editor_container input[type=text]').length).toEqual(1)
74
+ expect($('#page_editor_container iframe.mercury-iframe').length).toEqual(1)
75
+
76
+ it "instantiates the toolbar", ->
77
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
78
+ expect(@pageEditor.toolbar).toEqual({toolbar: true})
79
+
80
+ it "instantiates the statusbar", ->
81
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
82
+ expect(@pageEditor.statusbar).toEqual({statusbar: true})
83
+
84
+
85
+ describe "#initializeFrame", ->
86
+
87
+ beforeEach ->
88
+ @bindEventsSpy = spyOn(Mercury.PageEditor.prototype, 'bindEvents').andCallFake(=>)
89
+ @initializeRegionsSpy = spyOn(Mercury.PageEditor.prototype, 'initializeRegions').andCallFake(=>)
90
+ @finalizeInterfaceSpy = spyOn(Mercury.PageEditor.prototype, 'finalizeInterface')
91
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
92
+
93
+ it "does nothing if the iframe is already loaded", ->
94
+ @finalizeInterfaceSpy.andCallFake(=>)
95
+ @pageEditor.iframe.data('loaded', true)
96
+ @pageEditor.initializeFrame()
97
+ expect(@pageEditor.document).toBeUndefined()
98
+
99
+ it "tells the iframe that it's loaded", ->
100
+ @finalizeInterfaceSpy.andCallFake(=>)
101
+ @pageEditor.initializeFrame()
102
+ expect(@pageEditor.iframe.data('loaded')).toEqual(true)
103
+
104
+ it "gets the document from the iframe", ->
105
+ @finalizeInterfaceSpy.andCallFake(=>)
106
+ @pageEditor.initializeFrame()
107
+ expect(@pageEditor.document).toBeDefined()
108
+
109
+ it "injects needed mercury styles", ->
110
+ @finalizeInterfaceSpy.andCallFake(=>)
111
+ spy = spyOn($.fn, 'appendTo').andCallFake(=>)
112
+ @pageEditor.initializeFrame()
113
+ expect(spy.callCount).toEqual(1)
114
+
115
+ it "calls bindEvents", ->
116
+ @finalizeInterfaceSpy.andCallFake(=>)
117
+ @pageEditor.initializeFrame()
118
+ expect(@bindEventsSpy.callCount).toEqual(1)
119
+
120
+ it "calls initializeRegions", ->
121
+ @finalizeInterfaceSpy.andCallFake(=>)
122
+ @pageEditor.initializeFrame()
123
+ expect(@initializeRegionsSpy.callCount).toEqual(1)
124
+
125
+ it "calls finalizeInterface", ->
126
+ @finalizeInterfaceSpy.andCallFake(=>)
127
+ @pageEditor.initializeFrame()
128
+ expect(@finalizeInterfaceSpy.callCount).toEqual(1)
129
+
130
+ it "shows the iframe", ->
131
+ @finalizeInterfaceSpy.andCallFake(=>)
132
+ @pageEditor.initializeFrame()
133
+ @pageEditor.iframe.css('loaded', true)
134
+
135
+ it "captures errors and alerts them", ->
136
+ @finalizeInterfaceSpy.andCallFake(=> throw('unknown error' ))
137
+ spy = spyOn(window, 'alert').andCallFake(=>)
138
+ @pageEditor.initializeFrame()
139
+ expect(spy.callCount).toEqual(1)
140
+ expect(spy.argsForCall[0]).toEqual(['Mercury.PageEditor failed to load: unknown error\n\nPlease try refreshing.'])
141
+
142
+
143
+ describe "#initializeRegions", ->
144
+
145
+ beforeEach ->
146
+ Mercury.PageEditor.prototype.initializeFrame = ->
147
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
148
+ @pageEditor.document = $(document)
149
+ @buildRegionSpy = spyOn(Mercury.PageEditor.prototype, 'buildRegion').andCallFake(=>)
150
+
151
+ it "it calls buildRegion for all the regions found in a document", ->
152
+ @pageEditor.initializeRegions()
153
+ expect(@buildRegionSpy.callCount).toEqual(3)
154
+
155
+ it "focuses the first region", ->
156
+ firstFocusCalled = false
157
+ @pageEditor.regions = [{focus: => firstFocusCalled = true}, {}, {}]
158
+ @pageEditor.initializeRegions()
159
+ expect(firstFocusCalled).toEqual(true)
160
+
161
+
162
+ describe "#buildRegion", ->
163
+
164
+ beforeEach ->
165
+ Mercury.PageEditor.prototype.initializeFrame = ->
166
+ Mercury.Regions.Editable = -> {region: true}
167
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
168
+
169
+ it "instantiates the region and pushes it into the regions array", ->
170
+ @pageEditor.buildRegion($('#region2'))
171
+ expect(@pageEditor.regions.length).toEqual(1)
172
+ expect(@pageEditor.regions[0]).toEqual({region: true})
173
+
174
+ it "alerts on errors", ->
175
+ Mercury.debug = false
176
+ Mercury.Regions.Editable = -> throw('error!')
177
+ spy = spyOn(window, 'alert').andCallFake(=>)
178
+ @pageEditor.buildRegion($('#region2'))
179
+ expect(spy.callCount).toEqual(1)
180
+ expect(spy.argsForCall[0]).toEqual(['Region type is malformed, no data-type provided, or "Editable" is unknown.'])
181
+
182
+
183
+ describe "#finalizeInterface", ->
184
+
185
+ beforeEach ->
186
+ Mercury.PageEditor.prototype.initializeFrame = ->
187
+ Mercury.SnippetToolbar = -> {snippetToolbar: true}
188
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
189
+ @highjackLinksSpy = spyOn(Mercury.PageEditor.prototype, 'hijackLinks').andCallFake(=>)
190
+ @resizeSpy = spyOn(Mercury.PageEditor.prototype, 'resize').andCallFake(=>)
191
+
192
+ it "it builds a snippetToolbar", ->
193
+ @pageEditor.finalizeInterface()
194
+ expect(@pageEditor.snippetToolbar).toEqual({snippetToolbar: true})
195
+
196
+ it "calls hijackLinks", ->
197
+ @pageEditor.finalizeInterface()
198
+ expect(@highjackLinksSpy.callCount).toEqual(1)
199
+
200
+ it "calls resize", ->
201
+ @pageEditor.finalizeInterface()
202
+ expect(@resizeSpy.callCount).toEqual(1)
203
+
204
+
205
+ describe "observed events", ->
206
+
207
+ beforeEach ->
208
+ @initializeInterfaceSpy = spyOn(Mercury.PageEditor.prototype, 'initializeInterface').andCallFake(=>)
209
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
210
+ @pageEditor.document = $(document)
211
+ @pageEditor.bindEvents()
212
+
213
+ describe "custom event: initialize:frame", ->
214
+
215
+ it "calls initializeFrame", ->
216
+ @initializeFrameSpy = spyOn(Mercury.PageEditor.prototype, 'initializeFrame').andCallFake(=>)
217
+ @setTimeoutSpy = spyOn(window, 'setTimeout').andCallFake((callback) -> callback())
218
+ Mercury.trigger('initialize:frame')
219
+ expect(@initializeFrameSpy.callCount).toEqual(1)
220
+ expect(@setTimeoutSpy.callCount).toEqual(1)
221
+
222
+ describe "custom event: focus:frame", ->
223
+
224
+ it "calls focus on the iframe", ->
225
+ callCount = 0
226
+ @pageEditor.iframe = {focus: -> callCount += 1}
227
+ Mercury.trigger('focus:frame')
228
+ expect(callCount).toEqual(1)
229
+
230
+ describe "custom event: focus:window", ->
231
+
232
+ it "calls focus on a focusable element", ->
233
+ callCount = 0
234
+ @pageEditor.focusableElement = {focus: -> callCount += 1}
235
+ @setTimeoutSpy = spyOn(window, 'setTimeout').andCallFake((callback) -> callback())
236
+ Mercury.trigger('focus:window')
237
+ expect(callCount).toEqual(1)
238
+
239
+ describe "custom event: action", ->
240
+
241
+ it "calls save if the action was save", ->
242
+ spy = spyOn(Mercury.PageEditor.prototype, 'save').andCallFake(=>)
243
+ Mercury.trigger('action', {action: 'foo'})
244
+ expect(spy.callCount).toEqual(0)
245
+
246
+ Mercury.trigger('action', {action: 'save'})
247
+ expect(spy.callCount).toEqual(1)
248
+
249
+ describe "mousedown on document", ->
250
+
251
+ beforeEach ->
252
+ @triggerSpy = spyOn(Mercury, 'trigger').andCallFake(=>)
253
+
254
+ it "triggers hide:dialogs", ->
255
+ Mercury.region = {element: $('#region3')}
256
+ jasmine.simulate.mousedown($('#anchor1r').get(0))
257
+ expect(@triggerSpy.callCount).toEqual(1)
258
+ expect(@triggerSpy.argsForCall[0]).toEqual(['hide:dialogs'])
259
+
260
+ it "triggers unfocus:regions unless the event happened in a region", ->
261
+ Mercury.region = {element: {get: -> null}}
262
+ jasmine.simulate.mousedown(document)
263
+ expect(@triggerSpy.callCount).toEqual(2)
264
+ expect(@triggerSpy.argsForCall[1]).toEqual(['unfocus:regions'])
265
+
266
+ describe "window resize", ->
267
+
268
+ it "calls resize", ->
269
+ # untestable
270
+ #spy = spyOn(Mercury.PageEditor.prototype, 'resize').andCallFake(=>)
271
+ #resizeTo($(window).width() - 1, $(window).height() - 1)
272
+ #expect(spy.callCount).toEqual(1)
273
+
274
+ describe "onbeforeunload", ->
275
+
276
+ it "calls Mercury.beforeUnload", ->
277
+ # untestable
278
+ #spy = spyOn(Mercury, 'beforeUnload').andCallFake(=>)
279
+ #window.onbeforeunload()
280
+ #expect(spy.callCount).toEqual(1)
281
+
282
+
283
+ describe "#resize", ->
284
+
285
+ beforeEach ->
286
+ Mercury.Toolbar = -> {toolbar: true, height: -> 100}
287
+ Mercury.Statusbar = -> {statusbar: true, height: -> 10}
288
+ Mercury.PageEditor.prototype.initializeFrame = ->
289
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
290
+
291
+ it "sets the display rectangle to displayRect", ->
292
+ @pageEditor.resize()
293
+ expect(Mercury.displayRect.top).toEqual(100)
294
+ expect(Mercury.displayRect.height).toEqual($(window).height() - 100 - 10)
295
+
296
+ it "resizes the iframe", ->
297
+ @pageEditor.resize()
298
+ expect($('.mercury-iframe').css('top')).toEqual('100px')
299
+ expect($('.mercury-iframe').css('height')).toEqual("#{$(window).height() - 100 - 10}px")
300
+
301
+ it "triggers a resize event", ->
302
+ spy = spyOn(Mercury, 'trigger').andCallFake(=>)
303
+ @pageEditor.resize()
304
+ expect(spy.callCount).toEqual(1)
305
+
306
+
307
+ describe "#iframeSrc", ->
308
+
309
+ beforeEach ->
310
+ Mercury.PageEditor.prototype.initializeFrame = ->
311
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
312
+
313
+ it "takes the location and removes the /edit", ->
314
+ expect(@pageEditor.iframeSrc('http://something/edit/path')).toEqual('http://something/path')
315
+
316
+
317
+ describe "#hijackLinks", ->
318
+
319
+ beforeEach ->
320
+ Mercury.PageEditor.prototype.initializeFrame = ->
321
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
322
+ @pageEditor.options.ignoredLinks = ['lightview']
323
+ @pageEditor.document = $(document)
324
+
325
+ it "finds links and set their target to top if it's not already set", ->
326
+ @pageEditor.hijackLinks()
327
+ expect($('#anchor1').attr('target')).toEqual('_top')
328
+ expect($('#anchor2').attr('target')).toEqual('_blank')
329
+ expect($('#anchor3').attr('target')).toEqual('_top')
330
+ expect($('#anchor4').attr('target')).toEqual('_top')
331
+
332
+ it "ignores links in regions", ->
333
+ @pageEditor.hijackLinks()
334
+ expect($('#anchor1r').attr('target')).toEqual('_top')
335
+ expect($('#anchor2r').attr('target')).toEqual('_blank')
336
+ expect($('#anchor3r').attr('target')).toEqual('_self')
337
+ expect($('#anchor4r').attr('target')).toBeUndefined()
338
+
339
+ it "ignores links that are in the config to be ignored (by class)", ->
340
+ @pageEditor.hijackLinks()
341
+ expect($('#anchor5').attr('target')).toEqual('_self')
342
+
343
+
344
+ describe "#beforeUnload", ->
345
+
346
+ beforeEach ->
347
+ Mercury.PageEditor.prototype.initializeInterface = ->
348
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
349
+ Mercury.silent = false
350
+ Mercury.changes = true
351
+
352
+ it "returns a message if changes were made", ->
353
+ expect(@pageEditor.beforeUnload()).toEqual('You have unsaved changes. Are you sure you want to leave without saving them first?')
354
+
355
+ Mercury.changes = false
356
+ expect(@pageEditor.beforeUnload()).toEqual(null)
357
+
358
+ it "does nothing if in silent mode", ->
359
+ Mercury.silent = true
360
+ expect(@pageEditor.beforeUnload()).toEqual(null)
361
+
362
+
363
+ describe "#save", ->
364
+
365
+ beforeEach ->
366
+ Mercury.PageEditor.prototype.initializeInterface = ->
367
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
368
+ @iframeSrcSpy = spyOn(Mercury.PageEditor.prototype, 'iframeSrc').andCallFake(=> '/foo/baz')
369
+ @ajaxSpy = spyOn($, 'ajax')
370
+
371
+ it "makes an ajax request", ->
372
+ @ajaxSpy.andCallFake(=>)
373
+ @pageEditor.save()
374
+ expect(@ajaxSpy.callCount).toEqual(1)
375
+
376
+ it "uses the save url passed in via options, or the iframe src", ->
377
+ @ajaxSpy.andCallFake(=>)
378
+ @pageEditor.saveUrl = '/foo/bar'
379
+ @pageEditor.save()
380
+ expect(@ajaxSpy.argsForCall[0][0]).toEqual('/foo/bar')
381
+
382
+ @pageEditor.saveUrl = null
383
+ @pageEditor.save()
384
+ expect(@ajaxSpy.argsForCall[1][0]).toEqual('/foo/baz')
385
+
386
+ it "serializes the data in json", ->
387
+ @ajaxSpy.andCallFake(=>)
388
+ Mercury.config.saveStyle = 'json'
389
+ spy = spyOn(Mercury.PageEditor.prototype, 'serialize').andCallFake(=> {region1: 'region1'})
390
+ @pageEditor.save()
391
+ expect(@ajaxSpy.argsForCall[0][1]['data']).toEqual({content: '{"region1":"region1"}' })
392
+
393
+ it "can serialize as form values", ->
394
+ @ajaxSpy.andCallFake(=>)
395
+ @pageEditor.options.saveStyle = 'form'
396
+ spy = spyOn(Mercury.PageEditor.prototype, 'serialize').andCallFake(=> {region1: 'region1'})
397
+ @pageEditor.save()
398
+ expect(@ajaxSpy.argsForCall[0][1]['data']).toEqual({content: {region1: 'region1'}})
399
+
400
+ describe "on successful ajax request", ->
401
+
402
+ beforeEach ->
403
+ @ajaxSpy.andCallFake((url, options) => options.success('data') )
404
+
405
+ it "sets changes back to false", ->
406
+ Mercury.changes = true
407
+ @pageEditor.save()
408
+ expect(Mercury.changes).toEqual(false)
409
+
410
+ describe "on failed ajax request", ->
411
+
412
+ beforeEach ->
413
+ @ajaxSpy.andCallFake((url, options) => options.error() )
414
+
415
+ it "alerts with the url", ->
416
+ spy = spyOn(window, 'alert').andCallFake(=>)
417
+ @pageEditor.saveUrl = '/foo/bar'
418
+ @pageEditor.save()
419
+ expect(spy.callCount).toEqual(1)
420
+ expect(spy.argsForCall[0]).toEqual(['Mercury was unable to save to the url: /foo/bar'])
421
+
422
+
423
+ describe "#serialize", ->
424
+
425
+ beforeEach ->
426
+ Mercury.PageEditor.prototype.initializeInterface = ->
427
+ @pageEditor = new Mercury.PageEditor('', {appendTo: $('#test')})
428
+ @pageEditor.regions = [
429
+ {name: 'region1', serialize: -> 'region1'},
430
+ {name: 'region2', serialize: -> 'region2'}
431
+ ]
432
+
433
+ it "returns an object with the region name, and it's serialized value", ->
434
+ ret = @pageEditor.serialize()
435
+ expect(ret).toEqual({region1: 'region1', region2: 'region2'})