content: 
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  -- | Provides a more specific type for arrays in Javascript
  --   (together with basic operations on them).
  module Language.Sunroof.JS.Array
    ( JSArray
    , array, newArray
    , length'
    , lookup'
    , insert'
    , push, pop
    , shift, unshift
    , forEach             -- TODO: remove export in next release (included in SunroofFunctor)
    , empty
    ) where
  
  import Prelude hiding ( lookup, length )
  
  import Data.List ( intercalate )
  import Data.Boolean ( BooleanOf, IfB(..) )
  
  import Language.Sunroof.JavaScript ( Expr, showExpr, literal )
  import Language.Sunroof.Types
  import Language.Sunroof.Classes ( Sunroof(..), SunroofValue(..), SunroofArgument(..) )
  import Language.Sunroof.Selector ( JSSelector, index, (!) )
  import Language.Sunroof.JS.Bool ( JSBool, jsIfB )
  import Language.Sunroof.JS.Number ( JSNumber )
  
  -- -------------------------------------------------------------
  -- JSArray Type
  -- -------------------------------------------------------------
  
  -- | Type if arrays in Javascript. The type parameter
  --   given the entry type.
  data JSArray a = JSArray Expr
  
  -- | Show the Javascript.
  instance Show (JSArray a) where
    show (JSArray v) = showExpr False v
  
  -- | Arrays are first-class Javascript values.
  instance (Sunroof a) => Sunroof (JSArray a) where
    box = JSArray
    unbox (JSArray o) = o
  
  -- | The boolean of arrays are 'JSBool'.
  type instance BooleanOf (JSArray a) = JSBool
  
  -- | You can write branches that return arrays.
  instance (Sunroof a) => IfB (JSArray a) where
    ifB = jsIfB
  
  -- -------------------------------------------------------------
  -- JSArray Combinators
  -- -------------------------------------------------------------
  
  -- | Create a literal array from a Haskell list.
  array :: (SunroofValue a, Sunroof (ValueOf a)) => [a] -> JS t (JSArray (ValueOf a))
  array l  = evaluate $ box $ literal $ "[" ++ intercalate "," (fmap (showExpr False . unbox . js) l) ++ "]"
  
  -- | Create a new array object containing the given values.
  newArray :: (SunroofArgument args, Sunroof a) => args -> JS t (JSArray a)
  newArray args = fun "[]" $$ args
  
  -- | The empty array.
  empty :: (Sunroof a) => JS t (JSArray a)
  empty = newArray ()
  
  -- | The @length@ property of arrays.
  length' :: JSSelector JSNumber
  length' = attr "length"
  
  -- | A type-safe version of array lookup.
  lookup' :: (Sunroof a) => JSNumber -> JSArray a -> a
  lookup' n e = e ! index n
  
  -- | A type-safe version of array insert.
  insert' :: (Sunroof a) => JSNumber -> a -> JSArray a -> JS t ()
  insert' n a e = e # index n := a
  
  -- | Push a element into the array as if it was a stack.
  --   Returns the new length.
  --   See <http://www.w3schools.com/jsref/jsref_push.asp>.
  push :: (SunroofArgument a, Sunroof a) => a -> JSArray a -> JS t JSNumber
  push a = invoke "push" a
  
  -- | Adds a new element to the beginning of the array (queue).
  --   Returns the new length.
  --   See <http://www.w3schools.com/jsref/jsref_unshift.asp>.
  unshift :: (SunroofArgument a, Sunroof a) => a -> JSArray a -> JS t JSNumber
  unshift a = invoke "unshift" a
  
  -- | Pop a element from the array as if it was a stack.
  --   See <http://www.w3schools.com/jsref/jsref_pop.asp>.
  pop :: (Sunroof a) => JSArray a -> JS t a
  pop = invoke "pop" ()
  
  -- | Removes and return the first element of an array (dequeue).
  --   See <http://www.w3schools.com/jsref/jsref_shift.asp>.
  shift :: (Sunroof a) => JSArray a -> JS t a
  shift = invoke "shift" ()
  
  
  instance SunroofFunctor JSArray where
    -- | Foreach iteration method provided by most browsers.
    --   Execute the given action on each element of the array.
    --   See
    --   <https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach>,
    --   <http://msdn.microsoft.com/en-us/library/ie/ff679980.aspx>.
    forEach body arr = do
          f <- function body
          arr # invoke "forEach" f :: JS t ()
          return ()
  
    -- | map provided in all modern versions of JavaScript.
    jsMap body arr = do
          f  <- function body
          arr # invoke "map" f
  