diff --git a/internal_packages/composer/lib/composer-view.cjsx b/internal_packages/composer/lib/composer-view.cjsx
index 506d97afb..4c31d5e20 100644
--- a/internal_packages/composer/lib/composer-view.cjsx
+++ b/internal_packages/composer/lib/composer-view.cjsx
@@ -246,7 +246,7 @@ class ComposerView extends React.Component
(@state.files ? []).map (file) =>
+ key={file.id} />
}
diff --git a/internal_packages/message-list/lib/email-frame.cjsx b/internal_packages/message-list/lib/email-frame.cjsx
index d51bedee6..f814ceb67 100644
--- a/internal_packages/message-list/lib/email-frame.cjsx
+++ b/internal_packages/message-list/lib/email-frame.cjsx
@@ -64,6 +64,6 @@ class EmailFrame extends React.Component
email
else
Utils.stripQuotedText(email)
-
+
module.exports = EmailFrame
diff --git a/src/components/evented-iframe.cjsx b/src/components/evented-iframe.cjsx
index b2a175118..5f6c45cf3 100644
--- a/src/components/evented-iframe.cjsx
+++ b/src/components/evented-iframe.cjsx
@@ -47,6 +47,7 @@ class EventedIFrame extends React.Component
doc.removeEventListener('mousedown', @_onIFrameMouseEvent)
doc.removeEventListener('mousemove', @_onIFrameMouseEvent)
doc.removeEventListener('mouseup', @_onIFrameMouseEvent)
+ doc.removeEventListener("contextmenu", @_onIFrameContextualMenu)
if node.contentWindow
node.contentWindow.removeEventListener('focus', @_onIFrameFocus)
node.contentWindow.removeEventListener('blur', @_onIFrameBlur)
@@ -60,10 +61,18 @@ class EventedIFrame extends React.Component
doc.addEventListener("mousedown", @_onIFrameMouseEvent)
doc.addEventListener("mousemove", @_onIFrameMouseEvent)
doc.addEventListener("mouseup", @_onIFrameMouseEvent)
+ doc.addEventListener("contextmenu", @_onIFrameContextualMenu)
if node.contentWindow
node.contentWindow.addEventListener("focus", @_onIFrameFocus)
node.contentWindow.addEventListener("blur", @_onIFrameBlur)
+ _getContainingTarget: (event, options) =>
+ target = event.target
+ while target? and (target isnt document) and (target isnt window)
+ return target if target.getAttribute(options.with)?
+ target = target.parentElement
+ return null
+
_onIFrameBlur: (event) =>
node = React.findDOMNode(@)
node.contentWindow.getSelection().empty()
@@ -78,15 +87,8 @@ class EventedIFrame extends React.Component
_onIFrameClick: (e) =>
e.preventDefault()
e.stopPropagation()
- target = e.target
-
- # This lets us detect when we click an element inside of an tag
- while target? and (target isnt document) and (target isnt window)
- if target.getAttribute('href')?
- atom.windowEventHandler.openLink target: target
- target = null
- else
- target = target.parentElement
+ target = @_getContainingTarget(e, {with: 'href'})
+ atom.windowEventHandler.openLink(target: target) if target
_onIFrameMouseEvent: (event) =>
node = React.findDOMNode(@)
@@ -102,5 +104,90 @@ class EventedIFrame extends React.Component
return if event.metaKey or event.altKey or event.ctrlKey
React.findDOMNode(@).dispatchEvent(new KeyboardEvent(event.type, event))
+ _onIFrameContextualMenu: (event) =>
+ # Build a standard-looking contextual menu with options like "Copy Link",
+ # "Copy Image" and "Search Google for 'Bla'"
+ event.preventDefault()
+
+ remote = require('remote')
+ clipboard = require('clipboard')
+ Menu = remote.require('menu')
+ MenuItem = remote.require('menu-item')
+ NativeImage = require('native-image')
+ shell = require('shell')
+ path = require('path')
+ fs = require('fs')
+ menu = new Menu()
+
+ # Menu actions for links
+ linkTarget = @_getContainingTarget(event, {with: 'href'})
+ if linkTarget
+ href = linkTarget.getAttribute('href')
+ menu.append(new MenuItem({ label: "Open Link", click:( -> atom.windowEventHandler.openLink({href}) )}))
+ menu.append(new MenuItem({ label: "Copy Link", click:( -> clipboard.writeText(href) )}))
+ menu.append(new MenuItem({ type: 'separator' }))
+
+ # Menu actions for images
+ imageTarget = @_getContainingTarget(event, {with: 'src'})
+ if imageTarget
+ src = imageTarget.getAttribute('src')
+ srcFilename = path.basename(src)
+ menu.append(new MenuItem({
+ label: "Save Image...",
+ click: ->
+ atom.showSaveDialog srcFilename, (path) ->
+ return unless path
+ oReq = new XMLHttpRequest()
+ oReq.open("GET", src, true)
+ oReq.responseType = "arraybuffer"
+ oReq.onload = ->
+ buffer = new Buffer(new Uint8Array(oReq.response))
+ fs.writeFile path, buffer, (err) ->
+ shell.showItemInFolder(path)
+ oReq.send()
+ }))
+ menu.append(new MenuItem({
+ label: "Copy Image",
+ click: ->
+ img = new Image()
+ img.addEventListener("load", ->
+ canvas = document.createElement("canvas")
+ canvas.width = img.width
+ canvas.height = img.height
+ canvas.getContext("2d").drawImage(imageTarget, 0, 0)
+ imageDataURL = canvas.toDataURL("image/png")
+ img = NativeImage.createFromDataUrl(imageDataURL)
+ clipboard.writeImage(img)
+ , false)
+ img.src = src
+ }))
+ menu.append(new MenuItem({ type: 'separator' }))
+
+ # Menu actions for text
+ text = ""
+ selection = React.findDOMNode(@).contentDocument.getSelection()
+ if selection.rangeCount > 0
+ range = selection.getRangeAt(0)
+ text = range.toString()
+ if not text or text.length is 0
+ text = (linkTarget ? event.target).innerText
+ text = text.trim()
+
+ if text.length > 0
+ if text.length > 45
+ textPreview = text.substr(0, 42) + "..."
+ else
+ textPreview = text
+ menu.append(new MenuItem({ label: "Copy", click:( -> clipboard.writeText(text) )}))
+ menu.append(new MenuItem({ label: "Search Google for '#{textPreview}'", click:( -> shell.openExternal("https://www.google.com/search?q=#{encodeURIComponent(text)}") )}))
+ if process.platform is 'darwin'
+ menu.append(new MenuItem({ label: "Look Up '#{textPreview}'", click:( -> atom.getCurrentWindow().showDefinitionForSelection() )}))
+
+
+ if process.platform is 'darwin'
+ menu.append(new MenuItem({ type: 'separator' }))
+ # Services menu appears here automatically
+
+ menu.popup(remote.getCurrentWindow())
module.exports = EventedIFrame
diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee
index 83200232d..28468ac5f 100644
--- a/src/window-event-handler.coffee
+++ b/src/window-event-handler.coffee
@@ -127,12 +127,13 @@ class WindowEventHandler
event.preventDefault()
event.stopPropagation()
- openLink: ({target, currentTarget}) ->
- location = target?.getAttribute('href') or currentTarget?.getAttribute('href')
- if location?
- schema = url.parse(location).protocol
+ openLink: ({href, target, currentTarget}) ->
+ if not href
+ href = target?.getAttribute('href') or currentTarget?.getAttribute('href')
+ if href?
+ schema = url.parse(href).protocol
if schema? and schema in ['http:', 'https:', 'mailto:', 'tel:']
- shell.openExternal(location)
+ shell.openExternal(href)
false
eachTabIndexedElement: (callback) ->