{-# LANGUAGE NamedFieldPuns #-} module Rendering where import Data.Aeson import Data.ByteString.Lazy.Char8 (unpack) import Unsafe.Coerce import Component isOn :: Attributes a -> Bool isOn (On _ _ _) = True isOn _ = False isGeneric :: Attributes a -> Bool isGeneric (Generic _ _) = True isGeneric _ = False isClass :: Attributes a -> Bool isClass (Generic "class" _) = True isClass _ = False getStyle :: Attributes a -> String getStyle (Style (hash, style')) = -- inline styles are just given a hash of -1 if hash == "-1" then style' else "" getStyle _ = "" getClassBasedStyle :: Attributes a -> String getClassBasedStyle (Style (hash, style')) = -- earlier we set the style' to "" to say it's been captured -- also filter out things like "p123 li", which are created -- by nested rules in [style||] templates if style' == "" && not (' ' `elem` hash) then hash else "" getClassBasedStyle _ = "" renderGeneric :: Attributes a -> String renderGeneric attr = case attr of (Generic name value) -> " " <> name <> "=" <> unpack (encode value) _ -> "" renderAttributes :: [Attributes a] -> String renderAttributes attrs = let styles = concatMap getStyle attrs renderedStyle = if not (null styles) then " style=" <> show styles else "" -- TODO: this is uggo classStyles = filter (not . null) $ fmap getClassBasedStyle attrs existingClasses = (\(Generic _ name) -> name) <$> filter isClass attrs combinedClasses = classStyles <> existingClasses renderedClasses = if not (null combinedClasses) then " class=\"" <> unwords combinedClasses <> "\"" else "" listeners = filter isOn attrs renderedListeners = concatMap (\(On name ident action) -> " " <> name <> "-location=" <> unpack (encode ident)) listeners noticeToBind = if null listeners then "" else " bubbling-bound" generics = filter (not . isClass) $ filter isGeneric attrs renderedGenerics = concatMap renderGeneric generics in renderedStyle <> noticeToBind <> renderedListeners <> renderedGenerics <> renderedClasses {-| Takes the tree and turns it into HTML. Attributes are passed down to children until they reach a real HTML tag. -} render :: Purview action m -> String render = render' [] render' :: [Attributes action] -> Purview action m -> String render' attrs tree = case tree of Html kind rest -> "<" <> kind <> renderAttributes attrs <> ">" <> concatMap (render' []) rest <> " kind <> ">" Text val -> val Attribute attr rest -> -- collecting all the attributes till we hit html render' (attr:attrs) rest EffectHandler parentLocation location initEvents state _ cont -> "
" <> render' attrs (unsafeCoerce cont state) <> "
" Handler { identifier, state, continuation } -> "
" <> render' attrs (unsafeCoerce continuation state) <> "
" Receiver { parentIdentifier, identifier, name, child, state } -> " " handler=" <> (show . encode) identifier <> " parent-handler=" <> (show . encode) parentIdentifier <> " receiver-name=\"" <> name <> "\"" <> ">" <> render' attrs (child state) <> "" Value a -> show a