Mailspring/src/window-event-handler.coffee
Ben Gotow b00e5439e5 feat(paste): Paste accepts more HTML, paste and match style now available
Summary:
Related to #320, #494, #515, #553

Ignore newlines and returns in HTML, they can be inside tags

Allow all attributes so that paste from excel looks nice

Never let someone paste a `contenteditable` attribute

Update specs

Test Plan: Run new specs

Reviewers: juan, evan

Reviewed By: evan

Differential Revision: https://phab.nylas.com/D2309
2015-12-07 15:34:03 -08:00

264 lines
8.9 KiB
CoffeeScript

path = require 'path'
{$} = require './space-pen-extensions'
_ = require 'underscore'
{Disposable} = require 'event-kit'
{shell, ipcRenderer} = require 'electron'
{Subscriber} = require 'emissary'
fs = require 'fs-plus'
url = require 'url'
# Handles low-level events related to the window.
module.exports =
class WindowEventHandler
Subscriber.includeInto(this)
constructor: ->
@reloadRequested = false
_.defer =>
@showDevModeMessages()
@subscribe ipcRenderer, 'open-path', (event, pathToOpen) ->
unless NylasEnv.project?.getPaths().length
if fs.existsSync(pathToOpen) or fs.existsSync(path.dirname(pathToOpen))
NylasEnv.project?.setPaths([pathToOpen])
unless fs.isDirectorySync(pathToOpen)
NylasEnv.workspace?.open(pathToOpen, {})
@subscribe ipcRenderer, 'update-available', (event, detail) ->
NylasEnv.updateAvailable(detail)
@subscribe ipcRenderer, 'send-feedback', (detail) ->
Actions = require './flux/actions'
Actions.sendFeedback()
@subscribe ipcRenderer, 'browser-window-focus', ->
document.body.classList.remove('is-blurred')
@subscribe ipcRenderer, 'browser-window-blur', ->
document.body.classList.add('is-blurred')
@subscribe ipcRenderer, 'command', (event, command, args...) ->
activeElement = document.activeElement
# Use the workspace element view if body has focus
if activeElement is document.body and workspaceElement = document.getElementById("nylas-workspace")
activeElement = workspaceElement
NylasEnv.commands.dispatch(activeElement, command, args[0])
@subscribe $(window), 'beforeunload', =>
if NylasEnv.getCurrentWindow().isWebViewFocused() and not @reloadRequested
NylasEnv.hide()
@reloadRequested = false
NylasEnv.storeWindowDimensions()
NylasEnv.saveStateAndUnloadWindow()
true
@subscribe $(window), 'unload', =>
NylasEnv.windowEventHandler?.unsubscribe()
@subscribeToCommand $(window), 'window:toggle-full-screen', ->
NylasEnv.toggleFullScreen()
@subscribeToCommand $(window), 'window:close', ->
NylasEnv.close()
@subscribeToCommand $(window), 'window:reload', =>
@reloadRequested = true
NylasEnv.reload()
@subscribeToCommand $(window), 'window:toggle-dev-tools', ->
NylasEnv.toggleDevTools()
@subscribeToCommand $(window), 'window:open-errorlogger-logs', ->
NylasEnv.errorLogger.openLogs()
@subscribeToCommand $(window), 'window:toggle-component-regions', ->
ComponentRegistry = require './component-registry'
ComponentRegistry.toggleComponentRegions()
@subscribeToCommand $(window), 'window:toggle-react-remote', ->
ReactRemote = require './react-remote/react-remote-parent'
ReactRemote.toggleContainerVisible()
@subscribeToCommand $(document), 'core:focus-next', @focusNext
@subscribeToCommand $(document), 'core:focus-previous', @focusPrevious
document.addEventListener 'keydown', @onKeydown
# "Pinch to zoom" on the Mac gets translated by the system into a
# "scroll with ctrl key down". To prevent the page from zooming in,
# prevent default when the ctrlKey is detected.
document.addEventListener 'mousewheel', ->
if event.ctrlKey
event.preventDefault()
document.addEventListener 'drop', @onDrop
@subscribe new Disposable =>
document.removeEventListener('drop', @onDrop)
document.addEventListener 'dragover', @onDragOver
@subscribe new Disposable =>
document.removeEventListener('dragover', @onDragOver)
@subscribe $(document), 'click', 'a', @openLink
@subscribe $(document), 'contextmenu', 'input', @openContextualMenuForInput
# Prevent form submits from changing the current window's URL
@subscribe $(document), 'submit', 'form', (e) -> e.preventDefault()
@handleNativeKeybindings()
# Wire commands that should be handled by Chromium for elements with the
# `.override-key-bindings` class.
handleNativeKeybindings: ->
menu = null
webContents = NylasEnv.getCurrentWindow().webContents
bindCommandToAction = (command, action) =>
@subscribe $(document), command, (event) ->
unless event.target.webkitMatchesSelector('.override-key-bindings')
webContents[action]()
true
bindCommandToAction('core:copy', 'copy')
bindCommandToAction('core:cut', 'cut')
bindCommandToAction('core:paste', 'paste')
bindCommandToAction('core:paste-and-match-style', 'pasteAndMatchStyle')
bindCommandToAction('core:undo', 'undo')
bindCommandToAction('core:redo', 'redo')
bindCommandToAction('core:select-all', 'selectAll')
onKeydown: (event) ->
NylasEnv.keymaps.handleKeyboardEvent(event)
# Important: even though we don't do anything here, we need to catch the
# drop event to prevent the browser from navigating the to the "url" of the
# file and completely leaving the app.
onDrop: (event) ->
event.preventDefault()
event.stopPropagation()
onDragOver: (event) ->
event.preventDefault()
event.stopPropagation()
openLink: ({href, target, currentTarget}) ->
if not href
href = target?.getAttribute('href') or currentTarget?.getAttribute('href')
return unless href
schema = url.parse(href).protocol
return unless schema
if schema is 'mailto:'
# We sometimes get mailto URIs that are not escaped properly, or have been only partially escaped.
# (T1927) Be sure to escape them once, and completely, before we try to open them. This logic
# *might* apply to http/https as well but it's unclear.
shell.openExternal(encodeURI(decodeURI(href)))
else if schema in ['http:', 'https:', 'tel:']
shell.openExternal(href)
return
openContextualMenuForInput: (event) ->
event.preventDefault()
hasSelectedText = event.target.selectionStart isnt event.target.selectionEnd
remote = require('remote')
Menu = remote.require('menu')
MenuItem = remote.require('menu-item')
menu = new Menu()
menu.append(new MenuItem({
label: 'Cut'
enabled: hasSelectedText
click: => document.execCommand('cut')
}))
menu.append(new MenuItem({
label: 'Copy'
enabled: hasSelectedText
click: => document.execCommand('copy')
}))
menu.append(new MenuItem({
label: 'Paste',
click: => document.execCommand('paste')
}))
menu.popup(remote.getCurrentWindow())
eachTabIndexedElement: (callback) ->
for element in $('[tabindex]')
element = $(element)
continue if element.isDisabled()
tabIndex = parseInt(element.attr('tabindex'))
continue unless tabIndex >= 0
callback(element, tabIndex)
focusNext: =>
focusedTabIndex = parseInt($(':focus').attr('tabindex')) or -Infinity
nextElement = null
nextTabIndex = Infinity
lowestElement = null
lowestTabIndex = Infinity
@eachTabIndexedElement (element, tabIndex) ->
if tabIndex < lowestTabIndex
lowestTabIndex = tabIndex
lowestElement = element
if focusedTabIndex < tabIndex < nextTabIndex
nextTabIndex = tabIndex
nextElement = element
if nextElement?
nextElement.focus()
else if lowestElement?
lowestElement.focus()
focusPrevious: =>
focusedTabIndex = parseInt($(':focus').attr('tabindex')) or Infinity
previousElement = null
previousTabIndex = -Infinity
highestElement = null
highestTabIndex = -Infinity
@eachTabIndexedElement (element, tabIndex) ->
if tabIndex > highestTabIndex
highestTabIndex = tabIndex
highestElement = element
if focusedTabIndex > tabIndex > previousTabIndex
previousTabIndex = tabIndex
previousElement = element
if previousElement?
previousElement.focus()
else if highestElement?
highestElement.focus()
showDevModeMessages: ->
return unless NylasEnv.isMainWindow()
if NylasEnv.inDevMode()
Actions = require './flux/actions'
Actions.postNotification
icon: 'fa-flask'
type: 'developer'
tag: 'developer'
sticky: true
actions: [{label: 'Thanks', id: 'ok', dismisses: true, default: true}]
message: "N1 is running with debug flags enabled (slower). Packages in
~/.nylas/dev/packages will be loaded. Have fun!"
else
console.log("%c Welcome to N1! If you're exploring the source or building a
plugin, you should enable debug flags. It's slower, but
gives you better exceptions, the debug version of React,
and more. Choose %c Developer > Run with Debug Flags %c
from the menu. Also, check out https://nylas.com/N1/docs
for documentation and sample code!",
"background-color: antiquewhite;",
"background-color: antiquewhite; font-weight:bold;",
"background-color: antiquewhite; font-weight:normal;")