{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- | Defines operations on html data types.
module Zenacy.HTML.Internal.Oper
  ( htmlNodeIsElem
  , htmlNodeIsText
  , htmlNodeContent
  , htmlNodeContentSet
  , htmlNodeShow
  , htmlNodeFind
  , htmlNodeCount
  , htmlNodeCountM
  , htmlTextSpace
  , htmlTextAppend
  , htmlTextPrepend
  , htmlAttrHasName
  , htmlAttrRename
  , htmlElemAttr
  , htmlElemAttrCount
  , htmlElemAttrFind
  , htmlElemAttrFindName
  , htmlElemAttrApply
  , htmlElemAttrFilter
  , htmlElemAttrMap
  , htmlElemHasAttr
  , htmlElemHasAttrName
  , htmlElemHasAttrVal
  , htmlElemHasAttrValInfix
  , htmlElemAddAttr
  , htmlElemSetAttr
  , htmlElemGetAttr
  , htmlElemAttrRemove
  , htmlElemRemoveAllAttr
  , htmlElemAttrRename
  , htmlElemID
  , htmlElemIDSet
  , htmlElemHasID
  , htmlElemFindID
  , htmlElemClass
  , htmlElemClassSet
  , htmlElemClasses
  , htmlElemClassesSet
  , htmlElemClassesAdd
  , htmlElemClassesRemove
  , htmlElemClassesContains
  , htmlElemStyle
  , htmlElemStyles
  , htmlElemStyleParseURL
  , htmlElemContent
  , htmlElemContentSet
  , htmlElemHasContent
  , htmlElemNodeFirst
  , htmlElemNodeLast
  , htmlElemNodeCount
  , htmlElemName
  , htmlElemHasName
  , htmlElemRename
  , htmlElemFindElem
  , htmlElemFindElemNamed
  , htmlElemHasElem
  , htmlElemHasElemNamed
  , htmlElemContentApply
  , htmlElemContentMap
  , htmlElemContentFilter
  , htmlElemSearch
  , htmlElemText
  , htmlDocHtml
  , htmlDocBody
  , htmlDocHead
  , htmlDocTitle
  , htmlMapElem
  , htmlMapElemM
  , htmlElemCollapse
  , htmlElemCollapseM
  ) where

import Zenacy.HTML.Internal.Core
import Zenacy.HTML.Internal.HTML
import Control.Monad
  ( (>=>)
  )
import Control.Monad.Extra as X
  ( whenJust
  , whenJustM
  , concatMapM
  , ifM
  )
-- import Control.Monad.Identity
--   ( runIdentity
--   )
import Data.Char
  ( isSpace
  )
import Data.Functor.Identity
  ( runIdentity
  )
import Data.List
  ( find
  )
import Data.List.Extra
  ( firstJust
  )
import Data.Map
  ( Map
  )
import qualified Data.Map as Map
  ( empty
  , fromList
  , lookup
  )
import Data.Maybe
  ( listToMaybe
  , isJust
  )
import Data.Monoid
  ( (<>)
  )
import Data.Set
  ( Set
  )
import qualified Data.Set as Set
  ( delete
  , empty
  , fromList
  , insert
  , member
  , notMember
  , toList
  , union
  , unions
  )
import Data.Text
  ( Text
  )
import qualified Data.Text as T
  ( all
  , append
  , breakOn
  , concat
  , drop
  , dropAround
  , empty
  , isInfixOf
  , isPrefixOf
  , null
  , split
  , splitOn
  , strip
  , words
  , unwords
  )
import Data.Tuple.Extra
  ( first
  , second
  )

-- | Determines if a node is an element node.
htmlNodeIsElem :: HTMLNode -> Bool
htmlNodeIsElem :: HTMLNode -> Bool
htmlNodeIsElem HTMLElement {} = Bool
True
htmlNodeIsElem HTMLNode
_ = Bool
False

-- | Determines if a node is a text node.
htmlNodeIsText :: HTMLNode -> Bool
htmlNodeIsText :: HTMLNode -> Bool
htmlNodeIsText HTMLText {} = Bool
True
htmlNodeIsText HTMLNode
_ = Bool
False

-- | Gets the content of a node.
htmlNodeContent :: HTMLNode -> [HTMLNode]
htmlNodeContent :: HTMLNode -> [HTMLNode]
htmlNodeContent (HTMLDocument Text
_ [HTMLNode]
c) = [HTMLNode]
c
htmlNodeContent (HTMLFragment Text
_ [HTMLNode]
c) = [HTMLNode]
c
htmlNodeContent (HTMLElement Text
_ HTMLNamespace
_ [HTMLAttr]
_ [HTMLNode]
c) = [HTMLNode]
c
htmlNodeContent HTMLNode
_ = []

-- | Sets the content of a node.
htmlNodeContentSet :: [HTMLNode] -> HTMLNode -> HTMLNode
htmlNodeContentSet :: [HTMLNode] -> HTMLNode -> HTMLNode
htmlNodeContentSet [HTMLNode]
x (HTMLDocument Text
n [HTMLNode]
c) = Text -> [HTMLNode] -> HTMLNode
HTMLDocument Text
n [HTMLNode]
x
htmlNodeContentSet [HTMLNode]
x (HTMLFragment Text
n [HTMLNode]
c) = Text -> [HTMLNode] -> HTMLNode
HTMLFragment Text
n [HTMLNode]
x
htmlNodeContentSet [HTMLNode]
x (HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
x
htmlNodeContentSet [HTMLNode]
x HTMLNode
y = HTMLNode
y

-- | Shows the node without its content.
htmlNodeShow :: HTMLNode -> String
htmlNodeShow :: HTMLNode -> String
htmlNodeShow = forall a. Show a => a -> String
show forall b c a. (b -> c) -> (a -> b) -> a -> c
. [HTMLNode] -> HTMLNode -> HTMLNode
htmlNodeContentSet []

-- | Finds a child node using a predicate.
htmlNodeFind :: (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlNodeFind :: (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlNodeFind HTMLNode -> Bool
p HTMLNode
x = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find HTMLNode -> Bool
p forall a b. (a -> b) -> a -> b
$ HTMLNode -> [HTMLNode]
htmlNodeContent HTMLNode
x

-- | Counts the number of nodes matching a predicate.
htmlNodeCount :: (HTMLNode -> Bool) -> HTMLNode -> Int
htmlNodeCount :: (HTMLNode -> Bool) -> HTMLNode -> Int
htmlNodeCount HTMLNode -> Bool
f = forall a. Identity a -> a
runIdentity forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *).
Monad m =>
(HTMLNode -> m Bool) -> HTMLNode -> m Int
htmlNodeCountM (forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> Bool
f)

-- | Counts the number of nodes matching a predicate.
htmlNodeCountM :: Monad m => (HTMLNode -> m Bool) -> HTMLNode -> m Int
htmlNodeCountM :: forall (m :: * -> *).
Monad m =>
(HTMLNode -> m Bool) -> HTMLNode -> m Int
htmlNodeCountM HTMLNode -> m Bool
f HTMLNode
x = do
  Int
n <- forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (forall (m :: * -> *).
Monad m =>
(HTMLNode -> m Bool) -> HTMLNode -> m Int
htmlNodeCountM HTMLNode -> m Bool
f) (HTMLNode -> [HTMLNode]
htmlNodeContent HTMLNode
x)
  forall (m :: * -> *) a. Monad m => m Bool -> m a -> m a -> m a
ifM (HTMLNode -> m Bool
f HTMLNode
x) (forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Int
1 forall a. Num a => a -> a -> a
+ Int
n) (forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
n)

-- | Determines if a node is a text node containing only whitespace.
htmlTextSpace :: HTMLNode -> Bool
htmlTextSpace :: HTMLNode -> Bool
htmlTextSpace (HTMLText Text
x) = (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isSpace Text
x
htmlTextSpace HTMLNode
_ = Bool
False

-- | Appends text to a text node.
htmlTextAppend :: Text -> HTMLNode -> HTMLNode
htmlTextAppend :: Text -> HTMLNode -> HTMLNode
htmlTextAppend Text
a (HTMLText Text
x) = Text -> HTMLNode
HTMLText forall a b. (a -> b) -> a -> b
$ Text -> Text -> Text
T.append Text
x Text
a
htmlTextAppend Text
a HTMLNode
x = HTMLNode
x

-- | Prepends text to a text node.
htmlTextPrepend :: Text -> HTMLNode -> HTMLNode
htmlTextPrepend :: Text -> HTMLNode -> HTMLNode
htmlTextPrepend Text
a (HTMLText Text
x) = Text -> HTMLNode
HTMLText forall a b. (a -> b) -> a -> b
$ Text -> Text -> Text
T.append Text
a Text
x
htmlTextPrepend Text
a HTMLNode
x = HTMLNode
x

-- | A predicate for checking attribute names.
htmlAttrHasName :: Text -> HTMLAttr -> Bool
htmlAttrHasName :: Text -> HTMLAttr -> Bool
htmlAttrHasName Text
x HTMLAttr
a = Text
x forall a. Eq a => a -> a -> Bool
== HTMLAttr -> Text
htmlAttrName HTMLAttr
a

-- | Renames an attribute.
htmlAttrRename :: Text -> HTMLAttr -> HTMLAttr
htmlAttrRename :: Text -> HTMLAttr -> HTMLAttr
htmlAttrRename Text
x (HTMLAttr Text
n Text
v HTMLAttrNamespace
s) = Text -> Text -> HTMLAttrNamespace -> HTMLAttr
HTMLAttr Text
x Text
v HTMLAttrNamespace
s

-- | Gets the attributes for an element.
htmlElemAttr :: HTMLNode -> [HTMLAttr]
htmlElemAttr :: HTMLNode -> [HTMLAttr]
htmlElemAttr (HTMLElement Text
_ HTMLNamespace
_ [HTMLAttr]
a [HTMLNode]
_) = [HTMLAttr]
a
htmlElemAttr HTMLNode
_ = []

-- | Gets the number of attributes for an element.
htmlElemAttrCount :: HTMLNode -> Int
htmlElemAttrCount :: HTMLNode -> Int
htmlElemAttrCount = forall (t :: * -> *) a. Foldable t => t a -> Int
length forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> [HTMLAttr]
htmlElemAttr

-- | Finds an attribute for an element.
htmlElemAttrFind :: (HTMLAttr -> Bool) -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFind :: (HTMLAttr -> Bool) -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFind HTMLAttr -> Bool
f (HTMLElement Text
_ HTMLNamespace
_ [HTMLAttr]
a [HTMLNode]
_) = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find HTMLAttr -> Bool
f [HTMLAttr]
a
htmlElemAttrFind HTMLAttr -> Bool
_ HTMLNode
_ = forall a. Maybe a
Nothing

-- | Finds an attribute by name for an element.
htmlElemAttrFindName :: Text -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFindName :: Text -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFindName Text
x = (HTMLAttr -> Bool) -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFind forall a b. (a -> b) -> a -> b
$ Text -> HTMLAttr -> Bool
htmlAttrHasName Text
x

-- | Applies a function to the attributes for an element.
htmlElemAttrApply :: ([HTMLAttr] -> [HTMLAttr]) -> HTMLNode -> HTMLNode
htmlElemAttrApply :: ([HTMLAttr] -> [HTMLAttr]) -> HTMLNode -> HTMLNode
htmlElemAttrApply [HTMLAttr] -> [HTMLAttr]
f (HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s ([HTMLAttr] -> [HTMLAttr]
f [HTMLAttr]
a) [HTMLNode]
c
htmlElemAttrApply [HTMLAttr] -> [HTMLAttr]
_ HTMLNode
x = HTMLNode
x

-- | Filters the attributes for an element.
htmlElemAttrFilter :: (HTMLAttr -> Bool) -> HTMLNode -> HTMLNode
htmlElemAttrFilter :: (HTMLAttr -> Bool) -> HTMLNode -> HTMLNode
htmlElemAttrFilter HTMLAttr -> Bool
f = ([HTMLAttr] -> [HTMLAttr]) -> HTMLNode -> HTMLNode
htmlElemAttrApply forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter HTMLAttr -> Bool
f

-- | Maps an endofunctor over an element attributes.
htmlElemAttrMap :: (HTMLAttr -> HTMLAttr) -> HTMLNode -> HTMLNode
htmlElemAttrMap :: (HTMLAttr -> HTMLAttr) -> HTMLNode -> HTMLNode
htmlElemAttrMap HTMLAttr -> HTMLAttr
f = ([HTMLAttr] -> [HTMLAttr]) -> HTMLNode -> HTMLNode
htmlElemAttrApply forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map HTMLAttr -> HTMLAttr
f

-- | Determines if the element has attributes.
htmlElemHasAttr :: HTMLNode -> Bool
htmlElemHasAttr :: HTMLNode -> Bool
htmlElemHasAttr HTMLNode
x = HTMLNode -> Int
htmlElemAttrCount HTMLNode
x forall a. Ord a => a -> a -> Bool
> Int
0

-- | Determines if an element has an attribute.
htmlElemHasAttrName :: Text -> HTMLNode -> Bool
htmlElemHasAttrName :: Text -> HTMLNode -> Bool
htmlElemHasAttrName Text
x = forall a. Maybe a -> Bool
isJust forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFindName Text
x

-- | Determines if an element has an attribute value.
htmlElemHasAttrVal :: Text -> Text -> HTMLNode -> Bool
htmlElemHasAttrVal :: Text -> Text -> HTMLNode -> Bool
htmlElemHasAttrVal Text
x Text
y HTMLNode
z =
  forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (\HTMLAttr
a -> Text
y forall a. Eq a => a -> a -> Bool
== HTMLAttr -> Text
htmlAttrVal HTMLAttr
a) forall a b. (a -> b) -> a -> b
$ Text -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFindName Text
x HTMLNode
z

-- | Determines if an element has part of an attribute value.
htmlElemHasAttrValInfix :: Text -> Text -> HTMLNode -> Bool
htmlElemHasAttrValInfix :: Text -> Text -> HTMLNode -> Bool
htmlElemHasAttrValInfix Text
x Text
y HTMLNode
z =
  forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (\HTMLAttr
a -> Text
y Text -> Text -> Bool
`T.isInfixOf` HTMLAttr -> Text
htmlAttrVal HTMLAttr
a) forall a b. (a -> b) -> a -> b
$ Text -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFindName Text
x HTMLNode
z

-- | Adds an attribute to an element.
htmlElemAddAttr :: HTMLAttr -> HTMLNode -> HTMLNode
htmlElemAddAttr :: HTMLAttr -> HTMLNode -> HTMLNode
htmlElemAddAttr HTMLAttr
x (HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s ([HTMLAttr]
a forall a. Semigroup a => a -> a -> a
<> [HTMLAttr
x]) [HTMLNode]
c
htmlElemAddAttr HTMLAttr
x HTMLNode
y = HTMLNode
y

-- | Sets an attribute value.
htmlElemSetAttr :: Text -> Text -> HTMLNode -> HTMLNode
htmlElemSetAttr :: Text -> Text -> HTMLNode -> HTMLNode
htmlElemSetAttr Text
x Text
v HTMLNode
n =
  if Text -> HTMLNode -> Bool
htmlElemHasAttrName Text
x HTMLNode
n
     then (HTMLAttr -> HTMLAttr) -> HTMLNode -> HTMLNode
htmlElemAttrMap HTMLAttr -> HTMLAttr
f HTMLNode
n
     else HTMLAttr -> HTMLNode -> HTMLNode
htmlElemAddAttr (Text -> Text -> HTMLAttr
htmlAttr Text
x Text
v) HTMLNode
n
  where
    f :: HTMLAttr -> HTMLAttr
f a :: HTMLAttr
a@(HTMLAttr Text
an Text
av HTMLAttrNamespace
as) =
      if Text
an forall a. Eq a => a -> a -> Bool
== Text
x then (Text -> Text -> HTMLAttrNamespace -> HTMLAttr
HTMLAttr Text
an Text
v HTMLAttrNamespace
as) else HTMLAttr
a

-- | Gets an attribute value.
htmlElemGetAttr :: Text -> HTMLNode -> Maybe Text
htmlElemGetAttr :: Text -> HTMLNode -> Maybe Text
htmlElemGetAttr Text
x HTMLNode
n = HTMLAttr -> Text
htmlAttrVal forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> HTMLNode -> Maybe HTMLAttr
htmlElemAttrFindName Text
x HTMLNode
n

-- | Removes an attribute from an element.
htmlElemAttrRemove :: Text -> HTMLNode -> HTMLNode
htmlElemAttrRemove :: Text -> HTMLNode -> HTMLNode
htmlElemAttrRemove Text
x (HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a' [HTMLNode]
c
  where a' :: [HTMLAttr]
a' = forall a. (a -> Bool) -> [a] -> [a]
filter (\HTMLAttr
y -> HTMLAttr -> Text
htmlAttrName HTMLAttr
y forall a. Eq a => a -> a -> Bool
/= Text
x) [HTMLAttr]
a
htmlElemAttrRemove Text
x HTMLNode
y = HTMLNode
y

-- | Removes all attributes from an element.
htmlElemRemoveAllAttr :: HTMLNode -> HTMLNode
htmlElemRemoveAllAttr :: HTMLNode -> HTMLNode
htmlElemRemoveAllAttr (HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s [] [HTMLNode]
c
htmlElemRemoveAllAttr HTMLNode
x = HTMLNode
x

-- | Renames an attribute for an element.
htmlElemAttrRename :: Text -> Text -> HTMLNode -> HTMLNode
htmlElemAttrRename :: Text -> Text -> HTMLNode -> HTMLNode
htmlElemAttrRename Text
old Text
new = (HTMLAttr -> HTMLAttr) -> HTMLNode -> HTMLNode
htmlElemAttrMap HTMLAttr -> HTMLAttr
rename
  where
    rename :: HTMLAttr -> HTMLAttr
rename HTMLAttr
x =
      if Text -> HTMLAttr -> Bool
htmlAttrHasName Text
old HTMLAttr
x
         then Text -> HTMLAttr -> HTMLAttr
htmlAttrRename Text
new HTMLAttr
x
         else HTMLAttr
x

-- | Gets the id attribute for an element.
htmlElemID :: HTMLNode -> Maybe Text
htmlElemID :: HTMLNode -> Maybe Text
htmlElemID = Text -> HTMLNode -> Maybe Text
htmlElemGetAttr Text
"id"

-- | Sets the id attribute for an element.
htmlElemIDSet :: Text -> HTMLNode -> HTMLNode
htmlElemIDSet :: Text -> HTMLNode -> HTMLNode
htmlElemIDSet = Text -> Text -> HTMLNode -> HTMLNode
htmlElemSetAttr Text
"id"

-- | Determines if an element has an id.
htmlElemHasID :: Text -> HTMLNode -> Bool
htmlElemHasID :: Text -> HTMLNode -> Bool
htmlElemHasID Text
x HTMLNode
y = HTMLNode -> Maybe Text
htmlElemID HTMLNode
y forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just Text
x

-- | Searches for an element with an id.
htmlElemFindID :: Text -> HTMLNode -> Maybe HTMLNode
htmlElemFindID :: Text -> HTMLNode -> Maybe HTMLNode
htmlElemFindID Text
x = (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlElemSearch forall a b. (a -> b) -> a -> b
$ Text -> HTMLNode -> Bool
htmlElemHasID Text
x

-- | Gets the id attribute for an element.
htmlElemClass :: HTMLNode -> Maybe Text
htmlElemClass :: HTMLNode -> Maybe Text
htmlElemClass = Text -> HTMLNode -> Maybe Text
htmlElemGetAttr Text
"class"

-- | Sets the class attribute for an element.
htmlElemClassSet :: Text -> HTMLNode -> HTMLNode
htmlElemClassSet :: Text -> HTMLNode -> HTMLNode
htmlElemClassSet = Text -> Text -> HTMLNode -> HTMLNode
htmlElemSetAttr Text
"class"

-- | Gets the element classes.
htmlElemClasses :: HTMLNode -> Set Text
htmlElemClasses :: HTMLNode -> Set Text
htmlElemClasses = forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. Set a
Set.empty (forall a. Ord a => [a] -> Set a
Set.fromList forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.words) forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> Maybe Text
htmlElemClass

-- | Sets the element classes.
htmlElemClassesSet :: Set Text -> HTMLNode -> HTMLNode
htmlElemClassesSet :: Set Text -> HTMLNode -> HTMLNode
htmlElemClassesSet Set Text
s = Text -> HTMLNode -> HTMLNode
htmlElemClassSet ([Text] -> Text
T.unwords forall a b. (a -> b) -> a -> b
$ forall a. Set a -> [a]
Set.toList Set Text
s)

-- | Adds the class to the element's classes.
htmlElemClassesAdd :: Text -> HTMLNode -> HTMLNode
htmlElemClassesAdd :: Text -> HTMLNode -> HTMLNode
htmlElemClassesAdd Text
c HTMLNode
x =
  Set Text -> HTMLNode -> HTMLNode
htmlElemClassesSet (forall a. Ord a => a -> Set a -> Set a
Set.insert Text
c forall a b. (a -> b) -> a -> b
$ HTMLNode -> Set Text
htmlElemClasses HTMLNode
x) HTMLNode
x

-- | Removes a class from the element's classes.
htmlElemClassesRemove :: Text -> HTMLNode -> HTMLNode
htmlElemClassesRemove :: Text -> HTMLNode -> HTMLNode
htmlElemClassesRemove Text
c HTMLNode
x =
  Set Text -> HTMLNode -> HTMLNode
htmlElemClassesSet (forall a. Ord a => a -> Set a -> Set a
Set.delete Text
c forall a b. (a -> b) -> a -> b
$ HTMLNode -> Set Text
htmlElemClasses HTMLNode
x) HTMLNode
x

-- | Determines if the element contains a class.
htmlElemClassesContains :: Text -> HTMLNode -> Bool
htmlElemClassesContains :: Text -> HTMLNode -> Bool
htmlElemClassesContains Text
c = forall a. Ord a => a -> Set a -> Bool
Set.member Text
c forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> Set Text
htmlElemClasses

-- | Gets the style attribute for an element.
htmlElemStyle :: HTMLNode -> Maybe Text
htmlElemStyle :: HTMLNode -> Maybe Text
htmlElemStyle = Text -> HTMLNode -> Maybe Text
htmlElemGetAttr Text
"style"

-- | Gets the styles for an element.
htmlElemStyles :: HTMLNode -> Map Text Text
htmlElemStyles :: HTMLNode -> Map Text Text
htmlElemStyles =
  forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall k a. Map k a
Map.empty Text -> Map Text Text
parse forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> Maybe Text
htmlElemStyle
  where
    parse :: Text -> Map Text Text
parse =
      ( forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
      forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map
        ( forall a a' b. (a -> a') -> (a, b) -> (a', b)
first Text -> Text
T.strip
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b b' a. (b -> b') -> (a, b) -> (a, b')
second Text -> Text
T.strip
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b b' a. (b -> b') -> (a, b) -> (a, b')
second (Int -> Text -> Text
T.drop Int
1)
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> (Text, Text)
T.breakOn Text
":"
        )
      forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Bool
T.null)
      forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Text -> Text
T.strip
      forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> [Text]
T.splitOn Text
";"
      )

-- | Parses and returns a url style value.
htmlElemStyleParseURL :: Text -> Maybe Text
htmlElemStyleParseURL :: Text -> Maybe Text
htmlElemStyleParseURL Text
x
  | Text
"url" Text -> Text -> Bool
`T.isPrefixOf` Text
x =
      ( Text -> Text
T.strip
      forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> Text -> Text
T.dropAround (forall a. Eq a => a -> a -> Bool
==Char
'\'')
      -- Only a stylesheet can have a double quote, but we check for it anyway.
      forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> Text -> Text
T.dropAround (forall a. Eq a => a -> a -> Bool
==Char
'\"')
      forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
T.strip
      ) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Text -> Text -> Maybe Text
textExtract Text
"(" Text
")" Text
x
  | Bool
otherwise = forall a. Maybe a
Nothing

-- | Gets the children for the element if the node is an element.
htmlElemContent :: HTMLNode -> [HTMLNode]
htmlElemContent :: HTMLNode -> [HTMLNode]
htmlElemContent (HTMLElement Text
_ HTMLNamespace
_ [HTMLAttr]
_ [HTMLNode]
c) = [HTMLNode]
c
htmlElemContent HTMLNode
_ = []

-- | Sets the content for an element.
htmlElemContentSet :: [HTMLNode] -> HTMLNode -> HTMLNode
htmlElemContentSet :: [HTMLNode] -> HTMLNode -> HTMLNode
htmlElemContentSet [HTMLNode]
x (HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
x
htmlElemContentSet [HTMLNode]
x HTMLNode
y = HTMLNode
y

-- | Determines if the element has children.
htmlElemHasContent :: HTMLNode -> Bool
htmlElemHasContent :: HTMLNode -> Bool
htmlElemHasContent (HTMLElement Text
_ HTMLNamespace
_ [HTMLAttr]
_ []) = Bool
False
htmlElemHasContent (HTMLElement Text
_ HTMLNamespace
_ [HTMLAttr]
_ (HTMLNode
x:[HTMLNode]
xs)) = Bool
True
htmlElemHasContent HTMLNode
_ = Bool
False

-- | Gets the first child for an element.
htmlElemNodeFirst :: HTMLNode -> Maybe HTMLNode
htmlElemNodeFirst :: HTMLNode -> Maybe HTMLNode
htmlElemNodeFirst = forall a. [a] -> Maybe a
listToMaybe forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> [HTMLNode]
htmlElemContent

-- | Gets the last child for an element.
htmlElemNodeLast :: HTMLNode -> Maybe HTMLNode
htmlElemNodeLast :: HTMLNode -> Maybe HTMLNode
htmlElemNodeLast =  forall a. [a] -> Maybe a
listToMaybe forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> [HTMLNode]
htmlElemContent

-- | Gets the number of children for an element.
htmlElemNodeCount :: HTMLNode -> Int
htmlElemNodeCount :: HTMLNode -> Int
htmlElemNodeCount = forall (t :: * -> *) a. Foldable t => t a -> Int
length forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> [HTMLNode]
htmlElemContent

-- | Gets the name of an element.
htmlElemName :: HTMLNode -> Text
htmlElemName :: HTMLNode -> Text
htmlElemName (HTMLElement Text
n HTMLNamespace
_ [HTMLAttr]
_ [HTMLNode]
_) = Text
n
htmlElemName HTMLNode
_ = Text
T.empty

-- | Checks if the name of an element matches a specified name.
htmlElemHasName :: Text -> HTMLNode -> Bool
htmlElemHasName :: Text -> HTMLNode -> Bool
htmlElemHasName Text
x HTMLNode
y = HTMLNode -> Text
htmlElemName HTMLNode
y forall a. Eq a => a -> a -> Bool
== Text
x

-- | Sets the name of an element.
htmlElemRename :: Text -> HTMLNode -> HTMLNode
htmlElemRename :: Text -> HTMLNode -> HTMLNode
htmlElemRename Text
n (HTMLElement Text
_ HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c
htmlElemRename Text
n HTMLNode
x = HTMLNode
x

-- | Finds a child element using a predicate.
htmlElemFindElem :: (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlElemFindElem :: (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlElemFindElem HTMLNode -> Bool
p (HTMLElement Text
_ HTMLNamespace
_ [HTMLAttr]
_ [HTMLNode]
c) = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find HTMLNode -> Bool
p [HTMLNode]
c
htmlElemFindElem HTMLNode -> Bool
_ HTMLNode
_ = forall a. Maybe a
Nothing

-- | Finds a child element with a specified name.
htmlElemFindElemNamed :: Text -> HTMLNode -> Maybe HTMLNode
htmlElemFindElemNamed :: Text -> HTMLNode -> Maybe HTMLNode
htmlElemFindElemNamed Text
x = (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlElemFindElem forall a b. (a -> b) -> a -> b
$ Text -> HTMLNode -> Bool
htmlElemHasName Text
x

-- | Determines if an element has a child.
htmlElemHasElem :: (HTMLNode -> Bool) -> HTMLNode -> Bool
htmlElemHasElem :: (HTMLNode -> Bool) -> HTMLNode -> Bool
htmlElemHasElem HTMLNode -> Bool
p = forall a. Maybe a -> Bool
isJust forall b c a. (b -> c) -> (a -> b) -> a -> c
. (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlElemFindElem HTMLNode -> Bool
p

-- | Determines if an element has a child.
htmlElemHasElemNamed :: Text -> HTMLNode -> Bool
htmlElemHasElemNamed :: Text -> HTMLNode -> Bool
htmlElemHasElemNamed Text
x = forall a. Maybe a -> Bool
isJust forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> HTMLNode -> Maybe HTMLNode
htmlElemFindElemNamed Text
x

-- | Modifies an elements children by applying a function.
htmlElemContentApply :: ([HTMLNode] -> [HTMLNode]) -> HTMLNode -> HTMLNode
htmlElemContentApply :: ([HTMLNode] -> [HTMLNode]) -> HTMLNode -> HTMLNode
htmlElemContentApply [HTMLNode] -> [HTMLNode]
f (HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a forall a b. (a -> b) -> a -> b
$ [HTMLNode] -> [HTMLNode]
f [HTMLNode]
c
htmlElemContentApply [HTMLNode] -> [HTMLNode]
_ HTMLNode
x = HTMLNode
x

-- | Modifies an elements children by mapping a function over them.
htmlElemContentMap :: (HTMLNode -> HTMLNode) -> HTMLNode -> HTMLNode
htmlElemContentMap :: (HTMLNode -> HTMLNode) -> HTMLNode -> HTMLNode
htmlElemContentMap HTMLNode -> HTMLNode
f = ([HTMLNode] -> [HTMLNode]) -> HTMLNode -> HTMLNode
htmlElemContentApply forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map HTMLNode -> HTMLNode
f

-- | Modifies an elements children by filtering them.
htmlElemContentFilter :: (HTMLNode -> Bool) -> HTMLNode -> HTMLNode
htmlElemContentFilter :: (HTMLNode -> Bool) -> HTMLNode -> HTMLNode
htmlElemContentFilter HTMLNode -> Bool
f = ([HTMLNode] -> [HTMLNode]) -> HTMLNode -> HTMLNode
htmlElemContentApply forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter HTMLNode -> Bool
f

-- | Finds an element using a depth-first search.
htmlElemSearch :: (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlElemSearch :: (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlElemSearch HTMLNode -> Bool
f HTMLNode
x = case HTMLNode
x of
  HTMLElement Text
_ HTMLNamespace
_ [HTMLAttr]
_ [HTMLNode]
c ->
    if HTMLNode -> Bool
f HTMLNode
x then forall a. a -> Maybe a
Just HTMLNode
x else forall a b. (a -> Maybe b) -> [a] -> Maybe b
firstJust ((HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlElemSearch HTMLNode -> Bool
f) [HTMLNode]
c
  HTMLNode
_otherwise -> forall a. Maybe a
Nothing

-- | Gets the text content for an element.
htmlElemText :: HTMLNode -> Maybe Text
htmlElemText :: HTMLNode -> Maybe Text
htmlElemText (HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) =
  case forall a. (a -> Bool) -> [a] -> [a]
filter HTMLNode -> Bool
htmlNodeIsText [HTMLNode]
c of
    a :: [HTMLNode]
a@(HTMLNode
x:[HTMLNode]
xs) -> forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Text] -> Text
T.concat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map HTMLNode -> Text
htmlTextData forall a b. (a -> b) -> a -> b
$ [HTMLNode]
a
    [] -> forall a. Maybe a
Nothing
htmlElemText HTMLNode
_ = forall a. Maybe a
Nothing

-- | Finds the html element given a document.
htmlDocHtml :: HTMLNode -> Maybe HTMLNode
htmlDocHtml :: HTMLNode -> Maybe HTMLNode
htmlDocHtml = (HTMLNode -> Bool) -> HTMLNode -> Maybe HTMLNode
htmlNodeFind forall a b. (a -> b) -> a -> b
$ Text -> HTMLNode -> Bool
htmlElemHasName Text
"html"

-- | Finds the body element given a document.
htmlDocBody :: HTMLNode -> Maybe HTMLNode
htmlDocBody :: HTMLNode -> Maybe HTMLNode
htmlDocBody = HTMLNode -> Maybe HTMLNode
htmlDocHtml forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Text -> HTMLNode -> Maybe HTMLNode
htmlElemFindElemNamed Text
"body"

-- | Finds the head element given a document.
htmlDocHead :: HTMLNode -> Maybe HTMLNode
htmlDocHead :: HTMLNode -> Maybe HTMLNode
htmlDocHead = HTMLNode -> Maybe HTMLNode
htmlDocHtml forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Text -> HTMLNode -> Maybe HTMLNode
htmlElemFindElemNamed Text
"head"

-- | Finds the title for a document.
htmlDocTitle :: HTMLNode -> Maybe Text
htmlDocTitle :: HTMLNode -> Maybe Text
htmlDocTitle = HTMLNode -> Maybe HTMLNode
htmlDocHead
  forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Text -> HTMLNode -> Maybe HTMLNode
htmlElemFindElemNamed Text
"title"
  forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> HTMLNode -> Maybe Text
htmlElemText

-- | Maps a function over all the elements defined by a node.
htmlMapElem :: (HTMLNode -> HTMLNode) -> HTMLNode -> HTMLNode
htmlMapElem :: (HTMLNode -> HTMLNode) -> HTMLNode -> HTMLNode
htmlMapElem HTMLNode -> HTMLNode
f = forall a. Identity a -> a
runIdentity forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *).
Monad m =>
(HTMLNode -> m HTMLNode) -> HTMLNode -> m HTMLNode
htmlMapElemM (forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> HTMLNode
f)

-- | Maps a function over all the elements defined by a node.
htmlMapElemM :: Monad m => (HTMLNode -> m HTMLNode) -> HTMLNode -> m HTMLNode

-- htmlMapElemM f x@(HTMLElement {}) = do
--   HTMLElement n s a c <- f x
--   HTMLElement n s a <$> mapM (htmlMapElemM f) c
-- htmlMapElemM f x = pure x

htmlMapElemM :: forall (m :: * -> *).
Monad m =>
(HTMLNode -> m HTMLNode) -> HTMLNode -> m HTMLNode
htmlMapElemM HTMLNode -> m HTMLNode
f HTMLNode
x =
  case HTMLNode
x of
    HTMLElement {} -> do
      HTMLNode
a <- HTMLNode -> m HTMLNode
f HTMLNode
x
      case HTMLNode
a of
        HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c ->
          Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (forall (m :: * -> *).
Monad m =>
(HTMLNode -> m HTMLNode) -> HTMLNode -> m HTMLNode
htmlMapElemM HTMLNode -> m HTMLNode
f) [HTMLNode]
c
        HTMLNode
_otherwise ->
          forall (f :: * -> *) a. Applicative f => a -> f a
pure HTMLNode
a
    HTMLNode
_otherwise ->
      forall (f :: * -> *) a. Applicative f => a -> f a
pure HTMLNode
x

-- | Collapses a tree of elements based on a predicate.
htmlElemCollapse :: (HTMLNode -> Bool) -> HTMLNode -> [HTMLNode]
htmlElemCollapse :: (HTMLNode -> Bool) -> HTMLNode -> [HTMLNode]
htmlElemCollapse HTMLNode -> Bool
f = forall a. Identity a -> a
runIdentity forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *).
Monad m =>
(HTMLNode -> m Bool) -> HTMLNode -> m [HTMLNode]
htmlElemCollapseM (forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. HTMLNode -> Bool
f)

-- | Collapses a tree of elements based on a predicate.
htmlElemCollapseM :: Monad m => (HTMLNode -> m Bool) -> HTMLNode -> m [HTMLNode]
htmlElemCollapseM :: forall (m :: * -> *).
Monad m =>
(HTMLNode -> m Bool) -> HTMLNode -> m [HTMLNode]
htmlElemCollapseM HTMLNode -> m Bool
f x :: HTMLNode
x@(HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c) = do
  [HTMLNode]
c' <- forall (m :: * -> *) a b. Monad m => (a -> m [b]) -> [a] -> m [b]
concatMapM (forall (m :: * -> *).
Monad m =>
(HTMLNode -> m Bool) -> HTMLNode -> m [HTMLNode]
htmlElemCollapseM HTMLNode -> m Bool
f) [HTMLNode]
c
  forall (m :: * -> *) a. Monad m => m Bool -> m a -> m a -> m a
ifM (HTMLNode -> m Bool
f HTMLNode
x) (forall (f :: * -> *) a. Applicative f => a -> f a
pure [HTMLNode]
c') forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. Applicative f => a -> f a
pure [ Text -> HTMLNamespace -> [HTMLAttr] -> [HTMLNode] -> HTMLNode
HTMLElement Text
n HTMLNamespace
s [HTMLAttr]
a [HTMLNode]
c' ]
htmlElemCollapseM HTMLNode -> m Bool
_ HTMLNode
x = forall (f :: * -> *) a. Applicative f => a -> f a
pure [HTMLNode
x]