mercury-rails 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. data/LICENSE +5 -0
  2. data/{README.rdoc → README.md} +150 -60
  3. data/VERSION +1 -1
  4. data/annotated_source.template +57 -0
  5. data/app/controllers/mercury_controller.rb +8 -4
  6. data/app/models/image.rb +2 -2
  7. data/app/views/layouts/mercury.html.erb +14 -0
  8. data/app/views/mercury/lightviews/about.html +7 -0
  9. data/app/views/mercury/lightviews/imageprocessor.html +3 -0
  10. data/app/views/mercury/modals/character.html +255 -0
  11. data/app/views/mercury/modals/htmleditor.html +13 -0
  12. data/app/views/mercury/modals/link.html +75 -0
  13. data/app/views/mercury/modals/media.html +82 -0
  14. data/app/views/mercury/modals/sanitizer.html +9 -0
  15. data/app/views/mercury/modals/table.html +84 -0
  16. data/app/views/mercury/palettes/backcolor.html +73 -0
  17. data/app/views/mercury/palettes/forecolor.html +73 -0
  18. data/app/views/mercury/panels/history.html +3 -0
  19. data/app/views/mercury/panels/notes.html +3 -0
  20. data/app/views/mercury/panels/snippets.html +12 -0
  21. data/app/views/mercury/selects/formatblock.html +11 -0
  22. data/app/views/mercury/selects/style.html +5 -0
  23. data/app/views/mercury/snippets/example.html.erb +1 -0
  24. data/app/views/mercury/snippets/example_options.html.erb +23 -0
  25. data/config/routes.rb +1 -1
  26. data/features/editing/basic.feature +1 -1
  27. data/features/support/paths.rb +1 -0
  28. data/lib/generators/mercury/install/install_generator.rb +14 -0
  29. data/mercury-rails.gemspec +115 -106
  30. data/spec/javascripts/mercury/dialog_spec.js.coffee +54 -38
  31. data/spec/javascripts/mercury/dialogs/backcolor_spec.js.coffee +1 -1
  32. data/spec/javascripts/mercury/dialogs/forecolor_spec.js.coffee +1 -1
  33. data/spec/javascripts/mercury/dialogs/formatblock_spec.js.coffee +1 -1
  34. data/spec/javascripts/mercury/dialogs/{objectspanel_spec.js.coffee → snippetpanel_spec.js.coffee} +4 -4
  35. data/spec/javascripts/mercury/dialogs/style_spec.js.coffee +1 -1
  36. data/spec/javascripts/mercury/history_buffer_spec.js.coffee +0 -1
  37. data/spec/javascripts/mercury/lightview_spec.js.coffee +448 -0
  38. data/spec/javascripts/mercury/mercury_spec.js.coffee +1 -1
  39. data/spec/javascripts/mercury/modal_spec.js.coffee +474 -0
  40. data/spec/javascripts/mercury/modals/htmleditor_spec.js.coffee +1 -1
  41. data/spec/javascripts/mercury/modals/insertcharacter_spec.js.coffee +1 -1
  42. data/spec/javascripts/mercury/modals/insertlink_spec.js.coffee +8 -2
  43. data/spec/javascripts/mercury/modals/insertmedia_spec.js.coffee +1 -1
  44. data/spec/javascripts/mercury/modals/insertsnippet_spec.js.coffee +1 -1
  45. data/spec/javascripts/mercury/modals/inserttable_spec.js.coffee +1 -1
  46. data/spec/javascripts/mercury/native_extensions_spec.js.coffee +1 -1
  47. data/spec/javascripts/mercury/page_editor_spec.js.coffee +160 -20
  48. data/spec/javascripts/mercury/palette_spec.js.coffee +1 -1
  49. data/spec/javascripts/mercury/panel_spec.js.coffee +1 -1
  50. data/spec/javascripts/mercury/region_spec.js.coffee +1 -1
  51. data/spec/javascripts/mercury/regions/editable_spec.js.coffee +191 -6
  52. data/spec/javascripts/mercury/regions/markupable_spec.js.coffee +369 -0
  53. data/spec/javascripts/mercury/regions/snippetable_spec.js.coffee +1 -1
  54. data/spec/javascripts/mercury/select_spec.js.coffee +1 -1
  55. data/spec/javascripts/mercury/snippet_spec.js.coffee +3 -3
  56. data/spec/javascripts/mercury/snippet_toolbar_spec.js.coffee +2 -2
  57. data/spec/javascripts/mercury/statusbar_spec.js.coffee +91 -17
  58. data/spec/javascripts/mercury/table_editor_spec.js.coffee +5 -1
  59. data/spec/javascripts/mercury/toolbar.button_group_spec.js.coffee +1 -1
  60. data/spec/javascripts/mercury/toolbar.button_spec.js.coffee +1 -1
  61. data/spec/javascripts/mercury/toolbar.expander_spec.js.coffee +1 -1
  62. data/spec/javascripts/mercury/toolbar_spec.js.coffee +70 -15
  63. data/spec/javascripts/mercury/tooltip_spec.js.coffee +1 -1
  64. data/spec/javascripts/mercury/uploader_spec.js.coffee +9 -1
  65. data/spec/javascripts/templates/mercury/dialogs/{objectspanel.html → snippetpanel.html} +0 -0
  66. data/spec/javascripts/templates/mercury/lightview.html +13 -0
  67. data/spec/javascripts/templates/mercury/modal.html +13 -0
  68. data/spec/javascripts/templates/mercury/page_editor.html +11 -1
  69. data/spec/javascripts/templates/mercury/statusbar.html +1 -0
  70. data/spec/javascripts/templates/mercury/toolbar.html +1 -0
  71. data/{app → vendor}/assets/images/mercury/button.png +0 -0
  72. data/{app → vendor}/assets/images/mercury/clippy.png +0 -0
  73. data/{app → vendor}/assets/images/mercury/default-snippet.png +0 -0
  74. data/{app → vendor}/assets/images/mercury/loading-dark.gif +0 -0
  75. data/{app → vendor}/assets/images/mercury/loading-light.gif +0 -0
  76. data/{app → vendor}/assets/images/mercury/search-icon.png +0 -0
  77. data/vendor/assets/images/mercury/temp-logo.png +0 -0
  78. data/{app → vendor}/assets/images/mercury/toolbar/editable/buttons.png +0 -0
  79. data/{app → vendor}/assets/images/mercury/toolbar/primary/_expander.png +0 -0
  80. data/{app → vendor}/assets/images/mercury/toolbar/primary/_pressed.png +0 -0
  81. data/{app → vendor}/assets/images/mercury/toolbar/primary/historypanel.png +0 -0
  82. data/{app → vendor}/assets/images/mercury/toolbar/primary/insertcharacter.png +0 -0
  83. data/{app → vendor}/assets/images/mercury/toolbar/primary/insertlink.png +0 -0
  84. data/{app → vendor}/assets/images/mercury/toolbar/primary/insertmedia.png +0 -0
  85. data/{app → vendor}/assets/images/mercury/toolbar/primary/inserttable.png +0 -0
  86. data/{app → vendor}/assets/images/mercury/toolbar/primary/inspectorpanel.png +0 -0
  87. data/{app → vendor}/assets/images/mercury/toolbar/primary/notespanel.png +0 -0
  88. data/{app → vendor}/assets/images/mercury/toolbar/primary/preview.png +0 -0
  89. data/{app → vendor}/assets/images/mercury/toolbar/primary/redo.png +0 -0
  90. data/{app → vendor}/assets/images/mercury/toolbar/primary/save.png +0 -0
  91. data/{app/assets/images/mercury/toolbar/primary/objectspanel.png → vendor/assets/images/mercury/toolbar/primary/snippetpanel.png} +0 -0
  92. data/{app → vendor}/assets/images/mercury/toolbar/primary/undo.png +0 -0
  93. data/{app → vendor}/assets/images/mercury/toolbar/snippetable/buttons.png +0 -0
  94. data/vendor/assets/javascripts/mercury.js +302 -0
  95. data/vendor/assets/javascripts/mercury/dialog.js.coffee +157 -0
  96. data/{app → vendor}/assets/javascripts/mercury/dialogs/backcolor.js.coffee +0 -0
  97. data/{app → vendor}/assets/javascripts/mercury/dialogs/forecolor.js.coffee +0 -0
  98. data/{app → vendor}/assets/javascripts/mercury/dialogs/formatblock.js.coffee +0 -0
  99. data/{app → vendor}/assets/javascripts/mercury/dialogs/objectspanel.js.coffee +1 -1
  100. data/{app → vendor}/assets/javascripts/mercury/dialogs/style.js.coffee +0 -0
  101. data/{app → vendor}/assets/javascripts/mercury/history_buffer.js.coffee +0 -0
  102. data/vendor/assets/javascripts/mercury/lightview.js.coffee +156 -0
  103. data/vendor/assets/javascripts/mercury/lightviews/imageprocessor.js.coffee +2 -0
  104. data/vendor/assets/javascripts/mercury/mercury.js.coffee +63 -0
  105. data/{app → vendor}/assets/javascripts/mercury/modal.js.coffee +25 -17
  106. data/{app → vendor}/assets/javascripts/mercury/modals/htmleditor.js.coffee +0 -0
  107. data/{app → vendor}/assets/javascripts/mercury/modals/insertcharacter.js.coffee +0 -0
  108. data/{app → vendor}/assets/javascripts/mercury/modals/insertlink.js.coffee +3 -0
  109. data/{app → vendor}/assets/javascripts/mercury/modals/insertmedia.js.coffee +10 -2
  110. data/{app → vendor}/assets/javascripts/mercury/modals/insertsnippet.js.coffee +0 -0
  111. data/{app → vendor}/assets/javascripts/mercury/modals/inserttable.js.coffee +2 -2
  112. data/{app → vendor}/assets/javascripts/mercury/native_extensions.js.coffee +0 -0
  113. data/{app → vendor}/assets/javascripts/mercury/page_editor.js.coffee +44 -13
  114. data/{app → vendor}/assets/javascripts/mercury/palette.js.coffee +0 -0
  115. data/{app → vendor}/assets/javascripts/mercury/panel.js.coffee +0 -0
  116. data/{app → vendor}/assets/javascripts/mercury/region.js.coffee +0 -0
  117. data/{app → vendor}/assets/javascripts/mercury/regions/editable.js.coffee +58 -12
  118. data/{app → vendor}/assets/javascripts/mercury/regions/markupable.js.coffee +26 -24
  119. data/{app → vendor}/assets/javascripts/mercury/regions/snippetable.js.coffee +0 -0
  120. data/{app → vendor}/assets/javascripts/mercury/select.js.coffee +3 -0
  121. data/{app → vendor}/assets/javascripts/mercury/snippet.js.coffee +9 -5
  122. data/{app → vendor}/assets/javascripts/mercury/snippet_toolbar.js.coffee +0 -0
  123. data/vendor/assets/javascripts/mercury/statusbar.js.coffee +51 -0
  124. data/{app → vendor}/assets/javascripts/mercury/table_editor.js.coffee +9 -8
  125. data/{app → vendor}/assets/javascripts/mercury/toolbar.button.js.coffee +0 -0
  126. data/{app → vendor}/assets/javascripts/mercury/toolbar.button_group.js.coffee +0 -0
  127. data/{app → vendor}/assets/javascripts/mercury/toolbar.expander.js.coffee +0 -0
  128. data/{app → vendor}/assets/javascripts/mercury/toolbar.js.coffee +14 -1
  129. data/{app → vendor}/assets/javascripts/mercury/tooltip.js.coffee +3 -0
  130. data/{app → vendor}/assets/javascripts/mercury/uploader.js.coffee +22 -13
  131. data/vendor/assets/javascripts/{jquery-1.6.js → mercury_dependencies/jquery-1.6.js} +0 -0
  132. data/vendor/assets/javascripts/{jquery-ui-1.8.13.custom.js → mercury_dependencies/jquery-ui-1.8.13.custom.js} +0 -0
  133. data/vendor/assets/javascripts/{jquery.additions.js → mercury_dependencies/jquery.additions.js} +0 -0
  134. data/vendor/assets/javascripts/{liquidmetal.js → mercury_dependencies/liquidmetal.js} +0 -0
  135. data/vendor/assets/javascripts/{showdown.js → mercury_dependencies/showdown.js} +0 -0
  136. data/vendor/assets/javascripts/mercury_loader.js +191 -0
  137. data/{app → vendor}/assets/stylesheets/mercury.css +3 -6
  138. data/vendor/assets/stylesheets/mercury/dialog.css +199 -0
  139. data/vendor/assets/stylesheets/mercury/lightview.css +92 -0
  140. data/vendor/assets/stylesheets/mercury/mercury.css +134 -0
  141. data/vendor/assets/stylesheets/mercury/modal.css +191 -0
  142. data/{app/assets/stylesheets/mercury/statusbar.scss → vendor/assets/stylesheets/mercury/statusbar.css} +9 -1
  143. data/vendor/assets/stylesheets/mercury/toolbar.css +329 -0
  144. data/{app/assets/stylesheets/mercury/tooltip.scss → vendor/assets/stylesheets/mercury/tooltip.css} +7 -7
  145. data/vendor/assets/stylesheets/mercury/uploader.css +111 -0
  146. metadata +230 -225
  147. data/app/assets/images/mercury/toolbar/markupable/buttons.png +0 -0
  148. data/app/assets/images/mercury/toolbar/primary/todospanel.png +0 -0
  149. data/app/assets/javascripts/mercury.js +0 -30
  150. data/app/assets/javascripts/mercury/dialog.js.coffee +0 -75
  151. data/app/assets/javascripts/mercury/mercury.js.coffee +0 -286
  152. data/app/assets/javascripts/mercury/statusbar.js.coffee +0 -29
  153. data/app/assets/javascripts/mercury_loader.js +0 -98
  154. data/app/assets/stylesheets/mercury/dialog.scss +0 -179
  155. data/app/assets/stylesheets/mercury/mercury.scss +0 -127
  156. data/app/assets/stylesheets/mercury/modal.scss +0 -194
  157. data/app/assets/stylesheets/mercury/toolbar.scss +0 -415
  158. data/app/assets/stylesheets/mercury/uploader.scss +0 -109
  159. data/app/views/layouts/mercury.html.haml +0 -8
  160. data/app/views/mercury/modals/character.html.haml +0 -252
  161. data/app/views/mercury/modals/htmleditor.html.haml +0 -8
  162. data/app/views/mercury/modals/link.html.haml +0 -31
  163. data/app/views/mercury/modals/media.html.haml +0 -33
  164. data/app/views/mercury/modals/sanitizer.html.haml +0 -4
  165. data/app/views/mercury/modals/table.html.haml +0 -49
  166. data/app/views/mercury/palettes/backcolor.html.haml +0 -79
  167. data/app/views/mercury/palettes/forecolor.html.haml +0 -79
  168. data/app/views/mercury/panels/history.html.haml +0 -0
  169. data/app/views/mercury/panels/notes.html.haml +0 -0
  170. data/app/views/mercury/panels/snippets.html.haml +0 -10
  171. data/app/views/mercury/selects/formatblock.html.haml +0 -10
  172. data/app/views/mercury/selects/style.html.haml +0 -4
  173. data/app/views/mercury/snippets/example.html.haml +0 -2
  174. data/app/views/mercury/snippets/example_options.html.haml +0 -16
  175. data/log/.gitkeep +0 -0
  176. data/spec/javascripts/mercury/regions/_markupable_.js.coffee +0 -0
@@ -55,6 +55,9 @@
55
55
  @element.find('#link_popup_height').val(href.match(/height=(\d+),/)[1])
56
56
  @element.find('#popup_options').show()
57
57
 
58
+ # get the text content
59
+ @element.find('#link_text').val(selection.textContent()) if selection.textContent
60
+
58
61
  # build the link on form submission
59
62
  @element.find('form').submit (event) =>
60
63
  event.preventDefault()
@@ -49,7 +49,11 @@
49
49
  Mercury.trigger('action', {action: 'insertImage', value: attrs})
50
50
 
51
51
  when 'youtube_url'
52
- code = @element.find('#media_youtube_url').val().replace('http://youtu.be/', '')
52
+ url = @element.find('#media_youtube_url').val()
53
+ unless /^http:\/\/youtu.be\//.test(url)
54
+ alert('Error: The provided youtube share url was invalid.')
55
+ return
56
+ code = url.replace('http://youtu.be/', '')
53
57
  value = jQuery('<iframe>', {
54
58
  width: @element.find('#media_youtube_width').val() || 560,
55
59
  height: @element.find('#media_youtube_height').val() || 349,
@@ -60,7 +64,11 @@
60
64
  Mercury.trigger('action', {action: 'insertHTML', value: value})
61
65
 
62
66
  when 'vimeo_url'
63
- code = @element.find('#media_vimeo_url').val().replace('http://vimeo.com/', '')
67
+ url = @element.find('#media_vimeo_url').val()
68
+ unless /^http:\/\/vimeo.com\//.test(url)
69
+ alert('Error: The provided vimeo url was invalid.')
70
+ return
71
+ code = url.replace('http://vimeo.com/', '')
64
72
  value = jQuery('<iframe>', {
65
73
  width: @element.find('#media_vimeo_width').val() || 400,
66
74
  height: @element.find('#media_vimeo_height').val() || 225,
@@ -7,12 +7,12 @@
7
7
  table = cell.closest('table')
8
8
  table.find('.selected').removeClass('selected')
9
9
  cell.addClass('selected')
10
- Mercury.tableEditor(table, cell)
10
+ Mercury.tableEditor(table, cell, '&nbsp;')
11
11
 
12
12
  # select the first td
13
13
  firstCell = table.find('td, th').first()
14
14
  firstCell.addClass('selected')
15
- Mercury.tableEditor(table, firstCell)
15
+ Mercury.tableEditor(table, firstCell, '&nbsp;')
16
16
 
17
17
  # make the buttons work
18
18
  @element.find('input.action').click (event) =>
@@ -2,15 +2,18 @@ class @Mercury.PageEditor
2
2
 
3
3
  # options
4
4
  # saveStyle: 'form', or 'json' (defaults to json)
5
- # ignoredLinks: an array containing classes for links to ignore (eg. lightbox or accordian controls)
5
+ # visible: boolean, if the interface should start visible or not (defaults to true)
6
6
  constructor: (@saveUrl = null, @options = {}) ->
7
7
  throw "Mercury.PageEditor is unsupported in this client. Supported browsers are chrome 10+, firefix 4+, and safari 5+." unless Mercury.supported
8
8
  throw "Mercury.PageEditor can only be instantiated once." if window.mercuryInstance
9
9
 
10
+ @options.visible = true unless @options.visible == false
11
+ @visible = @options.visible
12
+
10
13
  window.mercuryInstance = @
11
14
  @regions = []
12
15
  @initializeInterface()
13
- Mercury.csrfToken = token if token = jQuery('meta[name="csrf-token"]').attr('content')
16
+ Mercury.csrfToken = token if token = jQuery(Mercury.config.csrfSelector).attr('content')
14
17
 
15
18
 
16
19
  initializeInterface: ->
@@ -35,12 +38,16 @@ class @Mercury.PageEditor
35
38
  # jquery: make jQuery evaluate scripts within the context of the iframe window -- note that this means that we
36
39
  # can't use eval in mercury (eg. script tags in ajax responses) because it will eval in the wrong context (you can
37
40
  # use top.Mercury though, if you keep it in mind)
41
+ # todo: look into `context` options for ajax as an alternative
38
42
  iframeWindow = @iframe.get(0).contentWindow
39
43
  jQuery.globalEval = (data) -> (iframeWindow.execScript || (data) -> iframeWindow["eval"].call(iframeWindow, data))(data) if (data && /\S/.test(data))
44
+ iframeWindow.Mercury = Mercury
40
45
 
41
46
  @bindEvents()
47
+ @resize()
42
48
  @initializeRegions()
43
49
  @finalizeInterface()
50
+ Mercury.trigger('ready')
44
51
 
45
52
  @iframe.css({visibility: 'visible'})
46
53
  catch error
@@ -49,6 +56,7 @@ class @Mercury.PageEditor
49
56
 
50
57
  initializeRegions: ->
51
58
  @buildRegion(jQuery(region)) for region in jQuery('.mercury-region', @document)
59
+ return unless @options.visible
52
60
  for region in @regions
53
61
  if region.focus
54
62
  region.focus()
@@ -67,8 +75,8 @@ class @Mercury.PageEditor
67
75
  finalizeInterface: ->
68
76
  @snippetToolbar = new Mercury.SnippetToolbar(@document)
69
77
 
70
- @hijackLinks()
71
- @resize()
78
+ @hijackLinksAndForms()
79
+ Mercury.trigger('mode', {mode: 'preview'}) unless @options.visible
72
80
 
73
81
 
74
82
  bindEvents: ->
@@ -76,23 +84,39 @@ class @Mercury.PageEditor
76
84
  Mercury.bind 'focus:frame', => @iframe.focus()
77
85
  Mercury.bind 'focus:window', => setTimeout((=> @focusableElement.focus()), 10)
78
86
 
87
+ Mercury.bind 'toggle:interface', => @toggleInterface()
88
+
79
89
  Mercury.bind 'action', (event, options) =>
80
90
  @save() if options.action == 'save'
81
91
 
82
92
  @document.mousedown (event) ->
83
93
  Mercury.trigger('hide:dialogs')
84
- Mercury.trigger('unfocus:regions') unless jQuery(event.target).closest('.mercury-region').get(0) == Mercury.region.element.get(0)
94
+ if Mercury.region
95
+ Mercury.trigger('unfocus:regions') unless jQuery(event.target).closest('.mercury-region').get(0) == Mercury.region.element.get(0)
85
96
 
86
97
  jQuery(window).resize => @resize()
87
98
  window.onbeforeunload = @beforeUnload
88
99
 
89
100
 
101
+ toggleInterface: ->
102
+ if @visible
103
+ @visible = false
104
+ @toolbar.hide()
105
+ @statusbar.hide()
106
+ else
107
+ @visible = true
108
+ @toolbar.show()
109
+ @statusbar.show()
110
+ Mercury.trigger('mode', {mode: 'preview'})
111
+ @resize()
112
+
113
+
90
114
  resize: ->
91
115
  width = jQuery(window).width()
92
116
  height = @statusbar.top()
93
117
  toolbarHeight = @toolbar.height()
94
118
 
95
- Mercury.displayRect = {top: toolbarHeight, left: 0, width: width, height: height - toolbarHeight}
119
+ Mercury.displayRect = {top: toolbarHeight, left: 0, width: width, height: height - toolbarHeight, fullHeight: height}
96
120
 
97
121
  @iframe.css {
98
122
  top: toolbarHeight
@@ -107,15 +131,15 @@ class @Mercury.PageEditor
107
131
  (url ? window.location.href).replace(/([http|https]:\/\/.[^\/]*)\/editor\/?(.*)/i, "$1/$2")
108
132
 
109
133
 
110
- hijackLinks: ->
111
- for link in jQuery('a', @document)
134
+ hijackLinksAndForms: ->
135
+ for element in jQuery('a, form', @document)
112
136
  ignored = false
113
- for classname in @options.ignoredLinks || []
114
- if jQuery(link).hasClass(classname)
137
+ for classname in Mercury.config.nonHijackableClasses || []
138
+ if jQuery(element).hasClass(classname)
115
139
  ignored = true
116
140
  continue
117
- if !ignored && (link.target == '' || link.target == '_self') && !jQuery(link).closest('.mercury-region').length
118
- jQuery(link).attr('target', '_top')
141
+ if !ignored && (element.target == '' || element.target == '_self') && !jQuery(element).closest('.mercury-region').length
142
+ jQuery(element).attr('target', '_top')
119
143
 
120
144
 
121
145
  beforeUnload: ->
@@ -125,12 +149,13 @@ class @Mercury.PageEditor
125
149
 
126
150
 
127
151
  save: ->
128
- url = @saveUrl ? @iframeSrc()
152
+ url = @saveUrl ? Mercury.saveURL ? @iframeSrc()
129
153
  data = @serialize()
130
154
  Mercury.log('saving', data)
131
155
  data = jQuery.toJSON(data) unless @options.saveStyle == 'form'
132
156
  jQuery.ajax url, {
133
157
  type: 'POST'
158
+ headers: @saveHeaders()
134
159
  data: {content: data}
135
160
  success: =>
136
161
  Mercury.changes = false
@@ -139,6 +164,12 @@ class @Mercury.PageEditor
139
164
  }
140
165
 
141
166
 
167
+ saveHeaders: ->
168
+ headers = {}
169
+ headers[Mercury.config.csrfHeader] = Mercury.csrfToken
170
+ return headers
171
+
172
+
142
173
  serialize: ->
143
174
  serialized = {}
144
175
  serialized[region.name] = region.serialize() for region in @regions
@@ -18,7 +18,7 @@ class @Mercury.Regions.Editable extends Mercury.Region
18
18
  @specialContainer = jQuery.browser.mozilla && @element.get(0).tagName != 'DIV'
19
19
 
20
20
  # make it editable
21
- # gecko: in this makes double clicking in textareas fail: https://bugzilla.mozilla.org/show_bug.cgi?id=490367
21
+ # mozilla: this makes double clicking in textareas fail: https://bugzilla.mozilla.org/show_bug.cgi?id=490367
22
22
  @element.get(0).contentEditable = true
23
23
 
24
24
  # make all snippets not editable, and set their versions to 1
@@ -46,7 +46,7 @@ class @Mercury.Regions.Editable extends Mercury.Region
46
46
  if currentElement.length
47
47
  # setup the table editor if we're inside a table
48
48
  table = currentElement.closest('table', @element)
49
- Mercury.tableEditor(table, currentElement.closest('tr, td')) if table.length
49
+ Mercury.tableEditor(table, currentElement.closest('tr, td'), '&nbsp;') if table.length
50
50
  # display a tooltip if we're in an anchor
51
51
  anchor = currentElement.closest('a', @element)
52
52
  if anchor.length && anchor.attr('href')
@@ -56,12 +56,12 @@ class @Mercury.Regions.Editable extends Mercury.Region
56
56
 
57
57
  @element.bind 'dragenter', (event) =>
58
58
  return if @previewing
59
- event.preventDefault() if event.shiftKey
59
+ event.preventDefault() unless Mercury.snippet
60
60
  event.originalEvent.dataTransfer.dropEffect = 'copy'
61
61
 
62
62
  @element.bind 'dragover', (event) =>
63
63
  return if @previewing
64
- event.preventDefault() if event.shiftKey
64
+ event.preventDefault() unless Mercury.snippet
65
65
  event.originalEvent.dataTransfer.dropEffect = 'copy'
66
66
  if jQuery.browser.webkit
67
67
  clearTimeout(@dropTimeout)
@@ -100,9 +100,12 @@ class @Mercury.Regions.Editable extends Mercury.Region
100
100
  return if @previewing
101
101
  return unless Mercury.region == @
102
102
  Mercury.changes = true
103
+ if @specialContainer
104
+ event.preventDefault()
105
+ return
103
106
  content = @content()
104
- event.preventDefault() if @specialContainer
105
- setTimeout((=> @handlePaste(content)), 1)
107
+ clearTimeout(@handlePasteTimeout)
108
+ @handlePasteTimeout = setTimeout((=> @handlePaste(content)), 100)
106
109
 
107
110
  @element.focus =>
108
111
  return if @previewing
@@ -183,8 +186,11 @@ class @Mercury.Regions.Editable extends Mercury.Region
183
186
 
184
187
 
185
188
  focus: ->
186
- @element.focus()
187
- setTimeout((=> @selection().forceSelection(@element.get(0))), 1)
189
+ if Mercury.region != @
190
+ @element.focus()
191
+ selection = @selection().selection.collapseToStart()
192
+
193
+ Mercury.trigger('region:focused', {region: @})
188
194
  Mercury.trigger('region:update', {region: @})
189
195
 
190
196
 
@@ -268,6 +274,7 @@ class @Mercury.Regions.Editable extends Mercury.Region
268
274
  @document.execCommand(action, false, options.value)
269
275
  catch error
270
276
  # mozilla: indenting when there's no br tag handles strangely
277
+ # todo: mozilla: trying to justify the first line of any contentEditable fails
271
278
  @element.prev().remove() if action == 'indent' && @element.prev() != sibling
272
279
 
273
280
 
@@ -348,6 +355,44 @@ class @Mercury.Regions.Editable extends Mercury.Region
348
355
 
349
356
  # Custom actions (eg. things that execCommand doesn't do, or doesn't do well)
350
357
  @actions: {
358
+ # bold: (selection) ->
359
+ # unless selection.collapsed
360
+ # @document.execCommand('bold', false, null)
361
+ # else
362
+ ## selection.selectWordByCursor()
363
+ ## @document.execCommand('bold', false, null)
364
+ #
365
+ # selection.placeMarker()
366
+ # node = @element.find('.mercury-marker').get(0)
367
+ # prev = node.previousSibling
368
+ # selection.removeMarker()
369
+ # next = prev.nextSibling
370
+ # console.debug(prev, next)
371
+ ## if prev.textContent[prev.textContent.length - 1] != ' ' &&
372
+
373
+
374
+ #[some]| content
375
+ # textContent = some, wholeText = some
376
+ # textContent = '', wholeText = some
377
+ #content |[some]
378
+ #content [s|ome]
379
+ #co|ntent [some]
380
+ #|content [some]
381
+
382
+
383
+ #
384
+ # console.debug(commonAncestor)
385
+ # beforeText = commonAncestor.textContent
386
+ # afterText = commonAncestor.wholeText.substring(commonAncestor.wholeText.lastIndexOf(beforeText) + beforeText.length, commonAncestor.wholeText.length)
387
+ # if beforeText && afterText && (beforeChar = beforeText[beforeText.length - 1]) != ' ' && (afterChar = afterText[0]) != ' '
388
+ # console.debug('bolding word', beforeChar, afterChar)
389
+ # else
390
+ # @document.execCommand('bold', false, null)
391
+ #
392
+ ## selection.selectWord()
393
+ ## commonAncestor.wholeText, commonAncestor.textContent
394
+ # else
395
+
351
396
  insertRowBefore: -> Mercury.tableEditor.addRow('before')
352
397
 
353
398
  insertRowAfter: -> Mercury.tableEditor.addRow('after')
@@ -385,11 +430,11 @@ class @Mercury.Regions.Editable extends Mercury.Region
385
430
  insertImage: (selection, options) -> @execCommand('insertHTML', {value: jQuery('<img/>', options.value)})
386
431
 
387
432
  insertLink: (selection, options) ->
388
- anchor = jQuery("<#{options.value.tagName}>").attr(options.value.attrs).html(options.value.content)
433
+ anchor = jQuery("<#{options.value.tagName}>", @document).attr(options.value.attrs).html(options.value.content)
389
434
  selection.insertNode(anchor)
390
435
 
391
436
  replaceLink: (selection, options) ->
392
- anchor = jQuery("<#{options.value.tagName}>").attr(options.value.attrs).html(options.value.content)
437
+ anchor = jQuery("<#{options.value.tagName}>", @document).attr(options.value.attrs).html(options.value.content)
393
438
  selection.selectNode(options.node)
394
439
  html = jQuery('<div>').html(selection.content()).find('a').html()
395
440
  selection.replace(jQuery(anchor, selection.context).html(html))
@@ -419,6 +464,7 @@ class Mercury.Regions.Editable.Selection
419
464
  @range = @selection.getRangeAt(0)
420
465
  @fragment = @range.cloneContents()
421
466
  @clone = @range.cloneRange()
467
+ @collapsed = @selection.isCollapsed
422
468
 
423
469
 
424
470
  commonAncestor: (onlyTag = false) ->
@@ -456,12 +502,12 @@ class Mercury.Regions.Editable.Selection
456
502
  # https://github.com/lautis/uglifier/issues/11
457
503
  if @range
458
504
  if @commonAncestor(true).closest('.mercury-snippet').length
459
- lastChild = @context.createTextNode(' ') #\00
505
+ lastChild = @context.createTextNode('\00') #\00
460
506
  element.appendChild(lastChild)
461
507
  else
462
508
  if element.lastChild && element.lastChild.nodeType == 3 && element.lastChild.textContent.replace(/^[\s+|\n+]|[\s+|\n+]$/, '') == ''
463
509
  lastChild = element.lastChild
464
- element.lastChild.textContent = ' ' #\00
510
+ element.lastChild.textContent = '\00' #\00
465
511
  else
466
512
  lastChild = @context.createTextNode(' ') #\00
467
513
  element.appendChild(lastChild)
@@ -13,20 +13,20 @@ class @Mercury.Regions.Markupable extends Mercury.Region
13
13
 
14
14
 
15
15
  build: ->
16
- width = @element.width()
17
- width = '100%' unless width
16
+ width = '100%'
18
17
  height = @element.height()
19
18
 
20
- value = @element.html().replace(/^\s+|\s+$/g, '')
19
+ value = @element.html().replace(/^\s+|\s+$/g, '').replace('&gt;', '>')
21
20
  @textarea = jQuery('<textarea>', @document).val(value)
22
21
  @textarea.attr('class', @element.attr('class')).addClass('mercury-textarea')
23
22
  @textarea.css({border: 0, background: 'transparent', display: 'block', width: width, height: height, fontFamily: '"Courier New", Courier, monospace', fontSize: '14px'})
24
- @element.after(@textarea)
25
- @element.hide()
26
- @resize()
23
+ @element.empty().append(@textarea)
24
+ @element.removeClass('mercury-region')
27
25
 
28
- @previewElement = @element
26
+ @previewElement = jQuery('<div>', @document)
27
+ @element.append(@previewElement)
29
28
  @element = @textarea
29
+ @resize()
30
30
 
31
31
 
32
32
  focus: ->
@@ -49,10 +49,10 @@ class @Mercury.Regions.Markupable extends Mercury.Region
49
49
 
50
50
  Mercury.bind 'unfocus:regions', (event) =>
51
51
  return if @previewing
52
- if Mercury.region == @
53
- @element.blur()
54
- @element.removeClass('focus')
55
- Mercury.trigger('region:blurred', {region: @})
52
+ return unless Mercury.region == @
53
+ @element.blur()
54
+ @element.removeClass('focus')
55
+ Mercury.trigger('region:blurred', {region: @})
56
56
 
57
57
  @element.bind 'dragenter', (event) =>
58
58
  return if @previewing
@@ -90,6 +90,12 @@ class @Mercury.Regions.Markupable extends Mercury.Region
90
90
  Mercury.changes = true
91
91
  @resize()
92
92
  switch event.keyCode
93
+ when 90 # undo / redo
94
+ return unless event.metaKey
95
+ event.preventDefault()
96
+ if event.shiftKey then @execCommand('redo') else @execCommand('undo')
97
+ return
98
+
93
99
  when 13 # enter or return
94
100
  selection = @selection()
95
101
  text = @element.val()
@@ -107,11 +113,9 @@ class @Mercury.Regions.Markupable extends Mercury.Region
107
113
  selection.replace("\n#{number += 1}. ", false, true)
108
114
  event.preventDefault()
109
115
 
110
- when 90 # undo / redo
111
- return unless event.metaKey
116
+ when 9 # tab
112
117
  event.preventDefault()
113
- if event.shiftKey then @execCommand('redo') else @execCommand('undo')
114
- return
118
+ @execCommand('insertHTML', {value: ' '})
115
119
 
116
120
  if event.metaKey
117
121
  switch event.keyCode
@@ -144,8 +148,6 @@ class @Mercury.Regions.Markupable extends Mercury.Region
144
148
 
145
149
  content: (value = null, filterSnippets = true) ->
146
150
  if value != null
147
- if $.type(value) == 'string'
148
- @element.val(value)
149
151
  if jQuery.type(value) == 'string'
150
152
  @element.val(value)
151
153
  else
@@ -155,6 +157,10 @@ class @Mercury.Regions.Markupable extends Mercury.Region
155
157
  return @element.val()
156
158
 
157
159
 
160
+ contentAndSelection: ->
161
+ return {html: @content(null, false), selection: @selection().serialize()}
162
+
163
+
158
164
  togglePreview: ->
159
165
  if @previewing
160
166
  @previewElement.hide()
@@ -174,10 +180,6 @@ class @Mercury.Regions.Markupable extends Mercury.Region
174
180
  @resize()
175
181
 
176
182
 
177
- htmlAndSelection: ->
178
- return {html: @content(null, false), selection: @selection().serialize()}
179
-
180
-
181
183
  pushHistory: (keyCode) ->
182
184
  # when pressing return, delete or backspace it should push to the history
183
185
  # all other times it should store if there's a 1 second pause
@@ -190,13 +192,13 @@ class @Mercury.Regions.Markupable extends Mercury.Region
190
192
 
191
193
  # if the key code was return, delete, or backspace store now -- unless it was the same as last time
192
194
  if knownKeyCode >= 0 && knownKeyCode != @lastKnownKeyCode # || !keyCode
193
- @history.push(@htmlAndSelection())
195
+ @history.push(@contentAndSelection())
194
196
  else if keyCode
195
197
  # set a timeout for pushing to the history
196
- @historyTimeout = setTimeout((=> @history.push(@htmlAndSelection())), waitTime * 1000)
198
+ @historyTimeout = setTimeout((=> @history.push(@contentAndSelection())), waitTime * 1000)
197
199
  else
198
200
  # push to the history immediately
199
- @history.push(@htmlAndSelection())
201
+ @history.push(@contentAndSelection())
200
202
 
201
203
  @lastKnownKeyCode = knownKeyCode
202
204