~alcinnz/bureaucromancy

b7acdbaa7957bbd8b008c339473a439cbabd0419 — Adrian Cochrane 1 year, 2 months ago e85207d
Add (partial) support image buttons.
M app/Main.hs => app/Main.hs +10 -2
@@ 7,13 7,15 @@ import Network.HTTP.Types
import System.Environment (getArgs)

import Text.HTML.Form.WebApp
import Text.HTML.Form.Query
import Text.HTML.Form

import Data.Text.Lazy.Encoding (encodeUtf8)
import qualified Data.Text.Encoding as Txt
import Data.Text.Lazy (fromStrict)
import qualified Data.Text as Txt
import qualified Text.HTML.DOM as HTML
import Data.Maybe (fromJust)
import Data.Maybe (fromJust, fromMaybe)

main :: IO ()
main = do


@@ 29,5 31,11 @@ servePage :: Form -> Application
servePage form req respond = do
    ret <- renderPage form (pathInfo req) (queryString req)
    case ret of
        Just txt -> respond $ responseLBS status200 [] $ encodeUtf8 $ fromStrict txt
        Just (Right txt) ->
            respond $ responseLBS status200 [] $ encodeUtf8 $ fromStrict txt
        Just (Left qs) -> respond $ responseLBS status200 [] $
            encodeUtf8 $ fromStrict $ Txt.pack $ renderQueryString'
            [(utf8 k, utf8 $ fromMaybe "" v) | (k, v) <- qs]
        Nothing -> respond $ responseLBS status404 [] "Unknown input or operation!"

utf8 = Txt.unpack . Txt.decodeUtf8

M src/Text/HTML/Form.hs => src/Text/HTML/Form.hs +8 -3
@@ 143,7 143,10 @@ queryInputs form = (allInputs >=> inForm) form
    nestedInForm x = listToMaybe ((ancestor >=> laxElement "form") x) == Just form
parseInput :: Cursor -> Maybe Input
parseInput el | _:_ <- laxElement "input" el = Just Input {
        label = fromMaybe (attr "name" el "") $ fmap text label',
        label = fromMaybe
                -- Additional fallbacks are primarily for buttons
                (attr "name" el $ attr "value" el $ attr "alt" el $
                attr "type" el "text") $ fmap text label',
        description = fromMaybe (mkEl $ attr "title" el "") $ fmap node $
            elByID (attr "aria-describedby" el "") `orElse` label',
        inputType = attr "type" el "text",


@@ 151,8 154,10 @@ parseInput el | _:_ <- laxElement "input" el = Just Input {
        inputAutocomplete = attr "autocomplete" el "on",
        autofocus = hasAttr "autofocus" el,
        checked = hasAttr "checked" el,
        disabled = hasAttr "disabled" el,
        readonly = hasAttr "readonly" el,
        -- NOTE: No remaining harm in displaying hidden inputs,
        -- might be informative...
        disabled = hasAttr "disabled" el || attr "type" el "" == "hidden",
        readonly = hasAttr "readonly" el || attr "type" el "" == "hidden",
        multiple = hasAttr "multiple" el,
        dirname = attr "dirname" el "",
        inputName = attr "name" el "",

M src/Text/HTML/Form/Query.hs => src/Text/HTML/Form/Query.hs +2 -0
@@ 17,6 17,8 @@ renderQueryString' query = intercalate "&" [
renderQuery' :: Form -> [(String, String)]
renderQuery' form = concatMap renderInput' $ inputs form
renderInput' :: Input -> [(String, String)]
renderInput' Input { inputType = inputType' }
    | inputType' `elem` ["submit", "reset", "button"] = []
renderInput' Input { checked = False, inputType = inputType' }
    | inputType' `elem` ["radio", "checkbox"] = []
renderInput' Input { inputType = "<select>",

M src/Text/HTML/Form/WebApp.hs => src/Text/HTML/Form/WebApp.hs +20 -5
@@ 12,18 12,19 @@ import System.Directory (XdgDirectory(..), getXdgDirectory, doesFileExist)

import Text.HTML.Form (Form(..), Input(..))
import Text.HTML.Form.WebApp.Ginger (template, resolveSource)
import Text.HTML.Form.Query (renderQueryString)
import Text.HTML.Form.Query (renderQueryString, renderQuery')

renderPage :: Form -> [Text] -> [(ByteString, Maybe ByteString)] -> IO (Maybe Text)
type Query = [(ByteString, Maybe ByteString)]
renderPage :: Form -> [Text] -> Query -> IO (Maybe (Either Query Text))
renderPage form (n:path) query
    | Just ix <- readMaybe $ Txt.unpack n, ix < Prelude.length (inputs form) =
        renderInput form ix (inputs form !! ix) path query
renderPage form [] _ = return $ Just $ Txt.concat [
renderPage form [] _ = return $ Just $ Right $ Txt.concat [
    "<a href='/0/?", Txt.pack $ renderQueryString form, "'>Start!</a>"]
renderPage _ _ _ = return Nothing

renderInput :: Form -> Int -> Input -> [Text] -> [(ByteString, Maybe ByteString)] ->
    IO (Maybe Text)
    IO (Maybe (Either Query Text))
renderInput form ix input [""] qs = renderInput form ix input [] qs
renderInput form ix input@Input { multiple = True } [p] qs
    | '=':v' <- Txt.unpack p,


@@ 64,6 65,19 @@ renderInput form ix input@Input {inputType="radio", inputName=k', value=v'} [] q
    template "checkbox.html" form ix input $ set k' v' qs
renderInput form ix input@Input { inputType="<select>" } [] qs =
    template "select.html" form ix input qs
renderInput form ix input@Input { inputType="submit" } [] qs =
    template "submit.html" form ix input qs
renderInput _ _ input@Input { inputType="submit" } ["_"] qs =
    return $ Just $ Left $ set (inputName input) (value input) qs
renderInput form ix input@Input { inputType="image" } [] qs =
    template "image-button.html" form ix input qs
renderInput _ _ input@Input { inputType="image" } ["_"] qs =
    return $ Just $ Left $ set (inputName input) (value input) qs
renderInput form ix input@Input { inputType="reset" } [] qs =
    template "reset.html" form ix input qs
renderInput form ix input@Input { inputType="reset" } ["_"] _ =
    template "reset.html" form ix input
        [(utf8' k, Just $ utf8' v) | (k, v) <- renderQuery' form]
renderInput form ix input [keyboard] qs =
    renderInput form ix input [keyboard, ""] qs
renderInput form ix input [keyboard, ""] qs | Just (Just _) <- resolveSource path =


@@ 92,7 106,7 @@ renderInput form ix input [] qs = do
            | otherwise = "latin1"
    template ("keyboards/" ++ keyboard' ++ ".html") form ix input qs
renderInput _ _ input _ _ =
    return $ Just $ Txt.concat ["Unknown input type: ", inputType input]
    return $ Just $ Right $ Txt.concat ["Unknown input type: ", inputType input]

utf8 :: Text -> ByteString
utf8 = Txt.encodeUtf8


@@ 100,6 114,7 @@ utf8' :: String -> ByteString
utf8' = utf8 . Txt.pack
set :: Text -> Text -> [(ByteString, Maybe ByteString)]
    -> [(ByteString, Maybe ByteString)]
set "" _ qs = qs -- Mostly for buttons!
set k' v' qs = (utf8 k', Just $ utf8 v'):[q | q@(k, _) <- qs, k /= utf8 k']
unset :: Text -> Text -> [(ByteString, Maybe ByteString)]
    -> [(ByteString, Maybe ByteString)]

M src/Text/HTML/Form/WebApp/Ginger.hs => src/Text/HTML/Form/WebApp/Ginger.hs +6 -4
@@ 27,13 27,15 @@ import Data.Maybe (fromMaybe, isJust)
import qualified Data.Map as M

type Query = [(ByteString, Maybe ByteString)]
template :: Monad m => String -> Form -> Int -> Input -> Query -> m (Maybe Text)
template :: Monad m => String -> Form -> Int -> Input -> Query ->
        m (Maybe (Either Query Text))
template name form ix input query
    | Just (Right tpl) <- parseGingerFile resolveSource name =
        return $ Just $ htmlSource $ flip runGinger tpl $ makeContextHtml ctxt
        return $ Just $ Right $ htmlSource $
            flip runGinger tpl $ makeContextHtml ctxt
    | Just (Left err) <- parseGingerFile resolveSource name =
        return $ Just $ Txt.pack $ show err
    | otherwise = return $ Just "Unexpected error!"
        return $ Just $ Right $ Txt.pack $ show err
    | otherwise = return $ Just $ Right "Unexpected error!"
  where
    ctxt :: Text -> GVal (Run SourcePos (Writer Html) Html)
    ctxt "Q" = query2gval query