content: {-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls, OverloadedStrings #-}
  -- | Haste's companion to the Prelude.
  module Haste (
      -- * Basic utility functions
      JSString, JSAny, URL, MonadIO (..),
      alert, prompt, eval, writeLog, catJSStr, fromJSStr,
  
      -- * Location handling
      onHashChange, setHash, getHash,
      getLocationHref, setLocationHref, getLocationHostName, getLocationPort,
  
      -- * Timers
      Timer, Interval (..), setTimer, stopTimer,
  
      -- * Fast conversions for JS-native types
      JSType (..), JSNum (..), toString, fromString, convert,
  
      -- * Reflection
      getProgramId, getProgramJS
    ) where
  import Haste.Prim
  import Haste.Timer
  import Haste.Prim.JSType
  import Haste.Hash
  import Haste.Foreign
  import Control.Monad.IO.Class
  
  jsAlert :: JSString -> IO ()
  jsAlert = ffi "alert"
  
  jsLog :: JSString -> IO ()
  jsLog = ffi "(function(x){console.log(x);})"
  
  jsPrompt :: JSString -> IO JSString
  jsPrompt = ffi "(function(s){var x = prompt(s);\
  \return (x === null) ? '' : x.toString();})"
  
  jsEval :: JSString -> IO JSString
  jsEval = ffi "(function(s){var x = eval(s);\
  \return (typeof x === 'undefined') ? 'undefined' : x.toString();})"
  
  -- | JavaScript @alert()@ function.
  alert :: MonadIO m => JSString -> m ()
  alert = liftIO . jsAlert
  
  -- | JavaScript @prompt()@ function.
  prompt :: MonadIO m => JSString -> m JSString
  prompt = liftIO . jsPrompt
  
  -- | JavaScript @eval()@ function.
  eval :: MonadIO m => JSString -> m JSString
  eval = liftIO . jsEval
  
  -- | JavaScript @console.log()@.
  writeLog :: MonadIO m => JSString -> m ()
  writeLog = liftIO . jsLog
  
  -- | Get the value of the @__haste_prog_id@ variable. Unless programmatically
  --   changed, this variable contains the SHA3-256 hash of the currently
  --   executing Haste program.
  getProgramId :: MonadIO m => m JSString
  getProgramId = liftIO getProgramId'
  
  getProgramId' :: IO JSString
  getProgramId' = ffi "(function(){return __haste_prog_id;})"
  
  -- | Get the complete JavaScript source code of the currently executing Haste
  --   program. On IE, this requires that the program's identifier, as returned
  --   by 'getProgramId', has not been tampered with.
  getProgramJS :: MonadIO m => m (Either URL JSString)
  getProgramJS = liftIO $ do
    (murl, msrc) <- getCurrentScript
    return $ maybe (maybe impossible Right msrc) Left murl
    where
      impossible = error "impossible!"
  
  -- | JS worker for 'getProgramJS'.
  getCurrentScript :: IO (Maybe URL, Maybe JSString)
  getCurrentScript = ffi "(function(){\
  \    if(__haste_script_elem) {\
  \        if(__haste_script_elem.innerHTML) {\
  \            return [null, __haste_script_elem.innerHTML];\
  \        } else {\
  \            return [__haste_script_elem.src, null];\
  \        }\
  \    } else {\
  \        var es = document.getElementsByTagName('SCRIPT');\
  \        var re = new RegExp('var __haste_prog_id = \\'\\([0-9a-f]{64}\\)\\';');\
  \        for(var i in es) {\
  \            if(es[i].innerHTML) {\
  \                var match = es[i].innerHTML.match(re);\
  \                if(match && match[1] == __haste_prog_id) {\
  \                    return [null, es[i].innerHTML];\
  \                }\
  \            } else if(es[i].src) {\
  \                var xhr = new XMLHttpRequest();\
  \                xhr.open('GET', es[i].src, false);\
  \                xhr.send();\
  \                var match = xhr.responseText.match(re);\
  \                if(match && match[1] == __haste_prog_id) {\
  \                    return [es[i].src, null];\
  \                }\
  \            }\
  \        }\
  \    }\
  \    throw 'source of current program not found';\
  \})"
  
  -- | Get the current complete location URL.
  getLocationHref :: MonadIO m => m URL
  getLocationHref = liftIO getLocationHref'
  
  getLocationHref' :: IO URL
  getLocationHref' = ffi "(function(){return location.href;})"
  
  -- | Set the location URL.
  setLocationHref :: MonadIO m => URL -> m ()
  setLocationHref href = liftIO (setLocationHref' href)
  
  setLocationHref' :: URL -> IO ()
  setLocationHref' = ffi "(function(href){location.href = href;})"
  
  -- | Get the current location host name.
  getLocationHostName :: MonadIO m => m JSString
  getLocationHostName = liftIO getLocationHostName'
  
  getLocationHostName' :: IO JSString
  getLocationHostName' = ffi "(function(){return location.hostname;})"
  
  -- | Get the current location port.
  getLocationPort :: MonadIO m => m Int
  getLocationPort = liftIO getLocationPort'
  
  getLocationPort' :: IO Int
  getLocationPort' = ffi "(function(){return location.port;})"
  