text: module Graphics.UI.Threepenny.Events (
      -- * Synopsis
      -- | Events on DOM elements.
  
      -- * Convenience events
      valueChange, selectionChange, checkedChange,
  
      -- * Standard DOM events
      click, contextmenu, mousemove, mousedown, mouseup,
      hover, leave,
      focus, blur,
      KeyCode, keyup, keydown, keypress,
  
      -- * Migration
      roundCoordinates
      ) where
  
  import Graphics.UI.Threepenny.Attributes
  import Graphics.UI.Threepenny.Core
  
  silence = fmap (const ())
  
  {-----------------------------------------------------------------------------
      Events
  ------------------------------------------------------------------------------}
  -- | Event that occurs when the /user/ changes the value of the input element.
  valueChange :: Element -> Event String
  valueChange el = unsafeMapUI el (const $ get value el) (domEvent "keydown" el)
  
  unsafeMapUI el f = unsafeMapIO (\a -> getWindow el >>= \w -> runUI w (f a))
  
  -- | Event that occurs when the /user/ changes the selection of a @<select>@ element.
  selectionChange :: Element -> Event (Maybe Int)
  selectionChange el = unsafeMapUI el (const $ get selection el) (click el)
  
  -- | Event that occurs when the /user/ changes the checked status of an input
  -- element of type checkbox.
  checkedChange :: Element -> Event Bool
  checkedChange el = unsafeMapUI el (const $ get checked el) (click el)
  
  {-----------------------------------------------------------------------------
      DOM Events
  ------------------------------------------------------------------------------}
  -- | Mouse click.
  click :: Element -> Event ()
  click = silence . domEvent "click"
  
  -- | Context menu event.
  --
  -- The mouse coordinates are relative to the upper left corner of the element.
  contextmenu :: Element -> Event (Double,Double)
  contextmenu = fmap readCoordinates . domEvent "contextmenu"
  
  -- | Mouse enters an element.
  hover :: Element -> Event ()
  hover = silence . domEvent "mouseenter"
  
  -- | Event that periodically occurs while the mouse is moving over an element.
  --
  -- The event value represents the mouse coordinates
  -- relative to the upper left corner of the element.
  --
  -- Note: The @<body>@ element responds to mouse move events,
  -- but only in the area occupied by actual content,
  -- not the whole browser window.
  mousemove :: Element -> Event (Double,Double)
  mousemove = fmap readCoordinates . domEvent "mousemove"
  
  -- NB:
  -- The return types of mouse events have been redefined from @long@
  -- to @double@ in the CSS Object Model View Model working draft,
  -- which browsers have begun to adopt.
  -- See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent#Specifications
  -- and https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX
  --
  -- Similarly, we rely on jQuery's @.offset()@ to return
  -- coordinates relative to the upper left corner of the
  -- element, and this may return fractional data.
  -- https://api.jquery.com/offset/
  readCoordinates :: EventData -> (Double,Double)
  readCoordinates json = (x,y)
      where [x,y] = unsafeFromJSON json
  
  -- | Round a pair of `Double` to the next integers.
  -- This function helps you migrate from previous versions of Threepenny-GUI.
  --
  -- The return types of mouse events (`mousedown`, `mouseup`, `mousemove`, `contextmenu`) 
  -- have been redefined from @long@
  -- to @double@ in the CSS Object Model View Model working draft,
  -- which browsers have begun to adopt.
  --
  -- See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent#Specifications
  --
  -- and https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX
  roundCoordinates :: (Double,Double) -> (Int,Int)
  roundCoordinates (x,y) = (round x, round y)
  
  -- | Mouse down event.
  --
  -- The mouse coordinates are relative to the upper left corner of the element.
  mousedown :: Element -> Event (Double,Double)
  mousedown = fmap readCoordinates . domEvent "mousedown"
  
  -- | Mouse up event.
  --
  -- The mouse coordinates are relative to the upper left corner of the element.
  mouseup :: Element -> Event (Double,Double)
  mouseup = fmap readCoordinates . domEvent "mouseup"
  
  -- | Mouse leaving an element.
  leave :: Element -> Event ()
  leave = silence . domEvent "mouseleave"
  
  -- | Element receives focus.
  focus :: Element -> Event ()
  focus = silence . domEvent "focus"
  
  -- | Element loses focus.
  blur :: Element -> Event ()
  blur = silence . domEvent "blur"
  
  
  type KeyCode = Int
  
  -- | Key pressed while element has focus.
  -- Returns the keycode (as opposed to the ASCII value) of any key, including
  -- SHIFT, CTRL and arrow keys.
  keydown :: Element -> Event KeyCode
  keydown = fmap unsafeFromJSON . domEvent "keydown"
  
  -- | Key released while element has focus.
  keyup :: Element -> Event KeyCode
  keyup   = fmap unsafeFromJSON . domEvent "keyup"
  
  -- | Key pressed while element has focus.
  -- Returns the actual character, taking into account SHIFT or CAPS LOCK.
  keypress :: Element -> Event Char
  keypress = fmap (toEnum . read . head . unsafeFromJSON) . domEvent "keypress"
  