{-# LANGUAGE OverloadedStrings #-}
-- | Manipulate CSS urls.
--
-- * Make relative urls absolute (useful when combining assets)
module Yesod.EmbeddedStatic.Css.AbsoluteUrl (
  -- * Absolute urls
    absoluteUrls
  , absoluteUrlsAt
  , absoluteUrlsWith
  , absCssUrlsFileProd
  , absCssUrlsProd
) where

import Yesod.EmbeddedStatic.Generators
import Yesod.EmbeddedStatic.Types

import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.IO as T
import qualified Data.Text.Lazy.Encoding as TL
import Control.Monad ((>=>))
import Data.Maybe (fromMaybe)
import System.FilePath ((</>))

import Yesod.EmbeddedStatic.Css.Util

-------------------------------------------------------------------------------
-- Generator
-------------------------------------------------------------------------------

-- | Anchors relative CSS image urls
absCssUrlsFileProd :: FilePath -- ^ Anchor relative urls to here
                     -> FilePath
                     -> IO BL.ByteString
absCssUrlsFileProd :: FilePath -> FilePath -> IO ByteString
absCssUrlsFileProd FilePath
dir FilePath
file = do
    contents <- FilePath -> IO Text
T.readFile FilePath
file
    return $ TL.encodeUtf8 $ absCssUrlsProd dir contents

absCssUrlsProd :: FilePath -- ^ Anchor relative urls to here
               -> T.Text
               -> TL.Text
absCssUrlsProd :: FilePath -> Text -> Text
absCssUrlsProd FilePath
dir Text
contents =
    let css :: Css
css = (FilePath -> Css) -> (Css -> Css) -> Either FilePath Css -> Css
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either FilePath -> Css
forall a. HasCallStack => FilePath -> a
error Css -> Css
forall a. a -> a
id (Either FilePath Css -> Css) -> Either FilePath Css -> Css
forall a b. (a -> b) -> a -> b
$ Text -> Either FilePath Css
parseCssUrls Text
contents
    in  (UrlReference -> Text) -> Css -> Text
renderCssWith UrlReference -> Text
toAbsoluteUrl Css
css
  where
    toAbsoluteUrl :: UrlReference -> Text
toAbsoluteUrl (UrlReference Text
rel) = [Text] -> Text
T.concat
        [ Text
"url('/"
        , (FilePath -> Text
T.pack (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ FilePath
dir FilePath -> FilePath -> FilePath
</> Text -> FilePath
T.unpack Text
rel)
        , Text
"')"
        ]


-- | Equivalent to passing the same string twice to 'absoluteUrlsAt'.
absoluteUrls :: FilePath -> Generator
absoluteUrls :: FilePath -> Generator
absoluteUrls FilePath
f = FilePath -> FilePath -> Generator
absoluteUrlsAt FilePath
f FilePath
f

-- | Equivalent to passing @return@ to 'absoluteUrlsWith'.
absoluteUrlsAt :: Location -> FilePath -> Generator
absoluteUrlsAt :: FilePath -> FilePath -> Generator
absoluteUrlsAt FilePath
loc FilePath
f = FilePath
-> FilePath -> Maybe (CssGeneration -> IO ByteString) -> Generator
absoluteUrlsWith FilePath
loc FilePath
f Maybe (CssGeneration -> IO ByteString)
forall a. Maybe a
Nothing

-- | Automatically make relative urls absolute
--
-- During development, leave CSS as is.
--
-- When CSS is organized into a directory structure, it will work properly for individual requests for each file.
-- During production, we want to combine and minify CSS as much as possible.
-- The combination process combines files from different directories, messing up relative urls.
-- This pre-processor makes relative urls absolute
absoluteUrlsWith ::
    Location -- ^ The location the CSS file should appear in the static subsite
  -> FilePath -- ^ Path to the CSS file.
  -> Maybe (CssGeneration -> IO BL.ByteString) -- ^ Another filter function run after this one (for example @return . yuiCSS . cssContent@) or other CSS filter that runs after this filter.
                     -> Generator
absoluteUrlsWith :: FilePath
-> FilePath -> Maybe (CssGeneration -> IO ByteString) -> Generator
absoluteUrlsWith FilePath
loc FilePath
file Maybe (CssGeneration -> IO ByteString)
mpostFilter =
    [Entry] -> Generator
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return [ (FilePath -> IO ByteString) -> FilePath -> FilePath -> Entry
cssProductionFilter (FilePath -> FilePath -> IO ByteString
absCssUrlsFileProd FilePath
loc (FilePath -> IO ByteString)
-> (ByteString -> IO ByteString) -> FilePath -> IO ByteString
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> CssGeneration -> IO ByteString
postFilter (CssGeneration -> IO ByteString)
-> (ByteString -> CssGeneration) -> ByteString -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath -> ByteString -> CssGeneration
mkCssGeneration FilePath
loc FilePath
file) FilePath
loc FilePath
file
    ]
  where
    postFilter :: CssGeneration -> IO ByteString
postFilter = (CssGeneration -> IO ByteString)
-> Maybe (CssGeneration -> IO ByteString)
-> CssGeneration
-> IO ByteString
forall a. a -> Maybe a -> a
fromMaybe (ByteString -> IO ByteString
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> IO ByteString)
-> (CssGeneration -> ByteString) -> CssGeneration -> IO ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CssGeneration -> ByteString
cssContent) Maybe (CssGeneration -> IO ByteString)
mpostFilter