{-# OPTIONS_HADDOCK hide #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Text.XHtml.internals
-- Copyright   :  (c) Andy Gill, and the Oregon Graduate Institute of
--                Science and Technology, 1999-2001,
--                (c) Bjorn Bringert, 2004-2006
-- License     :  BSD-style (see the file LICENSE)
-- Maintainer  :  Chris Dornan <chris@chrisdornan.com>
-- Stability   :  Stable
-- Portability :  Portable
--
-- Internals of the XHTML combinator library.
-----------------------------------------------------------------------------
module Text.XHtml.Internals where

import Data.Char
import qualified Data.Semigroup as Sem
import qualified Data.Monoid as Mon

infixr 2 +++  -- combining Html
infixr 7 <<   -- nesting Html
infixl 8 !    -- adding optional arguments

--
-- * Data types
--

-- | A important property of Html is that all strings inside the
-- structure are already in Html friendly format.
data HtmlElement
      = HtmlString String
        -- ^ ..just..plain..normal..text... but using &copy; and &amb;, etc.
      | HtmlTag {
              HtmlElement -> String
markupTag      :: String,
              HtmlElement -> [HtmlAttr]
markupAttrs    :: [HtmlAttr],
              HtmlElement -> Html
markupContent  :: Html
              }
        -- ^ tag with internal markup

-- | Attributes with name and value.
data HtmlAttr = HtmlAttr String String


htmlAttrPair :: HtmlAttr -> (String,String)
htmlAttrPair :: HtmlAttr -> (String, String)
htmlAttrPair (HtmlAttr String
n String
v) = (String
n,String
v)


newtype Html = Html { Html -> [HtmlElement]
getHtmlElements :: [HtmlElement] }


--
-- * Classes
--

instance Show Html where
      showsPrec :: Int -> Html -> ShowS
showsPrec Int
_ Html
html = String -> ShowS
showString (Html -> String
forall html. HTML html => html -> String
renderHtmlFragment Html
html)
      showList :: [Html] -> ShowS
showList [Html]
htmls   = (ShowS -> ShowS -> ShowS) -> ShowS -> [ShowS] -> ShowS
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.) ShowS
forall a. a -> a
id ((Html -> ShowS) -> [Html] -> [ShowS]
forall a b. (a -> b) -> [a] -> [b]
map Html -> ShowS
forall a. Show a => a -> ShowS
shows [Html]
htmls)

instance Show HtmlAttr where
      showsPrec :: Int -> HtmlAttr -> ShowS
showsPrec Int
_ (HtmlAttr String
str String
val) =
              String -> ShowS
showString String
str ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
              String -> ShowS
showString String
"=" ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
              String -> ShowS
forall a. Show a => a -> ShowS
shows String
val

-- | @since 3000.2.2
instance Sem.Semigroup Html where
    <> :: Html -> Html -> Html
(<>) = Html -> Html -> Html
forall a b. (HTML a, HTML b) => a -> b -> Html
(+++)

instance Mon.Monoid Html where
    mempty :: Html
mempty = Html
noHtml
    mappend :: Html -> Html -> Html
mappend = Html -> Html -> Html
forall a. Semigroup a => a -> a -> a
(Sem.<>)

-- | HTML is the class of things that can be validly put
-- inside an HTML tag. So this can be one or more 'Html' elements,
-- or a 'String', for example.
class HTML a where
      toHtml     :: a -> Html
      toHtmlFromList :: [a] -> Html

      toHtmlFromList [a]
xs = [HtmlElement] -> Html
Html ([[HtmlElement]] -> [HtmlElement]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ [HtmlElement]
x | (Html [HtmlElement]
x) <- (a -> Html) -> [a] -> [Html]
forall a b. (a -> b) -> [a] -> [b]
map a -> Html
forall a. HTML a => a -> Html
toHtml [a]
xs])

instance HTML Html where
      toHtml :: Html -> Html
toHtml Html
a    = Html
a

instance HTML Char where
      toHtml :: Char -> Html
toHtml       Char
a = String -> Html
forall a. HTML a => a -> Html
toHtml [Char
a]
      toHtmlFromList :: String -> Html
toHtmlFromList []  = [HtmlElement] -> Html
Html []
      toHtmlFromList String
str = [HtmlElement] -> Html
Html [String -> HtmlElement
HtmlString (ShowS
stringToHtmlString String
str)]

instance (HTML a) => HTML [a] where
      toHtml :: [a] -> Html
toHtml [a]
xs = [a] -> Html
forall a. HTML a => [a] -> Html
toHtmlFromList [a]
xs

instance HTML a => HTML (Maybe a) where
      toHtml :: Maybe a -> Html
toHtml = Html -> (a -> Html) -> Maybe a -> Html
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Html
noHtml a -> Html
forall a. HTML a => a -> Html
toHtml

class ADDATTRS a where
      (!) :: a -> [HtmlAttr] -> a

-- | CHANGEATTRS is a more expressive alternative to ADDATTRS
class CHANGEATTRS a where
      changeAttrs :: a -> ([HtmlAttr]->[HtmlAttr]) -> a

instance (ADDATTRS b) => ADDATTRS (a -> b) where
      a -> b
fn ! :: (a -> b) -> [HtmlAttr] -> a -> b
! [HtmlAttr]
attr        = \ a
arg -> a -> b
fn a
arg b -> [HtmlAttr] -> b
forall a. ADDATTRS a => a -> [HtmlAttr] -> a
! [HtmlAttr]
attr

instance (CHANGEATTRS b) => CHANGEATTRS (a -> b) where
      changeAttrs :: (a -> b) -> ([HtmlAttr] -> [HtmlAttr]) -> a -> b
changeAttrs a -> b
fn [HtmlAttr] -> [HtmlAttr]
f = \ a
arg -> b -> ([HtmlAttr] -> [HtmlAttr]) -> b
forall a. CHANGEATTRS a => a -> ([HtmlAttr] -> [HtmlAttr]) -> a
changeAttrs (a -> b
fn a
arg) [HtmlAttr] -> [HtmlAttr]
f

instance ADDATTRS Html where
      (Html [HtmlElement]
htmls) ! :: Html -> [HtmlAttr] -> Html
! [HtmlAttr]
attr = [HtmlElement] -> Html
Html ((HtmlElement -> HtmlElement) -> [HtmlElement] -> [HtmlElement]
forall a b. (a -> b) -> [a] -> [b]
map HtmlElement -> HtmlElement
addAttrs [HtmlElement]
htmls)
        where
              addAttrs :: HtmlElement -> HtmlElement
addAttrs (html :: HtmlElement
html@(HtmlTag { markupAttrs :: HtmlElement -> [HtmlAttr]
markupAttrs = [HtmlAttr]
attrs }) )
                            = HtmlElement
html { markupAttrs :: [HtmlAttr]
markupAttrs = [HtmlAttr]
attrs [HtmlAttr] -> [HtmlAttr] -> [HtmlAttr]
forall a. [a] -> [a] -> [a]
++ [HtmlAttr]
attr }
              addAttrs HtmlElement
html = HtmlElement
html

instance CHANGEATTRS Html where
      changeAttrs :: Html -> ([HtmlAttr] -> [HtmlAttr]) -> Html
changeAttrs (Html [HtmlElement]
htmls) [HtmlAttr] -> [HtmlAttr]
f = [HtmlElement] -> Html
Html ((HtmlElement -> HtmlElement) -> [HtmlElement] -> [HtmlElement]
forall a b. (a -> b) -> [a] -> [b]
map HtmlElement -> HtmlElement
addAttrs [HtmlElement]
htmls)
        where
              addAttrs :: HtmlElement -> HtmlElement
addAttrs (html :: HtmlElement
html@(HtmlTag { markupAttrs :: HtmlElement -> [HtmlAttr]
markupAttrs = [HtmlAttr]
attrs }) )
                            = HtmlElement
html { markupAttrs :: [HtmlAttr]
markupAttrs = [HtmlAttr] -> [HtmlAttr]
f [HtmlAttr]
attrs }
              addAttrs HtmlElement
html = HtmlElement
html


--
-- * Html primitives and basic combinators
--

-- | Put something inside an HTML element.
(<<) :: (HTML a) =>
        (Html -> b) -- ^ Parent
     -> a -- ^ Child
     -> b
Html -> b
fn << :: (Html -> b) -> a -> b
<< a
arg = Html -> b
fn (a -> Html
forall a. HTML a => a -> Html
toHtml a
arg)


concatHtml :: (HTML a) => [a] -> Html
concatHtml :: [a] -> Html
concatHtml [a]
as = [HtmlElement] -> Html
Html ([[HtmlElement]] -> [HtmlElement]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ((a -> [HtmlElement]) -> [a] -> [[HtmlElement]]
forall a b. (a -> b) -> [a] -> [b]
map (Html -> [HtmlElement]
getHtmlElements(Html -> [HtmlElement]) -> (a -> Html) -> a -> [HtmlElement]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.a -> Html
forall a. HTML a => a -> Html
toHtml) [a]
as))

-- | Create a piece of HTML which is the concatenation
--   of two things which can be made into HTML.
(+++) :: (HTML a,HTML b) => a -> b -> Html
a
a +++ :: a -> b -> Html
+++ b
b = [HtmlElement] -> Html
Html (Html -> [HtmlElement]
getHtmlElements (a -> Html
forall a. HTML a => a -> Html
toHtml a
a) [HtmlElement] -> [HtmlElement] -> [HtmlElement]
forall a. [a] -> [a] -> [a]
++ Html -> [HtmlElement]
getHtmlElements (b -> Html
forall a. HTML a => a -> Html
toHtml b
b))

-- | An empty piece of HTML.
noHtml :: Html
noHtml :: Html
noHtml = [HtmlElement] -> Html
Html []

-- | Checks whether the given piece of HTML is empty.
isNoHtml :: Html -> Bool
isNoHtml :: Html -> Bool
isNoHtml (Html [HtmlElement]
xs) = [HtmlElement] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [HtmlElement]
xs

-- | Constructs an element with a custom name.
tag :: String -- ^ Element name
    -> Html -- ^ Element contents
    -> Html
tag :: String -> Html -> Html
tag String
str       Html
htmls = [HtmlElement] -> Html
Html [
      HtmlTag :: String -> [HtmlAttr] -> Html -> HtmlElement
HtmlTag {
              markupTag :: String
markupTag = String
str,
              markupAttrs :: [HtmlAttr]
markupAttrs = [],
              markupContent :: Html
markupContent = Html
htmls }]

-- | Constructs an element with a custom name, and
--   without any children.
itag :: String -> Html
itag :: String -> Html
itag String
str = String -> Html -> Html
tag String
str Html
noHtml

emptyAttr :: String -> HtmlAttr
emptyAttr :: String -> HtmlAttr
emptyAttr String
s = String -> String -> HtmlAttr
HtmlAttr String
s String
s

intAttr :: String -> Int -> HtmlAttr
intAttr :: String -> Int -> HtmlAttr
intAttr String
s Int
i = String -> String -> HtmlAttr
HtmlAttr String
s (Int -> String
forall a. Show a => a -> String
show Int
i)

strAttr :: String -> String -> HtmlAttr
strAttr :: String -> String -> HtmlAttr
strAttr String
s String
t = String -> String -> HtmlAttr
HtmlAttr String
s (ShowS
stringToHtmlString String
t)

htmlAttr :: String -> Html -> HtmlAttr
htmlAttr :: String -> Html -> HtmlAttr
htmlAttr String
s Html
t = String -> String -> HtmlAttr
HtmlAttr String
s (Html -> String
forall a. Show a => a -> String
show Html
t)


{-
foldHtml :: (String -> [HtmlAttr] -> [a] -> a)
      -> (String -> a)
      -> Html
      -> a
foldHtml f g (HtmlTag str attr fmls)
      = f str attr (map (foldHtml f g) fmls)
foldHtml f g (HtmlString  str)
      = g str

-}

-- | Processing Strings into Html friendly things.
stringToHtmlString :: String -> String
stringToHtmlString :: ShowS
stringToHtmlString = (Char -> String) -> ShowS
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> String
fixChar
    where
      fixChar :: Char -> String
fixChar Char
'<' = String
"&lt;"
      fixChar Char
'>' = String
"&gt;"
      fixChar Char
'&' = String
"&amp;"
      fixChar Char
'"' = String
"&quot;"
      fixChar Char
c | Char -> Int
ord Char
c Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0x80 = [Char
c]
      fixChar Char
c = String
"&#" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (Char -> Int
ord Char
c) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
";"


-- | This is not processed for special chars.
-- use stringToHtml or lineToHtml instead, for user strings,
-- because they understand special chars, like @'<'@.
primHtml :: String -> Html
primHtml :: String -> Html
primHtml String
x | String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
x    = [HtmlElement] -> Html
Html []
           | Bool
otherwise = [HtmlElement] -> Html
Html [String -> HtmlElement
HtmlString String
x]



--
-- * Html Rendering
--

mkHtml :: HTML html => html -> Html
mkHtml :: html -> Html
mkHtml = (String -> Html -> Html
tag String
"html" (Html -> Html) -> [HtmlAttr] -> Html -> Html
forall a. ADDATTRS a => a -> [HtmlAttr] -> a
! [String -> String -> HtmlAttr
strAttr String
"xmlns" String
"http://www.w3.org/1999/xhtml"] (Html -> Html) -> html -> Html
forall a b. HTML a => (Html -> b) -> a -> b
<<)

-- | Output the HTML without adding newlines or spaces within the markup.
--   This should be the most time and space efficient way to
--   render HTML, though the ouput is quite unreadable.
showHtmlInternal :: HTML html =>
                    String -- ^ DOCTYPE declaration
                 -> html -> String
showHtmlInternal :: String -> html -> String
showHtmlInternal String
docType html
theHtml =
    String
docType String -> ShowS
forall a. [a] -> [a] -> [a]
++ Html -> String
forall html. HTML html => html -> String
showHtmlFragment (html -> Html
forall a. HTML a => a -> Html
mkHtml html
theHtml)

-- | Outputs indented HTML. Because space matters in
--   HTML, the output is quite messy.
renderHtmlInternal :: HTML html =>
                      String  -- ^ DOCTYPE declaration
                   -> html -> String
renderHtmlInternal :: String -> html -> String
renderHtmlInternal String
docType html
theHtml =
      String
docType String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Html -> String
forall html. HTML html => html -> String
renderHtmlFragment (html -> Html
forall a. HTML a => a -> Html
mkHtml html
theHtml) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\n"

-- | Outputs indented HTML, with indentation inside elements.
--   This can change the meaning of the HTML document, and
--   is mostly useful for debugging the HTML output.
--   The implementation is inefficient, and you are normally
--   better off using 'showHtml' or 'renderHtml'.
prettyHtmlInternal :: HTML html =>
                      String -- ^ DOCTYPE declaration
                   -> html -> String
prettyHtmlInternal :: String -> html -> String
prettyHtmlInternal String
docType html
theHtml =
    String
docType String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Html -> String
forall html. HTML html => html -> String
prettyHtmlFragment (html -> Html
forall a. HTML a => a -> Html
mkHtml html
theHtml)

-- | Render a piece of HTML without adding a DOCTYPE declaration
--   or root element. Does not add any extra whitespace.
showHtmlFragment :: HTML html => html -> String
showHtmlFragment :: html -> String
showHtmlFragment html
h =
    ((ShowS -> ShowS -> ShowS) -> ShowS -> [ShowS] -> ShowS
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.) ShowS
forall a. a -> a
id ([ShowS] -> ShowS) -> [ShowS] -> ShowS
forall a b. (a -> b) -> a -> b
$ (HtmlElement -> ShowS) -> [HtmlElement] -> [ShowS]
forall a b. (a -> b) -> [a] -> [b]
map HtmlElement -> ShowS
showHtml' ([HtmlElement] -> [ShowS]) -> [HtmlElement] -> [ShowS]
forall a b. (a -> b) -> a -> b
$ Html -> [HtmlElement]
getHtmlElements (Html -> [HtmlElement]) -> Html -> [HtmlElement]
forall a b. (a -> b) -> a -> b
$ html -> Html
forall a. HTML a => a -> Html
toHtml html
h) String
""

-- | Render a piece of indented HTML without adding a DOCTYPE declaration
--   or root element. Only adds whitespace where it does not change
--   the meaning of the document.
renderHtmlFragment :: HTML html => html -> String
renderHtmlFragment :: html -> String
renderHtmlFragment html
h =
    ((ShowS -> ShowS -> ShowS) -> ShowS -> [ShowS] -> ShowS
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.) ShowS
forall a. a -> a
id ([ShowS] -> ShowS) -> [ShowS] -> ShowS
forall a b. (a -> b) -> a -> b
$ (HtmlElement -> ShowS) -> [HtmlElement] -> [ShowS]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> HtmlElement -> ShowS
renderHtml' Int
0) ([HtmlElement] -> [ShowS]) -> [HtmlElement] -> [ShowS]
forall a b. (a -> b) -> a -> b
$ Html -> [HtmlElement]
getHtmlElements (Html -> [HtmlElement]) -> Html -> [HtmlElement]
forall a b. (a -> b) -> a -> b
$ html -> Html
forall a. HTML a => a -> Html
toHtml html
h) String
""

-- | Render a piece of indented HTML without adding a DOCTYPE declaration
--   or a root element.
--   The indentation is done inside elements.
--   This can change the meaning of the HTML document, and
--   is mostly useful for debugging the HTML output.
--   The implementation is inefficient, and you are normally
--   better off using 'showHtmlFragment' or 'renderHtmlFragment'.
prettyHtmlFragment :: HTML html => html -> String
prettyHtmlFragment :: html -> String
prettyHtmlFragment =
    [String] -> String
unlines ([String] -> String) -> (html -> [String]) -> html -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[String]] -> [String])
-> (html -> [[String]]) -> html -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (HtmlElement -> [String]) -> [HtmlElement] -> [[String]]
forall a b. (a -> b) -> [a] -> [b]
map HtmlElement -> [String]
prettyHtml' ([HtmlElement] -> [[String]])
-> (html -> [HtmlElement]) -> html -> [[String]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Html -> [HtmlElement]
getHtmlElements (Html -> [HtmlElement]) -> (html -> Html) -> html -> [HtmlElement]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. html -> Html
forall a. HTML a => a -> Html
toHtml

-- | Show a single HTML element, without adding whitespace.
showHtml' :: HtmlElement -> ShowS
showHtml' :: HtmlElement -> ShowS
showHtml' (HtmlString String
str) = String -> ShowS
forall a. [a] -> [a] -> [a]
(++) String
str
showHtml'(HtmlTag { markupTag :: HtmlElement -> String
markupTag = String
name,
                    markupContent :: HtmlElement -> Html
markupContent = Html
html,
                    markupAttrs :: HtmlElement -> [HtmlAttr]
markupAttrs = [HtmlAttr]
attrs })
    = if Html -> Bool
isNoHtml Html
html Bool -> Bool -> Bool
&& String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem String
name [String]
validHtmlITags
      then Bool -> String -> [HtmlAttr] -> String -> ShowS
renderTag Bool
True String
name [HtmlAttr]
attrs String
""
      else (Bool -> String -> [HtmlAttr] -> String -> ShowS
renderTag Bool
False String
name [HtmlAttr]
attrs String
""
            ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ShowS -> ShowS -> ShowS) -> ShowS -> [ShowS] -> ShowS
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.) ShowS
forall a. a -> a
id ((HtmlElement -> ShowS) -> [HtmlElement] -> [ShowS]
forall a b. (a -> b) -> [a] -> [b]
map HtmlElement -> ShowS
showHtml' (Html -> [HtmlElement]
getHtmlElements Html
html))
            ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String -> ShowS
renderEndTag String
name String
"")

renderHtml' :: Int -> HtmlElement -> ShowS
renderHtml' :: Int -> HtmlElement -> ShowS
renderHtml' Int
_ (HtmlString String
str) = String -> ShowS
forall a. [a] -> [a] -> [a]
(++) String
str
renderHtml' Int
n (HtmlTag
              { markupTag :: HtmlElement -> String
markupTag = String
name,
                markupContent :: HtmlElement -> Html
markupContent = Html
html,
                markupAttrs :: HtmlElement -> [HtmlAttr]
markupAttrs = [HtmlAttr]
attrs })
      = if Html -> Bool
isNoHtml Html
html Bool -> Bool -> Bool
&& String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem String
name [String]
validHtmlITags
        then Bool -> String -> [HtmlAttr] -> String -> ShowS
renderTag Bool
True String
name [HtmlAttr]
attrs (Int -> String
nl Int
n)
        else (Bool -> String -> [HtmlAttr] -> String -> ShowS
renderTag Bool
False String
name [HtmlAttr]
attrs (Int -> String
nl Int
n)
             ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ShowS -> ShowS -> ShowS) -> ShowS -> [ShowS] -> ShowS
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.) ShowS
forall a. a -> a
id ((HtmlElement -> ShowS) -> [HtmlElement] -> [ShowS]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> HtmlElement -> ShowS
renderHtml' (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
2)) (Html -> [HtmlElement]
getHtmlElements Html
html))
             ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String -> ShowS
renderEndTag String
name (Int -> String
nl Int
n))
    where
      nl :: Int -> String
nl Int
n' = String
"\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
n' Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
8) Char
'\t'
              String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
n' Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
8) Char
' '


prettyHtml' :: HtmlElement -> [String]
prettyHtml' :: HtmlElement -> [String]
prettyHtml' (HtmlString String
str) = [String
str]
prettyHtml' (HtmlTag
              { markupTag :: HtmlElement -> String
markupTag = String
name,
                markupContent :: HtmlElement -> Html
markupContent = Html
html,
                markupAttrs :: HtmlElement -> [HtmlAttr]
markupAttrs = [HtmlAttr]
attrs })
      = if Html -> Bool
isNoHtml Html
html Bool -> Bool -> Bool
&& String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem String
name [String]
validHtmlITags
        then
         [ShowS
rmNL (Bool -> String -> [HtmlAttr] -> String -> ShowS
renderTag Bool
True String
name [HtmlAttr]
attrs String
"" String
"")]
        else
         [ShowS
rmNL (Bool -> String -> [HtmlAttr] -> String -> ShowS
renderTag Bool
False String
name [HtmlAttr]
attrs String
"" String
"")] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++
          [String] -> [String]
shift ([[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ((HtmlElement -> [String]) -> [HtmlElement] -> [[String]]
forall a b. (a -> b) -> [a] -> [b]
map HtmlElement -> [String]
prettyHtml' (Html -> [HtmlElement]
getHtmlElements Html
html))) [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++
         [ShowS
rmNL (String -> String -> ShowS
renderEndTag String
name String
"" String
"")]
  where
      shift :: [String] -> [String]
shift = ShowS -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\String
x -> String
"   " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
x)
      rmNL :: ShowS
rmNL = (Char -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\n')


-- | Show a start tag
renderTag :: Bool       -- ^ 'True' if the empty tag shorthand should be used
          -> String     -- ^ Tag name
          -> [HtmlAttr] -- ^ Attributes
          -> String     -- ^ Whitespace to add after attributes
          -> ShowS
renderTag :: Bool -> String -> [HtmlAttr] -> String -> ShowS
renderTag Bool
empty String
name [HtmlAttr]
attrs String
nl String
r
      = String
"<" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
name String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
shownAttrs String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
nl String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
close String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
r
  where
      close :: String
close = if Bool
empty then String
" />" else String
">"

      shownAttrs :: String
shownAttrs = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [Char
' 'Char -> ShowS
forall a. a -> [a] -> [a]
:HtmlAttr -> String
showPair HtmlAttr
attr | HtmlAttr
attr <- [HtmlAttr]
attrs ]

      showPair :: HtmlAttr -> String
      showPair :: HtmlAttr -> String
showPair (HtmlAttr String
key String
val)
              = String
key String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"=\"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
val  String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\""

-- | Show an end tag
renderEndTag :: String -- ^ Tag name
             -> String -- ^ Whitespace to add after tag name
             -> ShowS
renderEndTag :: String -> String -> ShowS
renderEndTag String
name String
nl String
r = String
"</" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
name String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
nl String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
">" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
r


-- | The names of all elements which can represented using the empty tag
--   short-hand.
validHtmlITags :: [String]
validHtmlITags :: [String]
validHtmlITags = [
                  String
"area",
                  String
"base",
                  String
"basefont",
                  String
"br",
                  String
"col",
                  String
"frame",
                  String
"hr",
                  String
"img",
                  String
"input",
                  String
"isindex",
                  String
"link",
                  String
"meta",
                  String
"param"
                 ]