fix(iframes): Contextual menus for email contents

Summary: Fixes T1191

Test Plan: No new tests to see here.

Reviewers: evan

Reviewed By: evan

Maniphest Tasks: T1191

Differential Revision: https://phab.nylas.com/D1603
This commit is contained in:
Ben Gotow 2015-06-05 16:47:30 -07:00
parent fb3f7fc410
commit 1bd8767a77
4 changed files with 104 additions and 16 deletions

View file

@ -246,7 +246,7 @@ class ComposerView extends React.Component
(@state.files ? []).map (file) =>
<InjectedComponent matching={role:"Attachment"}
exposedProps={file: file, removable: true, messageLocalId: @props.localId}
key={file.filename} />
key={file.id} />
}
<FileUploads localId={@props.localId} />
</div>

View file

@ -64,6 +64,6 @@ class EmailFrame extends React.Component
email
else
Utils.stripQuotedText(email)
module.exports = EmailFrame

View file

@ -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 <a> 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

View file

@ -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) ->