Skip to content

Commit

Permalink
Merge pull request #1704 from AlchemyCMS/use-post-message
Browse files Browse the repository at this point in the history
Use postMessage to send messages between preview and element windows
  • Loading branch information
tvdeyen authored Jan 6, 2020
2 parents 04e5a11 + 0a0329c commit 085773b
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 29 deletions.
36 changes: 22 additions & 14 deletions app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@ Alchemy.ElementEditors =
# Binds the custom SaveElement event
@element_area.on "SaveElement.Alchemy", '.element-editor', (e, data) =>
@onSaveElement(e, data)
# Listen to postMessage messages from the preview frame
window.addEventListener 'message', (e) =>
if e.origin == window.location.origin
@onMessage(e.data)
else
console.warn 'Unsafe message origin!', e.origin
true
return

# Selects and scrolls to element with given id in the preview window.
#
selectElementInPreview: (element_id) ->
previewElements = document
.getElementById('alchemy_preview_window')
.contentDocument
.querySelectorAll('[data-alchemy-element]')
previewElement = Array.from(previewElements).find (element) ->
element.getAttribute('data-alchemy-element') == element_id
if previewElement
event = new Event('SelectPreviewElement.Alchemy')
previewElement.dispatchEvent(event)
focusElementPreview: (element_id) ->
Alchemy.PreviewWindow.postMessage
message: 'Alchemy.focusElement'
element_id: element_id
return

# Selects element
Expand All @@ -60,6 +61,7 @@ Alchemy.ElementEditors =
# Used by the elements on click events in the preview frame.
focusElement: ($element) ->
element_id = $element.attr('id').replace(/\D/g, "")
Alchemy.ElementsWindow.show()
@selectTabForElement($element)
# If we have folded parents we need to unfold each of them
# and then finally scroll to or unfold ourself
Expand Down Expand Up @@ -173,25 +175,31 @@ Alchemy.ElementEditors =

# Event handlers

onMessage: (data) ->
if data.message == 'Alchemy.focusElementEditor'
$element = $("#element_#{data.element_id}")
Alchemy.ElementEditors.focusElement($element)
else
console.warn 'Unknown message received!', data

onClickBody: (e) ->
frameWindow = $('#alchemy_preview_window')[0].contentWindow
element = $(e.target).parents('.element-editor')[0]
$('#element_area .element-editor').not(element).removeClass('selected')
unless element
frameWindow.postMessage('blurAlchemyElements', window.location.origin)
Alchemy.PreviewWindow.postMessage(message: 'Alchemy.blurElements')
return

# Click event handler for element body.
#
# - Focuses the element
# - Triggers custom 'SelectPreviewElement.Alchemy' event on target element in preview frame.
# - Sends 'Alchemy.focusElement' message to preview frame.
#
onClickElement: (e) ->
$target = $(e.target)
$element = $target.closest(".element-editor")
element_id = $element.attr("id").replace(/\D/g, "")
@selectElement($element)
@selectElementInPreview(element_id)
@focusElementPreview(element_id)
return

# Double click event handler for element head.
Expand Down
42 changes: 27 additions & 15 deletions app/assets/javascripts/alchemy/alchemy.preview.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ Alchemy.initAlchemyPreviewMode = ->
"outline-offset": "4px"

init: ->
window.addEventListener "message", (message) =>
if message.data == "blurAlchemyElements"
@blurElements()
, false
@elements = document.querySelectorAll("[data-alchemy-element]")
window.addEventListener "message", (event) =>
if event.origin != window.location.origin
console.warn 'Unsafe message origin!', event.origin
return
switch event.data.message
when "Alchemy.blurElements" then @blurElements()
when "Alchemy.focusElement" then @focusElement(event.data)
else console.info("Received unknown message!", event.data)
return
@elements = Array.from document.querySelectorAll("[data-alchemy-element]")
@elements.forEach (element) =>
element.addEventListener 'mouseover', =>
unless element.classList.contains('selected')
Expand All @@ -35,10 +40,6 @@ Alchemy.initAlchemyPreviewMode = ->
unless element.classList.contains('selected')
Object.assign element.style, @getStyle('reset')
return
element.addEventListener 'SelectPreviewElement.Alchemy', =>
@selectElement(element)
return
, false
element.addEventListener 'click', (e) =>
e.stopPropagation()
e.preventDefault()
Expand Down Expand Up @@ -66,14 +67,25 @@ Alchemy.initAlchemyPreviewMode = ->
return
return

# Focus the element in the Alchemy preview window.
focusElement: (data) ->
element = @getElement(data.element_id)
if element
@selectElement(element)
else
console.warn('Could not focus element with id', data.element_id)

getElement: (element_id) ->
@elements.find (element) ->
element.dataset.alchemyElement == element_id.toString()

# Focus the element editor in the Alchemy element window.
focusElementEditor: (element) ->
alchemy_window = window.parent
target_id = element.getAttribute('data-alchemy-element')
$element_editor = alchemy_window.$("#element_#{target_id}")
elements_window = alchemy_window.Alchemy.ElementsWindow
$element_editor.trigger("FocusElementEditor.Alchemy", target_id)
elements_window.show() if elements_window.hidden
element_id = element.getAttribute('data-alchemy-element')
window.parent.postMessage
message: 'Alchemy.focusElementEditor'
element_id: element_id
, window.location.origin
return

getStyle: (state) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ Alchemy.PreviewWindow =
$iframe.attr 'src', $iframe.attr('src')
true

postMessage: (data) ->
frameWindow = @currentWindow[0].contentWindow
frameWindow.postMessage(data, window.location.origin)

_showSpinner: ->
@reload = $('#reload_preview_button')
@spinner = new Alchemy.Spinner('small')
Expand Down

0 comments on commit 085773b

Please sign in to comment.