{-# LANGUAGE NoImplicitPrelude, FlexibleInstances #-} {- | A simple library for client-side HTML generation. Compatible with hsx2hs. -} module HTML where import Prelude import JQuery import FFI fromStringLit :: String -> String fromStringLit = id data Attr a b = a := b -- | type class for embedding values as HTML children -- -- since Fay does not yet support type-class methods we have to fake it via the ffi class AsChild a -- where asChild :: a -> Fay HTML instance AsChild [Char] -- where asChild s = return (CDATA True s) instance AsChild (Fay HTML) -- where asChild = id -- and here is the scary ffi code. asChild :: (AsChild a) => a -> Fay HTML asChild = ffi "(function () { if (%1 instanceof Fay$$Cons) { return { 'instance' : 'CDATA', slot1 : true, slot2 : Fay$$fayToJs_string(%1) }; } else { var monad = Fay$$_(%1, true); return monad.value; }})()" class AsAttr a -- where asAttr :: a -> Fay (String, String) instance AsAttr (Attr String String) -- where asAttr (a := b) = return (a, b) asAttr :: (Attr String String) -> Fay (String, String) asAttr ((:=) a b) = return (a, b) -- | ADT for 'HTML' data HTML = Element String [(String, String)] [HTML] -- ^ Element name attributes children | CDATA Bool String -- ^ CDATA needEscaping value -- | generate an HTML element genElement :: (Maybe String, String) -- ^ Element name -> [Fay (String, String)] -- ^ list of attributes -> [Fay HTML] -- ^ list of children -> Fay HTML genElement (_, n) genAttrs genChildren = do attrs <- sequence $ genAttrs children <- sequence $ genChildren return (Element n attrs children) -- | render the 'HTML' into a JQuery DOM tree. You still need to -- append the result somewhere. -- -- NOTE: This function requires 'jQuery' renderHTML :: HTML -> Fay JQuery renderHTML (Element n attrs children) = do elem <- selectElement =<< createElement n mapM_ (\(n, v) -> setAttr n v elem) attrs mapM_ (\child -> do cElem <- renderHTML child append cElem elem) children return elem renderHTML (CDATA True str) = selectElement =<< createTextNode str renderHTML (CDATA False str) = do alert "Unsure how to insert pre-escaped text into the generated HTML." selectElement =<< createTextNode str -- | Alert using window.alert. alert :: String -> Fay () alert = ffi "window.alert(%1)" ------------------------------------------------------------------------------ -- HTML Combinators -- | \