|
Packit |
247f4e |
-- | Combinators to match tags. Some people prefer to use @(~==)@ from
|
|
Packit |
247f4e |
-- "Text.HTML.TagSoup", others prefer these more structured combinators.
|
|
Packit |
247f4e |
-- Which you use is personal preference.
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- The functions below offer maximum flexibility for matching tags.
|
|
Packit |
247f4e |
-- Using 'tagOpen', for example, you can match all links or buttons that have the "btn" class.
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- For simple uses cases—like matching all comment tags, or matching opening @\@ tags,
|
|
Packit |
247f4e |
-- use the tag identification functions in "Text.HTML.TagSoup#tag-identification".
|
|
Packit |
247f4e |
module Text.HTML.TagSoup.Match where
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
import Text.HTML.TagSoup.Type (Tag(..), Attribute)
|
|
Packit |
247f4e |
import Data.List (tails)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- * Matching Tags
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Match an opening tag
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Matching an opening @\@ tag with a @"btn"@ class:/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> let tag = TagOpen "a" [("class", "btn")]
|
|
Packit |
247f4e |
-- >>> tagOpen (== "a") (\attrs -> any (== ("class", "btn")) attrs) tag
|
|
Packit |
247f4e |
-- True
|
|
Packit |
247f4e |
tagOpen :: (str -> Bool) -> ([Attribute str] -> Bool) -> Tag str -> Bool
|
|
Packit |
247f4e |
tagOpen pName pAttrs (TagOpen name attrs) =
|
|
Packit |
247f4e |
pName name && pAttrs attrs
|
|
Packit |
247f4e |
tagOpen _ _ _ = False
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Match a closing tag
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Matching a closing @\<\/a>@ tag:/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagClose (== "a") (TagClose "a")
|
|
Packit |
247f4e |
-- True
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagClose (== "a") (TagOpen "a" [])
|
|
Packit |
247f4e |
-- False
|
|
Packit |
247f4e |
tagClose :: (str -> Bool) -> Tag str -> Bool
|
|
Packit |
247f4e |
tagClose pName (TagClose name) = pName name
|
|
Packit |
247f4e |
tagClose _ _ = False
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Match text tags
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Match all text tags:/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> let tags = parseTags "This is a paragraph "
|
|
Packit |
247f4e |
-- [TagOpen "p" [],TagText "This is a paragraph",TagClose "p"]
|
|
Packit |
247f4e |
-- >>> filter (tagText (const True)) tags
|
|
Packit |
247f4e |
-- [TagText "This is a paragraph"]
|
|
Packit |
247f4e |
tagText :: (str -> Bool) -> Tag str -> Bool
|
|
Packit |
247f4e |
tagText p (TagText text) = p text
|
|
Packit |
247f4e |
tagText _ _ = False
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Match comment tags
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Matching comment tags that include an exclamation mark:/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> let tags = parseTags ""
|
|
Packit |
247f4e |
-- [TagComment "This is a comment!"]
|
|
Packit |
247f4e |
-- >>> all (tagComment (\s -> '!' `elem` s)) tags
|
|
Packit |
247f4e |
-- True
|
|
Packit |
247f4e |
tagComment :: (str -> Bool) -> Tag str -> Bool
|
|
Packit |
247f4e |
tagComment p (TagComment text) = p text
|
|
Packit |
247f4e |
tagComment _ _ = False
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Match an opening tag's name literally
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Matching @\@ tags with the @id@ "foo":/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> let tag = TagOpen "a" [("id", "foo")]
|
|
Packit |
247f4e |
-- TagOpen "a" [("id","foo")]
|
|
Packit |
247f4e |
-- >>> tagOpenLit "a" (\attrs -> any (== ("id", "foo")) attrs) tag
|
|
Packit |
247f4e |
-- True
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
tagOpenLit :: Eq str => str -> ([Attribute str] -> Bool) -> Tag str -> Bool
|
|
Packit |
247f4e |
tagOpenLit name = tagOpen (name==)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Match a closing tag's name literally
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Match a closing @\@ tag:/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagCloseLit "a" (TagClose "a")
|
|
Packit |
247f4e |
-- True
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagCloseLit "a" (TagClose "em")
|
|
Packit |
247f4e |
-- False
|
|
Packit |
247f4e |
tagCloseLit :: Eq str => str -> Tag str -> Bool
|
|
Packit |
247f4e |
tagCloseLit name = tagClose (name==)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Match an opening tag's name literally, and at least one of its attributes
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Matching a @\@ tag with the @id@ "foo":/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagOpenAttrLit "div" ("id", "foo") (TagOpen "div" [("id", "foo")])
|
|
Packit |
247f4e |
-- True
|
|
Packit |
247f4e |
tagOpenAttrLit :: Eq str => str -> Attribute str -> Tag str -> Bool
|
|
Packit |
247f4e |
tagOpenAttrLit name attr =
|
|
Packit |
247f4e |
tagOpenLit name (anyAttrLit attr)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
{- |
|
|
Packit |
247f4e |
Match a tag with given name, that contains an attribute
|
|
Packit |
247f4e |
with given name, that satisfies a predicate.
|
|
Packit |
247f4e |
If an attribute occurs multiple times,
|
|
Packit |
247f4e |
all occurrences are checked.
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
==== __Examples__
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
/Matching an @\@ tag with an ID that starts with "comment-":/
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
>>> let commentTag = TagOpen "a" [("id", "comment-45678")]
|
|
Packit |
247f4e |
>>> tagOpenAttrNameLit "a" "id" (\idValue -> "comment-" `Data.List.isPrefixOf` idValue) commentTag
|
|
Packit |
247f4e |
True
|
|
Packit |
247f4e |
-}
|
|
Packit |
247f4e |
tagOpenAttrNameLit :: Eq str => str -> str -> (str -> Bool) -> Tag str -> Bool
|
|
Packit |
247f4e |
tagOpenAttrNameLit tagName attrName pAttrValue =
|
|
Packit |
247f4e |
tagOpenLit tagName
|
|
Packit |
247f4e |
(anyAttr (\(name,value) -> name==attrName && pAttrValue value))
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Check if the 'Tag str' is 'TagOpen' and matches the given name
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Matching an @\@ tag:/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagOpenNameLit "a" (TagOpen "a" [])
|
|
Packit |
247f4e |
-- True
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagOpenNameLit "a" (TagOpen "div" [])
|
|
Packit |
247f4e |
-- False
|
|
Packit |
247f4e |
tagOpenNameLit :: Eq str => str -> Tag str -> Bool
|
|
Packit |
247f4e |
tagOpenNameLit name = tagOpenLit name (const True)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Check if the 'Tag str' is 'TagClose' and matches the given name
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- ==== __Examples__
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- /Matching a closing @\<\/a>@ tag:/
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagCloseNameLit "a" (TagClose "a")
|
|
Packit |
247f4e |
-- True
|
|
Packit |
247f4e |
--
|
|
Packit |
247f4e |
-- >>> tagCloseNameLit "a" (TagClose "div")
|
|
Packit |
247f4e |
-- False
|
|
Packit |
247f4e |
tagCloseNameLit :: Eq str => str -> Tag str -> Bool
|
|
Packit |
247f4e |
tagCloseNameLit name = tagCloseLit name
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- * Matching attributes
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Does any attribute name/value match the predicate.
|
|
Packit |
247f4e |
anyAttr :: ((str,str) -> Bool) -> [Attribute str] -> Bool
|
|
Packit |
247f4e |
anyAttr = any
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Does any attribute name match the predicate.
|
|
Packit |
247f4e |
anyAttrName :: (str -> Bool) -> [Attribute str] -> Bool
|
|
Packit |
247f4e |
anyAttrName p = any (p . fst)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Does any attribute value match the predicate.
|
|
Packit |
247f4e |
anyAttrValue :: (str -> Bool) -> [Attribute str] -> Bool
|
|
Packit |
247f4e |
anyAttrValue p = any (p . snd)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Does any attribute name/value match.
|
|
Packit |
247f4e |
anyAttrLit :: Eq str => (str,str) -> [Attribute str] -> Bool
|
|
Packit |
247f4e |
anyAttrLit attr = anyAttr (attr==)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Does any attribute name match.
|
|
Packit |
247f4e |
anyAttrNameLit :: Eq str => str -> [Attribute str] -> Bool
|
|
Packit |
247f4e |
anyAttrNameLit name = anyAttrName (name==)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Does any attribute value match.
|
|
Packit |
247f4e |
anyAttrValueLit :: Eq str => str -> [Attribute str] -> Bool
|
|
Packit |
247f4e |
anyAttrValueLit value = anyAttrValue (value==)
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
|
|
Packit |
247f4e |
-- | Get the tags under tags with a given name where the attributes match some predicate.
|
|
Packit |
247f4e |
getTagContent :: Eq str => str -> ([Attribute str] -> Bool) -> [Tag str] -> [Tag str]
|
|
Packit |
247f4e |
getTagContent name pAttrs =
|
|
Packit |
247f4e |
takeWhile (not . tagCloseLit name) . drop 1 .
|
|
Packit |
247f4e |
head . sections (tagOpenLit name pAttrs)
|
|
Packit |
247f4e |
where sections p = filter (p . head) . init . tails
|