module Text.CSS.CleverCSSUtil (
(+++), (+:+), (~>>), varCount, perhaps,
Color, HLSColor, colors, reverse_colors,
hexToColor, brightenColor, darkenColor, modifyChannels,
unitconv, inrange, readNum, readDim,
trim, ltrim, rtrim, split, joinStr, joinShow,
preprocess, hexToString, cssShow, showWithoutPos,
ratMod, roundRat, showRat
) where
import Control.Arrow ((&&&))
import Control.Monad (msum)
import Data.List hiding (partition)
import Data.Ratio ((%), numerator, denominator)
import Numeric (readFloat)
import Text.Printf (printf)
import Text.ParserCombinators.Parsec (choice, count, try, option, GenParser)
import Text.ParserCombinators.Parsec.Error
import qualified Data.Map as Map
type P tok = GenParser tok [Int]
infixl 1 +++, ~>>
infixr 1 +:+
(+++) :: P tok [a] -> P tok [a] -> P tok [a]
x +++ y = do { rx <- x; ry <- y; return $ rx ++ ry }
(+:+) :: P tok a -> P tok [a] -> P tok [a]
x +:+ y = do { rx <- x; ry <- y; return $ rx:ry }
(~>>) :: P tok a -> P tok b -> P tok a
x ~>> y = do { rx <- x; y; return rx }
varCount low high p = choice [try $ count x p | x <- [high,high1..low]]
perhaps c = option "" (count 1 c)
type Color = (Int, Int, Int)
type HLSColor = (Double, Double, Double)
hls_to_rgb :: HLSColor -> Color
hls_to_rgb (_, l, 0) = (l', l', l') where l' = round (255 * l)
hls_to_rgb (h, l, s) =
let m2 = if l <= 0.5 then l * (s+1) else l + s (l*s)
m1 = 2*l m2
in (v m1 m2 (h + 1/3), v m1 m2 h, v m1 m2 (h 1/3))
where
v m1' m2' hue' = round (255 * (v' m1' m2' hue')) where
v' m1 m2 hue =
let phue = snd $ properFraction (hue + 2) in
if phue < 1/6 then m1 + (m2m1) * phue * 6
else if phue < 1/2 then m2
else if phue < 2/3 then m1 + (m2m1) * (2/3 phue) * 6
else m1
rgb_to_hls :: Color -> HLSColor
rgb_to_hls (r', g', b') =
let r = fromIntegral r' / 255
g = fromIntegral g' / 255
b = fromIntegral b' / 255
maxc = max (max r g) b
minc = min (min r g) b
mami = maxc minc
l = (minc + maxc) / 2
s = if l <= 0.5 then mami / (maxc+minc)
else mami / (2maxcminc)
rc = (maxcr) / mami
gc = (maxcg) / mami
bc = (maxcb) / mami
h' = if r == maxc then bc gc
else if g == maxc then 2 + rc bc
else 4 + gc rc
h = snd $ properFraction (h'/6)
in if minc == maxc then (0.0, l, 0.0)
else (h, l, s)
colors = Map.fromList $ map (fst &&& hexToColor . snd) [
("aliceblue", "#f0f8ff"),
("antiquewhite", "#faebd7"),
("aqua", "#00ffff"),
("aquamarine", "#7fffd4"),
("azure", "#f0ffff"),
("beige", "#f5f5dc"),
("bisque", "#ffe4c4"),
("black", "#000000"),
("blanchedalmond", "#ffebcd"),
("blue", "#0000ff"),
("blueviolet", "#8a2be2"),
("brown", "#a52a2a"),
("burlywood", "#deb887"),
("cadetblue", "#5f9ea0"),
("chartreuse", "#7fff00"),
("chocolate", "#d2691e"),
("coral", "#ff7f50"),
("cornflowerblue", "#6495ed"),
("cornsilk", "#fff8dc"),
("crimson", "#dc143c"),
("cyan", "#00ffff"),
("darkblue", "#00008b"),
("darkcyan", "#008b8b"),
("darkgoldenrod", "#b8860b"),
("darkgray", "#a9a9a9"),
("darkgreen", "#006400"),
("darkkhaki", "#bdb76b"),
("darkmagenta", "#8b008b"),
("darkolivegreen", "#556b2f"),
("darkorange", "#ff8c00"),
("darkorchid", "#9932cc"),
("darkred", "#8b0000"),
("darksalmon", "#e9967a"),
("darkseagreen", "#8fbc8f"),
("darkslateblue", "#483d8b"),
("darkslategray", "#2f4f4f"),
("darkturquoise", "#00ced1"),
("darkviolet", "#9400d3"),
("deeppink", "#ff1493"),
("deepskyblue", "#00bfff"),
("dimgray", "#696969"),
("dodgerblue", "#1e90ff"),
("firebrick", "#b22222"),
("floralwhite", "#fffaf0"),
("forestgreen", "#228b22"),
("fuchsia", "#ff00ff"),
("gainsboro", "#dcdcdc"),
("ghostwhite", "#f8f8ff"),
("gold", "#ffd700"),
("goldenrod", "#daa520"),
("gray", "#808080"),
("green", "#008000"),
("greenyellow", "#adff2f"),
("honeydew", "#f0fff0"),
("hotpink", "#ff69b4"),
("indianred", "#cd5c5c"),
("indigo", "#4b0082"),
("ivory", "#fffff0"),
("khaki", "#f0e68c"),
("lavender", "#e6e6fa"),
("lavenderblush", "#fff0f5"),
("lawngreen", "#7cfc00"),
("lemonchiffon", "#fffacd"),
("lightblue", "#add8e6"),
("lightcoral", "#f08080"),
("lightcyan", "#e0ffff"),
("lightgoldenrodyellow", "#fafad2"),
("lightgreen", "#90ee90"),
("lightgrey", "#d3d3d3"),
("lightpink", "#ffb6c1"),
("lightsalmon", "#ffa07a"),
("lightseagreen", "#20b2aa"),
("lightskyblue", "#87cefa"),
("lightslategray", "#778899"),
("lightsteelblue", "#b0c4de"),
("lightyellow", "#ffffe0"),
("lime", "#00ff00"),
("limegreen", "#32cd32"),
("linen", "#faf0e6"),
("magenta", "#ff00ff"),
("maroon", "#800000"),
("mediumaquamarine", "#66cdaa"),
("mediumblue", "#0000cd"),
("mediumorchid", "#ba55d3"),
("mediumpurple", "#9370db"),
("mediumseagreen", "#3cb371"),
("mediumslateblue", "#7b68ee"),
("mediumspringgreen", "#00fa9a"),
("mediumturquoise", "#48d1cc"),
("mediumvioletred", "#c71585"),
("midnightblue", "#191970"),
("mintcream", "#f5fffa"),
("mistyrose", "#ffe4e1"),
("moccasin", "#ffe4b5"),
("navajowhite", "#ffdead"),
("navy", "#000080"),
("oldlace", "#fdf5e6"),
("olive", "#808000"),
("olivedrab", "#6b8e23"),
("orange", "#ffa500"),
("orangered", "#ff4500"),
("orchid", "#da70d6"),
("palegoldenrod", "#eee8aa"),
("palegreen", "#98fb98"),
("paleturquoise", "#afeeee"),
("palevioletred", "#db7093"),
("papayawhip", "#ffefd5"),
("peachpuff", "#ffdab9"),
("peru", "#cd853f"),
("pink", "#ffc0cb"),
("plum", "#dda0dd"),
("powderblue", "#b0e0e6"),
("purple", "#800080"),
("red", "#ff0000"),
("rosybrown", "#bc8f8f"),
("royalblue", "#4169e1"),
("saddlebrown", "#8b4513"),
("salmon", "#fa8072"),
("sandybrown", "#f4a460"),
("seagreen", "#2e8b57"),
("seashell", "#fff5ee"),
("sienna", "#a0522d"),
("silver", "#c0c0c0"),
("skyblue", "#87ceeb"),
("slateblue", "#6a5acd"),
("slategray", "#708090"),
("snow", "#fffafa"),
("springgreen", "#00ff7f"),
("steelblue", "#4682b4"),
("tan", "#d2b48c"),
("teal", "#008080"),
("thistle", "#d8bfd8"),
("tomato", "#ff6347"),
("turquoise", "#40e0d0"),
("violet", "#ee82ee"),
("wheat", "#f5deb3"),
("white", "#ffffff"),
("whitesmoke", "#f5f5f5"),
("yellow", "#ffff00"),
("yellowgreen", "#9acd32")]
reverse_colors = Map.fromList $ map (snd &&& fst) $ (Map.toList colors)
hexToColor [h1,h2,h3,h4,h5,h6] = (hx [h1,h2], hx [h3,h4], hx [h5,h6])
where hx x = read ("0x" ++ x) :: Int
hexToColor [h1,h2,h3] = hexToColor [h1,h1,h2,h2,h3,h3]
hexToColor ('#':hs) = hexToColor hs
hexToColor _ = error "invalid hex color string"
brightenColor = modifyColor (\l a -> l*(1+a))
darkenColor = modifyColor (\l a -> l*(1a))
modifyColor fun col am = hls_to_rgb (h, inrange 0.0 1.0 (fun l am), s)
where (h, l, s) = rgb_to_hls col
modifyChannels :: (Rational -> Rational -> Rational) -> (Int, Int, Int) ->
Rational -> (Int, Int, Int)
modifyChannels op (r, g, b) am = (m r, m g, m b)
where m x = inrange 0 255 $ floor $ op (fromInteger (toInteger x)) am
inrange low high val = min high (max low val)
units = [[("mm", 1), ("cm", 10), ("in", 254%10), ("pt", 254%720), ("pc", 254%60)],
[("ms", 1), ("s", 1000)],
[("Hz", 1), ("kHz", 1000)]]
unitconv :: (Rational, String) -> (Rational, String) -> Maybe (Rational, Rational, String)
unitconv (x, u) (y, v)
| u == v = Just (x, y, v)
| otherwise = msum $ map (ratio u v) units
where ratio u v list = case (lookup u list, lookup v list) of
(Just du, Just dv) -> if du < dv then Just (x, dv/du * y, u)
else Just (du/dv * x, y, v)
_ -> Nothing
readNum x = case readWithSign 1 x of (sign, res) -> sign * fst res
readDim x = case readWithSign 1 x of
(sign, res) -> (sign * fst res, trim (snd res))
readWithSign _ ('-':num) = readWithSign (1) num
readWithSign sign num = (sign, head (readFloat num :: [(Rational, String)]))
ltrim (c:cs) | c `elem` " \t\v\f\r\n" = ltrim cs
ltrim cs = cs
rtrim = reverse . ltrim . reverse
trim = ltrim . rtrim
preprocess :: String -> String
preprocess [] = ['\n']
preprocess ('\t':xs) = ' ':' ':' ':' ':' ':' ':' ':' ' : preprocess xs
preprocess ('\f':xs) = '\n' : preprocess xs
preprocess ('\r':xs) = preprocess xs
preprocess (x:xs) = x : preprocess xs
spanList :: ([a] -> Bool) -> [a] -> ([a], [a])
spanList _ [] = ([],[])
spanList func list@(x:xs) =
if func list
then (x:ys,zs)
else ([],list)
where (ys,zs) = spanList func xs
breakList :: ([a] -> Bool) -> [a] -> ([a], [a])
breakList func = spanList (not . func)
split :: String -> String -> [String]
split _ [] = []
split delim str =
let (firstline, remainder) = breakList (isPrefixOf delim) str in
firstline : case remainder of
[] -> []
x -> if x == delim then [] : []
else split delim (drop (length delim) x)
joinStr d x = concat (intersperse d x)
joinShow d x = joinStr d (map show x)
hexToString :: String -> Char
hexToString x = toEnum (read $ "0x" ++ x)
cssShow s = '"':cssShow' s where
cssShow' ('"':cs) = '\\':'"':cssShow' cs
cssShow' (c:cs) | c `elem` [' '..'~'] = c:cssShow' cs
| otherwise = printf "\\%x " (fromEnum c) ++ cssShow' cs
cssShow' [] = ['"']
showWithoutPos msg err = msg ++ showErrorMessages "or" "unknown parse error"
"expecting" "unexpected" "end of input" (errorMessages err)
ratMod :: Rational -> Rational -> Rational
ratMod x y = (nx `mod` ny) % d where
dx = denominator x
dy = denominator y
d = lcm dx dy
nx = numerator x * (d `div` dx)
ny = numerator y * (d `div` dy)
roundRat :: Rational -> Rational -> Rational
roundRat num places =
let exp = round places :: Integer in
(round (num * (10^exp))) % (10^exp)
showRat :: Rational -> String
showRat r | rest == 0 = show whole
| otherwise = printf "%f" (fromRational r :: Double)
where (whole, rest) = (numerator r) `divMod` (denominator r)