@@ 1,20 1,26 @@
{-# LANGUAGE OverloadedStrings #-}
-- | Lowers certain CSS properties to plain text.
-module Data.CSS.Preprocessor.Text(TextStyle, resolve) where
+module Data.CSS.Preprocessor.Text(TextStyle, resolve, StyleTree(..)) where
import Data.CSS.Syntax.Tokens (Token(..), NumericValue(..))
import Data.CSS.Style (PropertyParser(..))
-import Data.Text
+import Data.CSS.StyleTree
+import qualified Data.Text as Txt
+import Data.Text (Text)
+
import Data.Maybe (fromMaybe)
+import qualified Data.HashMap.Lazy as M
+import Data.Function ((&))
+type Counters = [(Text, Integer)]
-- | `PropertyParser` decorator that parses & lowers certain CSS properties to plain text.
data TextStyle p = TextStyle {
inner :: p,
content :: [Token],
- counterReset :: [(Text, Integer)],
- counterIncrement :: [(Text, Integer)],
- counterSet :: [(Text, Integer)]
+ counterReset :: Counters,
+ counterIncrement :: Counters,
+ counterSet :: Counters
}
instance PropertyParser p => PropertyParser (TextStyle p) where
@@ 59,6 65,72 @@ parseCounters x (Ident counter : Number _ (NVInteger count') : toks) =
parseCounters x (Ident counter : toks) = (:) (counter, x) <$> parseCounters x toks
parseCounters _ _ = Nothing
-resolve :: PropertyParser p => TextStyle p -> p
-resolve TextStyle {inner = inner', content = content'} =
- fromMaybe inner' $ longhand temp inner' "content" content'
+resolve :: PropertyParser p => StyleTree (TextStyle p) -> StyleTree p
+resolve = applyCounters
+
+--------
+---- Counters
+--------
+type Context = M.HashMap Text [([Integer], Integer)]
+
+inheritCounters :: Context -> Context -> Context
+inheritCounters counterSource valueSource = M.intersectionWith cb valueSource counterSource -- indexed by name & el-path
+ where cb val source = [counter | counter@(path, _) <- val, path `elem` [p | (p, _) <- source]]
+
+instantiateCounter :: Context -> Path -> Text -> Integer -> Context
+instantiateCounter counters path name val = M.insertWith appendCounter name [(path, val)] counters
+ where
+ appendCounter new (old@((_:oldPath), _):olds)
+ | oldPath == tail path = new ++ olds
+ | otherwise = new ++ (old:olds)
+ appendCounter new [] = new
+ appendCounter new (_:olds) = new ++ olds
+instantiateCounters :: Path -> Counters -> Context -> Context
+instantiateCounters path instruct counters = foldl cb counters instruct
+ where cb counters' (name, value) = instantiateCounter counters' path name value
+
+incrementCounter :: Context -> Path -> Text -> Integer -> Context
+incrementCounter counters path name val = M.insertWith addCounter name [(path, val)] counters
+ where
+ addCounter ((_, new):_) ((path', old):rest) = (path', new + old):rest
+ addCounter [] old = old
+ addCounter new [] = new
+incrementCounters :: Path -> Counters -> Context -> Context
+incrementCounters path instruct counters = foldl cb counters instruct
+ where cb counters' (name, value) = incrementCounter counters' path name value
+
+setCounter :: Context -> Path -> Text -> Integer -> Context
+setCounter counters path name val = M.insertWith setCounter' name [(path, val)] counters
+ where
+ setCounter' ((_, val'):_) ((path', _):rest) = (path', val'):rest
+ setCounter' [] old = old
+ setCounter' new [] = new
+setCounters :: Path -> Counters -> Context -> Context
+setCounters path instruct counters = foldl cb counters instruct
+ where cb counters' (name, value) = setCounter counters' path name value
+
+
+renderCounters :: Context -> [Token] -> [Token]
+renderCounters counters (Function "counter":Ident name:RightParen:toks)
+ | Just ((_, count):_) <- name `M.lookup` counters =
+ String (Txt.pack $ show count) : renderCounters counters toks
+ | otherwise = renderCounters counters toks
+renderCounters counters (Function "counters":Ident name:Comma:String sep:RightParen:toks)
+ | Just counter <- name `M.lookup` counters = String (Txt.intercalate sep [
+ Txt.pack $ show count | (_, count) <- reverse counter
+ ]) : renderCounters counters toks
+ | otherwise = renderCounters counters toks
+renderCounters counters (tok:toks) = tok : renderCounters counters toks
+renderCounters _ [] = []
+
+applyCounters :: PropertyParser p => StyleTree (TextStyle p) -> StyleTree p
+applyCounters = treeOrder applyCounters0 M.empty
+applyCounters0 :: PropertyParser p => Context -> Context -> Path -> TextStyle p -> (Context, p)
+applyCounters0 counterSource valueSource path node =
+ let counters = inheritCounters counterSource valueSource &
+ instantiateCounters path (counterReset node) &
+ incrementCounters path (counterIncrement node) &
+ setCounters path (counterSet node)
+ in let inner' = inner node
+ in (counters,
+ fromMaybe inner' $ longhand temp inner' "content" $ renderCounters counters $ content node)
@@ 54,7 54,7 @@ source-repository head
library
-- Modules exported by the library.
exposed-modules: Data.CSS.Syntax.StyleSheet, Data.CSS.Syntax.Selector,
- Data.CSS.Style,
+ Data.CSS.Style, Data.CSS.StyleTree,
Data.CSS.Preprocessor.Conditions, Data.CSS.Preprocessor.Conditions.Expr,
Data.CSS.Preprocessor.Assets, Data.CSS.Preprocessor.Text