    {-# LANGUAGE CPP               #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE PatternGuards     #-}

    module Text.XmlHtml.HTML.Render where

    import           Blaze.ByteString.Builder
    import           Control.Applicative
    import qualified Data.HashMap.Strict as M
    import qualified Data.HashSet as S
    import           Data.Maybe
    import qualified Text.Parsec as P
    import           Text.XmlHtml.Common
    import           Text.XmlHtml.TextParser
    import           Text.XmlHtml.HTML.Meta
    import qualified Text.XmlHtml.HTML.Parse as P
    import qualified Text.XmlHtml.XML.Parse as XML
    import           Text.XmlHtml.XML.Render (docTypeDecl, entity)

    import           Data.Text (Text)
    import qualified Data.Text as T

    #if !MIN_VERSION_base(4,8,0)
    import           Data.Monoid
    #endif

    ------------------------------------------------------------------------------
    -- | And, the rendering code.
    renderWithOptions :: RenderOptions -> Encoding -> Maybe DocType -> [Node] -> Builder
    renderWithOptions :: RenderOptions -> Encoding -> Maybe DocType -> [Node] -> Builder
    renderWithOptions RenderOptions
    opts Encoding
    e Maybe DocType
    dt [Node]
    ns = Builder
    byteOrder
           Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Maybe DocType -> Builder
    docTypeDecl Encoding
    e Maybe DocType
    dt
           Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Builder
    nodes
        where byteOrder :: Builder
    byteOrder | Encoding -> Bool
    isUTF16 Encoding
    e = Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "\xFEFF" -- byte order mark
                        | Bool
    otherwise = Builder
    forall a. Monoid a => a
    mempty
              nodes :: Builder
    nodes | [Node] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Node]
    ns   = Builder
    forall a. Monoid a => a
    mempty
                    | Bool
    otherwise = RenderOptions -> Encoding -> Node -> Builder
    firstNode RenderOptions
    opts Encoding
    e ([Node] -> Node
    forall a. HasCallStack => [a] -> a
    head [Node]
    ns)
                        Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` ([Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat ([Builder] -> Builder) -> [Builder] -> Builder
    forall a b. (a -> b) -> a -> b
    $ (Node -> Builder) -> [Node] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    map (RenderOptions -> Encoding -> Node -> Builder
    node RenderOptions
    opts Encoding
    e) ([Node] -> [Node]
    forall a. HasCallStack => [a] -> [a]
    tail [Node]
    ns))

    ------------------------------------------------------------------------------
    render :: Encoding -> Maybe DocType -> [Node] -> Builder
    render :: Encoding -> Maybe DocType -> [Node] -> Builder
    render = RenderOptions -> Encoding -> Maybe DocType -> [Node] -> Builder
    renderWithOptions RenderOptions
    defaultRenderOptions

    ------------------------------------------------------------------------------
    -- | Function for rendering HTML nodes without the overhead of creating a
    -- Document structure.
    renderHtmlFragmentWithOptions :: RenderOptions -> Encoding -> [Node] -> Builder
    renderHtmlFragmentWithOptions :: RenderOptions -> Encoding -> [Node] -> Builder
    renderHtmlFragmentWithOptions RenderOptions
    _ Encoding
    _ []     = Builder
    forall a. Monoid a => a
    mempty
    renderHtmlFragmentWithOptions RenderOptions
    opts Encoding
    e (Node
    n:[Node]
    ns) =
        RenderOptions -> Encoding -> Node -> Builder
    firstNode RenderOptions
    opts Encoding
    e Node
    n Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` ([Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat ([Builder] -> Builder) -> [Builder] -> Builder
    forall a b. (a -> b) -> a -> b
    $ (Node -> Builder) -> [Node] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    map (RenderOptions -> Encoding -> Node -> Builder
    node RenderOptions
    opts Encoding
    e) [Node]
    ns)

    ------------------------------------------------------------------------------
    -- | Function for rendering HTML nodes without the overhead of creating a
    -- Document structure, using default rendering options
    renderHtmlFragment :: Encoding -> [Node] -> Builder
    renderHtmlFragment :: Encoding -> [Node] -> Builder
    renderHtmlFragment = RenderOptions -> Encoding -> [Node] -> Builder
    renderHtmlFragmentWithOptions RenderOptions
    defaultRenderOptions

    ------------------------------------------------------------------------------
    -- | HTML allows & so long as it is not "ambiguous" (i.e., looks like an
    -- entity).  So we have a special case for that.
    escaped :: [Char] -> Encoding -> Text -> Builder
    escaped :: [Char] -> Encoding -> Text -> Builder
    escaped [Char]
    _   Encoding
    _ Text
    "" = Builder
    forall a. Monoid a => a
    mempty
    escaped [Char]
    bad Encoding
    e Text
    t  =
        let (Text
    p,Text
    s) = (Char -> Bool) -> Text -> (Text, Text)
    T.break (Char -> [Char] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [Char]
    bad) Text
    t
            r :: Maybe (Char, Text)
    r     = Text -> Maybe (Char, Text)
    T.uncons Text
    s
        in  Encoding -> Text -> Builder
    fromText Encoding
    e Text
    p Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` case Maybe (Char, Text)
    r of
                Maybe (Char, Text)
    Nothing
                    -> Builder
    forall a. Monoid a => a
    mempty
                Just (Char
    '&',Text
    ss) | Either [Char] () -> Bool
    forall {a} {b}. Either a b -> Bool
    isLeft (Parser () -> [Char] -> Text -> Either [Char] ()
    forall a. Parser a -> [Char] -> Text -> Either [Char] a
    parseText Parser ()
    ambigAmp [Char]
    "" Text
    s)
                    -> Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "&" Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` [Char] -> Encoding -> Text -> Builder
    escaped [Char]
    bad Encoding
    e Text
    ss
                Just (Char
    c,Text
    ss)
                    -> Encoding -> Char -> Builder
    entity Encoding
    e Char
    c Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` [Char] -> Encoding -> Text -> Builder
    escaped [Char]
    bad Encoding
    e Text
    ss
      where isLeft :: Either a b -> Bool
    isLeft   = (a -> Bool) -> (b -> Bool) -> Either a b -> Bool
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either (Bool -> a -> Bool
    forall a b. a -> b -> a
    const Bool
    True) (Bool -> b -> Bool
    forall a b. a -> b -> a
    const Bool
    False)
            ambigAmp :: Parser ()
    ambigAmp = Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    P.char Char
    '&' ParsecT Text () Identity Char -> Parser () -> Parser ()
    forall a b.
    ParsecT Text () Identity a
    -> ParsecT Text () Identity b -> ParsecT Text () Identity b
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *>
                (ParsecT Text () Identity Char
    P.finishCharRef ParsecT Text () Identity Char -> Parser () -> Parser ()
    forall a b.
    ParsecT Text () Identity a
    -> ParsecT Text () Identity b -> ParsecT Text () Identity b
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> () -> Parser ()
    forall a. a -> ParsecT Text () Identity a
    forall (m :: * -> *) a. Monad m => a -> m a
    return () Parser () -> Parser () -> Parser ()
    forall a.
    ParsecT Text () Identity a
    -> ParsecT Text () Identity a -> ParsecT Text () Identity a
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> Parser Text
    XML.name Parser Text -> Parser () -> Parser ()
    forall a b.
    ParsecT Text () Identity a
    -> ParsecT Text () Identity b -> ParsecT Text () Identity b
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> () -> Parser ()
    forall a. a -> ParsecT Text () Identity a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ())

    ------------------------------------------------------------------------------
    node :: RenderOptions -> Encoding -> Node -> Builder
    node :: RenderOptions -> Encoding -> Node -> Builder
    node RenderOptions
    _ Encoding
    e (TextNode Text
    t)                        = [Char] -> Encoding -> Text -> Builder
    escaped [Char]
    "<>&" Encoding
    e Text
    t
    node RenderOptions
    _ Encoding
    e (Comment Text
    t) | Text
    "--" Text -> Text -> Bool
    `T.isInfixOf`  Text
    t = [Char] -> Builder
    forall a. HasCallStack => [Char] -> a
    error [Char]
    "Invalid comment"
                         | Text
    "-"  Text -> Text -> Bool
    `T.isSuffixOf` Text
    t = [Char] -> Builder
    forall a. HasCallStack => [Char] -> a
    error [Char]
    "Invalid comment"
                         | Bool
    otherwise             = Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "<!--"
                                                   Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    t
                                                   Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "-->"
    node RenderOptions
    opts Encoding
    e (Element Text
    t [(Text, Text)]
    a [Node]
    c)                     =
        let tbase :: Text
    tbase = Text -> Text
    T.toLower (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ (Text, Text) -> Text
    forall a b. (a, b) -> b
    snd ((Text, Text) -> Text) -> (Text, Text) -> Text
    forall a b. (a -> b) -> a -> b
    $ HasCallStack => Text -> Text -> (Text, Text)
    Text -> Text -> (Text, Text)
    T.breakOnEnd Text
    ":" Text
    t
        in  RenderOptions
    -> Encoding -> Text -> Text -> [(Text, Text)] -> [Node] -> Builder
    element RenderOptions
    opts Encoding
    e Text
    t Text
    tbase [(Text, Text)]
    a [Node]
    c

    ------------------------------------------------------------------------------
    -- | Process the first node differently to encode leading whitespace.  This
    -- lets us be sure that @parseHTML@ is a left inverse to @render@.
    firstNode :: RenderOptions -> Encoding -> Node -> Builder
    firstNode :: RenderOptions -> Encoding -> Node -> Builder
    firstNode RenderOptions
    opts Encoding
    e (Comment Text
    t)     = RenderOptions -> Encoding -> Node -> Builder
    node RenderOptions
    opts Encoding
    e (Text -> Node
    Comment Text
    t)
    firstNode RenderOptions
    opts Encoding
    e (Element Text
    t [(Text, Text)]
    a [Node]
    c) = RenderOptions -> Encoding -> Node -> Builder
    node RenderOptions
    opts Encoding
    e (Text -> [(Text, Text)] -> [Node] -> Node
    Element Text
    t [(Text, Text)]
    a [Node]
    c)
    firstNode RenderOptions
    _    Encoding
    _ (TextNode Text
    "")   = Builder
    forall a. Monoid a => a
    mempty
    firstNode RenderOptions
    opts Encoding
    e (TextNode Text
    t)    = let (Char
    c,Text
    t') = Maybe (Char, Text) -> (Char, Text)
    forall a. HasCallStack => Maybe a -> a
    fromJust (Maybe (Char, Text) -> (Char, Text))
    -> Maybe (Char, Text) -> (Char, Text)
    forall a b. (a -> b) -> a -> b
    $ Text -> Maybe (Char, Text)
    T.uncons Text
    t
                                       in [Char] -> Encoding -> Text -> Builder
    escaped [Char]
    "<>& \t\r" Encoding
    e (Char -> Text
    T.singleton Char
    c)
                                          Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` RenderOptions -> Encoding -> Node -> Builder
    node RenderOptions
    opts Encoding
    e (Text -> Node
    TextNode Text
    t')

    ------------------------------------------------------------------------------
    -- XXX: Should do something to avoid concatting large CDATA sections before
    -- writing them to the output.
    element :: RenderOptions -> Encoding -> Text -> Text -> [(Text, Text)] -> [Node] -> Builder
    element :: RenderOptions
    -> Encoding -> Text -> Text -> [(Text, Text)] -> [Node] -> Builder
    element RenderOptions
    opts Encoding
    e Text
    t Text
    tb [(Text, Text)]
    a [Node]
    c
        | Text
    tb Text -> HashSet Text -> Bool
    forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
    `S.member` HashSet Text
    voidTags Bool -> Bool -> Bool
    && [Node] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Node]
    c         =
            Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "<"
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    t
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` ([Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat ([Builder] -> Builder) -> [Builder] -> Builder
    forall a b. (a -> b) -> a -> b
    $ ((Text, Text) -> Builder) -> [(Text, Text)] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    map (RenderOptions -> Encoding -> Text -> (Text, Text) -> Builder
    attribute RenderOptions
    opts Encoding
    e Text
    tb) [(Text, Text)]
    a)
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    " />"
        | Text
    tb Text -> HashSet Text -> Bool
    forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
    `S.member` HashSet Text
    voidTags                   =
            [Char] -> Builder
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> Builder) -> [Char] -> Builder
    forall a b. (a -> b) -> a -> b
    $ Text -> [Char]
    T.unpack Text
    t [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " must be empty"
        | Text -> [(Text, Text)] -> Bool
    isRawText Text
    tb [(Text, Text)]
    a,
          (Node -> Bool) -> [Node] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    all Node -> Bool
    isTextNode [Node]
    c,
          let s :: Text
    s = [Text] -> Text
    T.concat ((Node -> Text) -> [Node] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Node -> Text
    nodeText [Node]
    c),
          Bool -> Bool
    not (Text
    "</" Text -> Text -> Text
    `T.append` Text
    t Text -> Text -> Bool
    `T.isInfixOf` Text
    s) =
            Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "<"
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    t
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` ([Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat ([Builder] -> Builder) -> [Builder] -> Builder
    forall a b. (a -> b) -> a -> b
    $ ((Text, Text) -> Builder) -> [(Text, Text)] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    map (RenderOptions -> Encoding -> Text -> (Text, Text) -> Builder
    attribute RenderOptions
    opts Encoding
    e Text
    tb) [(Text, Text)]
    a)
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    ">"
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    s
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "</"
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    t
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    ">"
        | Text -> [(Text, Text)] -> Bool
    isRawText Text
    tb [(Text, Text)]
    a,
          [ TextNode Text
    _ ] <- [Node]
    c                     =
            [Char] -> Builder
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> Builder) -> [Char] -> Builder
    forall a b. (a -> b) -> a -> b
    $ Text -> [Char]
    T.unpack Text
    t [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " cannot contain text looking like its end tag"
        | Text -> [(Text, Text)] -> Bool
    isRawText Text
    tb [(Text, Text)]
    a                           =
            [Char] -> Builder
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> Builder) -> [Char] -> Builder
    forall a b. (a -> b) -> a -> b
    $ Text -> [Char]
    T.unpack Text
    t [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " cannot contain child elements or comments"
        | Bool
    otherwise =
            Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "<"
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    t
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` ([Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat ([Builder] -> Builder) -> [Builder] -> Builder
    forall a b. (a -> b) -> a -> b
    $ ((Text, Text) -> Builder) -> [(Text, Text)] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    map (RenderOptions -> Encoding -> Text -> (Text, Text) -> Builder
    attribute RenderOptions
    opts Encoding
    e Text
    tb) [(Text, Text)]
    a)
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    ">"
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` ([Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat ([Builder] -> Builder) -> [Builder] -> Builder
    forall a b. (a -> b) -> a -> b
    $ (Node -> Builder) -> [Node] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    map (RenderOptions -> Encoding -> Node -> Builder
    node RenderOptions
    opts Encoding
    e) [Node]
    c)
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    "</"
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    t
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    ">"

    ------------------------------------------------------------------------------
    attribute :: RenderOptions -> Encoding -> Text -> (Text, Text) -> Builder
    attribute :: RenderOptions -> Encoding -> Text -> (Text, Text) -> Builder
    attribute RenderOptions
    opts Encoding
    e Text
    tb (Text
    n,Text
    v)
        | Text
    v Text -> Text -> Bool
    forall a. Eq a => a -> a -> Bool
    == Text
    "" Bool -> Bool -> Bool
    && Bool -> Bool
    not Bool
    explicit =
            Encoding -> Text -> Builder
    fromText Encoding
    e Text
    " "
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    n
        | RenderOptions -> AttrResolveInternalQuotes
    roAttributeResolveInternal RenderOptions
    opts AttrResolveInternalQuotes -> AttrResolveInternalQuotes -> Bool
    forall a. Eq a => a -> a -> Bool
    == AttrResolveInternalQuotes
    AttrResolveAvoidEscape
          Bool -> Bool -> Bool
    && Text
    surround Text -> Text -> Bool
    `T.isInfixOf` Text
    v
          Bool -> Bool -> Bool
    && Bool -> Bool
    not (Text
    alternative Text -> Text -> Bool
    `T.isInfixOf` Text
    v) =
            Encoding -> Text -> Builder
    fromText Encoding
    e Text
    " "
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    n
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e (Char
    '=' Char -> Text -> Text
    `T.cons` Text
    alternative)
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` [Char] -> Encoding -> Text -> Builder
    escaped [Char]
    "&" Encoding
    e Text
    v
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    alternative
        | Bool
    otherwise =
            Encoding -> Text -> Builder
    fromText Encoding
    e Text
    " "
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    n
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e (Char
    '=' Char -> Text -> Text
    `T.cons` Text
    surround)
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` (Text -> Text) -> Builder -> Builder
    bmap (HasCallStack => Text -> Text -> Text -> Text
    Text -> Text -> Text -> Text
    T.replace Text
    surround Text
    ent) ([Char] -> Encoding -> Text -> Builder
    escaped [Char]
    "&" Encoding
    e Text
    v)
            Builder -> Builder -> Builder
    forall a. Monoid a => a -> a -> a
    `mappend` Encoding -> Text -> Builder
    fromText Encoding
    e Text
    surround
      where
        (Text
    surround, Text
    alternative, Text
    ent) = case RenderOptions -> AttrSurround
    roAttributeSurround RenderOptions
    opts of
            AttrSurround
    SurroundSingleQuote -> (Text
    "'" , Text
    "\"", Text
    "&apos;")
            AttrSurround
    SurroundDoubleQuote -> (Text
    "\"", Text
    "'" , Text
    "&quot;")
        nbase :: Text
    nbase    = Text -> Text
    T.toLower (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ (Text, Text) -> Text
    forall a b. (a, b) -> b
    snd ((Text, Text) -> Text) -> (Text, Text) -> Text
    forall a b. (a -> b) -> a -> b
    $ HasCallStack => Text -> Text -> (Text, Text)
    Text -> Text -> (Text, Text)
    T.breakOnEnd Text
    ":" Text
    n
        explicit :: Bool
    explicit = Bool
    -> (HashMap Text (HashSet Text) -> Bool)
    -> Maybe (HashMap Text (HashSet Text))
    -> Bool
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe
                   Bool
    True
                   -- Nothing 'explicitEmptyAttributes' means: attach '=""' to all
                   -- empty attributes
                   (Bool -> (HashSet Text -> Bool) -> Maybe (HashSet Text) -> Bool
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Bool
    False (Text -> HashSet Text -> Bool
    forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
    S.member Text
    nbase) (Maybe (HashSet Text) -> Bool)
    -> (HashMap Text (HashSet Text) -> Maybe (HashSet Text))
    -> HashMap Text (HashSet Text)
    -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> HashMap Text (HashSet Text) -> Maybe (HashSet Text)
    forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
    M.lookup Text
    tb)
                   -- (Just m) means: attach '=""' only when tag and attr name
                   -- are in the explicit-empty-attrs map 'm'
                   (RenderOptions -> Maybe (HashMap Text (HashSet Text))
    roExplicitEmptyAttrs RenderOptions
    opts)