mercury-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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'})