{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} -- | Combinators to build AST -- -- Example : -- -- > -- >import Language.Css.Syntax -- >import Language.Css.Build -- >import Language.Css.Pretty -- >import Language.Css.Build.Idents -- >import Language.Css.Build.Tags hiding (center) -- > -- >res = ruleSets [ -- > body [ -- > margin <:> int 0, -- > border <:> int 0 ], -- > -- > h1 [ textAlign <:> center], -- > -- > p [ -- > backgroundColor <:> black, -- > color <:> white, -- > padding <:> spaces [pct 5, pct 5, pct 10, pct 10] ], -- > -- > (star /. "warning") [ color <:> red ] -- > ] -- > -- >main = print $ pretty res -- > -- module Language.Css.Build ( -- * Classes Idents(..), ToExpr(..), -- * StyleSheet styleSheet, rules, ruleSets, addImports, addRules, charset, -- * AtRules media, page, fontFace, -- * RuleSets -- ** Selectors Sel', star, sels, (/>), (/-), (/#), (/.), (/:), Attrs(..), (!), (.=), (~=), (|=), -- ** Declarations (<:>), important, space, slash, comma, spaces, slashes, commas, -- * Primitive values fun, str, int, num, deg, rad, grad, cword, rgb, hz, khz, em, ex, px, in', cm, mm, pc, pt, pct, ms, s, url, -- * Colors aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, orange, purple, red, silver, teal, white, yellow ) where import Language.Css.Syntax import Control.Applicative class Idents a where ident :: String -> a instance Idents Ident where ident = Ident instance Idents String where ident = id class ToExpr a where expr :: a -> Expr instance ToExpr Expr where expr = id ----------------------------------------------------------------- -- StyleSheet styleSheet :: Maybe AtCharSet -> [AtImport] -> [StyleBody] -> StyleSheet styleSheet = StyleSheet -- | construct 'StyleSheet' from list of 'AtRule' 's or 'RuleSet' 's rules :: [StyleBody] -> StyleSheet rules = styleSheet Nothing [] -- | append imports addImports :: [AtImport] -> StyleSheet -> StyleSheet addImports is' (StyleSheet c is body) = StyleSheet c (is ++ is') body -- | append rules addRules :: [StyleBody] -> StyleSheet -> StyleSheet addRules rs (StyleSheet c is body) = StyleSheet c is $ rs ++ body -- | construct 'StyleSheet' from list of 'RuleSet' 's ruleSets :: [RuleSet] -> StyleSheet ruleSets = StyleSheet Nothing [] . map SRuleSet -- | set \@charset charset :: String -> StyleSheet -> StyleSheet charset str (StyleSheet _ is body) = StyleSheet (Just $ AtCharSet str) is body ----------------------------------------------------------------- -- AtRules -- | \@media media :: [String] -> [RuleSet] -> StyleBody media ms rs = SAtMedia $ AtMedia (map ident ms) rs -- | \@page page :: Maybe String -> Maybe PseudoPage -> [Decl] -> StyleBody page i p ds = SAtPage $ AtPage (ident <$> i) p ds -- | import from string importStr :: String -> [Ident] -> AtImport importStr str = AtImport (IStr str) -- | import from uri importUri :: String -> [Ident] -> AtImport importUri str = AtImport (IUri $ Uri str) -- | \@font-face fontFace :: [Decl] -> StyleBody fontFace = SAtFontFace . AtFontFace -- RuleSets ----------------------------------------------------------------- -- Selectors infixl 5 /-, />, /+ infixl 6 /#, /., !, /: infixr 0 <:> -- | 'RuleSet' constructor type Sel' = [Decl] -> RuleSet -- | @*@ selector star :: Sel' star = RuleSet (return $ SSel $ UnivSel []) instance Idents Sel' where ident x = RuleSet (return $ SSel $ TypeSel x []) -- compose -- | groups selectors sels :: [Sel'] -> Sel' sels xs d = joinRules [] $ map ($ d) xs where joinRules sels xs = case xs of [] -> RuleSet sels d a:as -> joinRules (sels ++ getSels a) as -- | Descendant -- -- space in css (/-) :: Sel' -> Sel' -> Sel' (/-) = liftSel2 DescendSel -- | Child -- -- @>@ in css (/>) :: Sel' -> Sel' -> Sel' (/>) = liftSel2 ChildSel -- | Sibling -- -- @+@ in css (/+) :: Sel' -> Sel' -> Sel' (/+) = liftSel2 AdjSel -- set attribs -- | set id (/#) :: Sel' -> String -> Sel' (/#) s id = liftSel1 (appendSubSel $ IdSel id) s -- | set class (/.) :: Sel' -> String -> Sel' (/.) s cl = liftSel1 (appendSubSel $ ClassSel cl) s -- | set attributes (!) :: Sel' -> Attr -> Sel' (!) s attr = liftSel1 (appendSubSel $ AttrSel attr) s -- | set pseudo classes/elements (/:) :: Sel' -> PseudoVal -> Sel' (/:) s p = liftSel1 (appendSubSel $ PseudoSel p) s liftSel1 :: (Sel -> Sel) -> (Sel' -> Sel') liftSel1 f = liftA f' where f' a = RuleSet (liftA f $ getSels a) $ getDecls a liftSel2 :: (Sel -> Sel -> Sel) -> (Sel' -> Sel' -> Sel') liftSel2 f = liftA2 f' where f' a b = RuleSet (liftA2 f (getSels a) (getSels b)) $ getDecls a instance Idents Attr where ident = Attr -- | attribute selector class Attrs a where attr :: String -> a instance Attrs Attr where attr = Attr instance Attrs AttrIdent where attr = id -- | element's attribute is (.=) :: AttrIdent -> AttrVal -> Attr (.=) = AttrIs -- | element's attribute includes (~=) :: AttrIdent -> AttrVal -> Attr (~=) = AttrIncl -- | element's attribute begins with (|=) :: AttrIdent -> AttrVal -> Attr (|=) = AttrBegins appendSubSel :: SubSel -> Sel -> Sel appendSubSel s a = case a of SSel x -> case x of UnivSel xs -> SSel $ UnivSel $ xs ++ [s] TypeSel el xs -> SSel $ TypeSel el $ xs ++ [s] DescendSel x y -> DescendSel (appendSubSel s x) (appendSubSel s y) ChildSel x y -> ChildSel (appendSubSel s x) (appendSubSel s y) AdjSel x y -> AdjSel (appendSubSel s x) (appendSubSel s y) getSels :: RuleSet -> [Sel] getSels (RuleSet xs _) = xs getDecls :: RuleSet -> [Decl] getDecls (RuleSet _ xs) = xs instance Idents PseudoVal where ident = PIdent . ident ----------------------------------------------------------------- -- Declarations -- | declaration constructor (<:>) :: String -> Expr -> Decl (<:>) a b = Decl Nothing (ident a) b -- | set @!important@ important :: Decl -> Decl important (Decl _ a b) = Decl (Just Important) a b -- | space separated values space :: Expr -> Expr -> Expr space = SpaceSep -- | 'space' on list of values spaces :: [Expr] -> Expr spaces = foldl1 space -- | slash separated values slash :: Expr -> Expr -> Expr slash = SlashSep -- | 'slash' on list of values slashes :: [Expr] -> Expr slashes = foldl1 slash -- | comma separated values comma :: Expr -> Expr -> Expr comma = CommaSep -- | 'comma' on lists of values commas :: [Expr] -> Expr commas = foldl1 comma instance ToExpr a => ToExpr [a] where expr x = case x of [] -> ident "" _ -> foldl1 space $ map expr x ----------------------------------------------------------------- -- Values -- instance Idents Expr where ident = EVal . ident instance Idents Value where ident = VIdent . ident instance ToExpr Value where expr = EVal ----------------------------------------------------------------- -- primitive values -- constructors -- | 'Func' constructor fun :: ToExpr a => Ident -> a -> Func fun str = Func str . expr -- | \ deg :: Double -> Expr deg = expr . Deg -- | \ rad :: Double -> Expr rad = expr . Rad -- | \ grad :: Double -> Expr grad = expr . Grad -- | \ cword :: String -> Expr cword = expr . Cword . checkWord -- | \ rgb :: Int -> Int -> Int -> Expr rgb x0 x1 x2 = expr $ Crgb x0 x1 x2 -- | \ hz :: Double -> Expr hz = expr . Hz -- | \ khz :: Double -> Expr khz = expr . KHz -- | \ em :: Double -> Expr em = expr . Em -- | \ ex :: Double -> Expr ex = expr . Ex -- | \ px :: Int -> Expr px = expr . Px -- | \ in' :: Double -> Expr in' = expr . In -- | \ cm :: Double -> Expr cm = expr . Cm -- | \ mm :: Double -> Expr mm = expr . Mm -- | \ pc :: Double -> Expr pc = expr . Pc -- | \ pt :: Int -> Expr pt = expr . Pt -- | \ pct :: Double -> Expr pct = expr . Percentage -- | \ ms :: Double -> Expr ms = expr . Ms -- | \ s :: Double -> Expr s = expr . S -- | \ url :: String -> Expr url = expr . Uri checkWord x | checkLeng x && checkNums x && checkFirst x = x | otherwise = errorMsg where errorMsg = error "must be number in hexadecimal notation" checkLeng x | length x == 4 || length x == 7 = True | otherwise = error "string length must be 4 or 7" checkNums x | all (`elem` (['0' .. '9'] ++ ['a' .. 'f'] ++ ['A' .. 'F']))$ tail x = True | otherwise = errorMsg checkFirst x | '#' == head x = True | otherwise = error "first character must be #" str :: String -> Expr str = expr . VString int :: Int -> Expr int = expr num :: Double -> Expr num = expr -- instances -- ToExpr instance ToExpr Deg where expr = expr . VDeg instance ToExpr Rad where expr = expr . VRad instance ToExpr Grad where expr = expr . VGrad instance ToExpr Color where expr = expr . VColor instance ToExpr Hz where expr = expr . VHz instance ToExpr KHz where expr = expr . VKHz instance ToExpr Func where expr = expr . VFunc instance ToExpr Ident where expr = expr . VIdent instance ToExpr Int where expr = expr . VInt instance ToExpr Em where expr = expr . VEm instance ToExpr Ex where expr = expr . VEx instance ToExpr Px where expr = expr . VPx instance ToExpr In where expr = expr . VIn instance ToExpr Cm where expr = expr . VCm instance ToExpr Mm where expr = expr . VMm instance ToExpr Pc where expr = expr . VPc instance ToExpr Pt where expr = expr . VPt instance ToExpr Double where expr = expr . VDouble instance ToExpr Percentage where expr = expr . VPercentage instance ToExpr Ms where expr = expr . VMs instance ToExpr S where expr = expr . VS instance ToExpr Uri where expr = expr . VUri -- colors -- aqua = cword "#00ffff" black = cword "#000000" blue = cword "#0000ff" fuchsia = cword "#ff00ff" gray = cword "#808080" green = cword "#008000" lime = cword "#00ff00" maroon = cword "#800000" navy = cword "#000080" olive = cword "#808000" orange = cword "#ffA500" purple = cword "#800080" red = cword "#ff0000" silver = cword "#c0c0c0" teal = cword "#008080" white = cword "#ffffff" yellow = cword "#ffff00"