{- |
Module      : Text.Pandoc.CSS
Copyright   : © 2006-2021 John MacFarlane <jgm@berkeley.edu>,
                2015-2016 Mauro Bieg,
                2015      Ophir Lifshitz <hangfromthefloor@gmail.com>
License     : GNU GPL, version 2 or above

Maintainer  : John MacFarlane <jgm@berkeley@edu>
Stability   : alpha
Portability : portable

Tools for working with CSS.
-}
module Text.Pandoc.CSS
  ( cssAttributes
  , pickStyleAttrProps
  , pickStylesToKVs
  )
where

import Data.Maybe (mapMaybe, listToMaybe)
import Data.Text (Text, pack)
import Text.Pandoc.Shared (trim)
import Text.Parsec
import Text.Parsec.Text

ruleParser :: Parser (Text, Text)
ruleParser :: Parser (Text, Text)
ruleParser = do
    [Char]
p <- ParsecT Text () Identity Char -> ParsecT Text () Identity [Char]
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m [a]
many1 ([Char] -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m Char
noneOf [Char]
":")  ParsecT Text () Identity [Char]
-> ParsecT Text () Identity Char -> ParsecT Text () Identity [Char]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Char -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
':'
    [Char]
v <- ParsecT Text () Identity Char -> ParsecT Text () Identity [Char]
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m [a]
many1 ([Char] -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m Char
noneOf [Char]
":;") ParsecT Text () Identity [Char]
-> ParsecT Text () Identity () -> ParsecT Text () Identity [Char]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ParsecT Text () Identity Char -> ParsecT Text () Identity ()
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m ()
optional (Char -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
';') ParsecT Text () Identity [Char]
-> ParsecT Text () Identity () -> ParsecT Text () Identity [Char]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ParsecT Text () Identity ()
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m ()
spaces
    (Text, Text) -> Parser (Text, Text)
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> Text
trim (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
pack [Char]
p, Text -> Text
trim (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
pack [Char]
v)

styleAttrParser :: Parser [(Text, Text)]
styleAttrParser :: Parser [(Text, Text)]
styleAttrParser = Parser (Text, Text) -> Parser [(Text, Text)]
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m [a]
many1 Parser (Text, Text)
ruleParser

-- | Parses a style string, returning the CSS attributes.
-- Returns an empty list on failure.
cssAttributes :: Text -> [(Text, Text)]
cssAttributes :: Text -> [(Text, Text)]
cssAttributes Text
styleString =
  -- Use Data.Either.fromRight once GHC 8.0 is no longer supported
  case Parser [(Text, Text)]
-> [Char] -> Text -> Either ParseError [(Text, Text)]
forall s t a.
Stream s Identity t =>
Parsec s () a -> [Char] -> s -> Either ParseError a
parse Parser [(Text, Text)]
styleAttrParser [Char]
"" Text
styleString of
    Left ParseError
_  -> []
    Right [(Text, Text)]
x -> [(Text, Text)]
x

-- | takes a list of keys/properties and a CSS string and
-- returns the corresponding key-value-pairs.
pickStylesToKVs :: [Text] -> Text -> [(Text, Text)]
pickStylesToKVs :: [Text] -> Text -> [(Text, Text)]
pickStylesToKVs [Text]
props Text
styleAttr =
  ((Text, Text) -> Bool) -> [(Text, Text)] -> [(Text, Text)]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(Text, Text)
s -> (Text, Text) -> Text
forall a b. (a, b) -> a
fst (Text, Text)
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
props) ([(Text, Text)] -> [(Text, Text)])
-> [(Text, Text)] -> [(Text, Text)]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)]
cssAttributes Text
styleAttr

-- | takes a list of key/property synonyms and a CSS string and maybe
-- returns the value of the first match (in order of the supplied list)
pickStyleAttrProps :: [Text] -> Text -> Maybe Text
pickStyleAttrProps :: [Text] -> Text -> Maybe Text
pickStyleAttrProps [Text]
lookupProps Text
styleAttr = do
    [(Text, Text)]
styles <- (ParseError -> Maybe [(Text, Text)])
-> ([(Text, Text)] -> Maybe [(Text, Text)])
-> Either ParseError [(Text, Text)]
-> Maybe [(Text, Text)]
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Maybe [(Text, Text)] -> ParseError -> Maybe [(Text, Text)]
forall a b. a -> b -> a
const Maybe [(Text, Text)]
forall a. Maybe a
Nothing) [(Text, Text)] -> Maybe [(Text, Text)]
forall a. a -> Maybe a
Just (Either ParseError [(Text, Text)] -> Maybe [(Text, Text)])
-> Either ParseError [(Text, Text)] -> Maybe [(Text, Text)]
forall a b. (a -> b) -> a -> b
$ Parser [(Text, Text)]
-> [Char] -> Text -> Either ParseError [(Text, Text)]
forall s t a.
Stream s Identity t =>
Parsec s () a -> [Char] -> s -> Either ParseError a
parse Parser [(Text, Text)]
styleAttrParser [Char]
"" Text
styleAttr
    [Text] -> Maybe Text
forall a. [a] -> Maybe a
listToMaybe ([Text] -> Maybe Text) -> [Text] -> Maybe Text
forall a b. (a -> b) -> a -> b
$ (Text -> Maybe Text) -> [Text] -> [Text]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
`lookup` [(Text, Text)]
styles) [Text]
lookupProps