module Text.XML.HXT.CSS
( css
, cssShallow
, cssNav
, cssShallowNav
, Css
)
where
import Data.Char
import Data.Maybe
import Data.List
import Data.List.Split
import Text.XML.HXT.Core
import qualified Text.XML.HXT.DOM.XmlNode as XN
import Data.Tree.NavigatableTree.Class
import qualified Data.Tree.NavigatableTree.XPathAxis as T
import Text.XML.HXT.DTDValidation.TypeDefs
import Data.Tree.NTree.Zipper.TypeDefs
import Text.XML.HXT.CSS.TypeDefs
import Text.XML.HXT.CSS.Parser
css :: (ArrowXml a, Css s) => s -> a XmlTree XmlTree
css = withNav . cssNav
cssShallow :: (ArrowXml a, Css s) => s -> a XmlTree XmlTree
cssShallow = withNav . cssShallowNav
cssNav :: (ArrowXml a, Css s) => s -> a XmlNavTree XmlNavTree
cssNav s = isElemN >>> skipXmlRoot >>> selectDeep s
cssShallowNav :: (ArrowXml a, Css s) => s -> a XmlNavTree XmlNavTree
cssShallowNav s = isElemN >>> skipXmlRoot >>> select s
class Css s where
selectDeep :: ArrowXml a => s -> a XmlNavTree XmlNavTree
select :: ArrowXml a => s -> a XmlNavTree XmlNavTree
selectDeep s = multi (isElemN >>> select s)
instance Css [Char] where
selectDeep s =
case safeParseCSS s of
Right sel -> selectDeep sel
Left msg -> constA $ XN.mkError c_err msg
select s =
case safeParseCSS s of
Right sel -> select sel
Left msg -> constA $ XN.mkError c_err msg
instance Css SelectorsGroup where
select (SelectorsGroup sels) =
foldr ((<+>) . select) zeroArrow sels
instance Css Selector where
select (Selector sss) = select sss
select (Descendant sss sel) =
select sss >>> getChildren >>> isElemN >>>
multi (isElemN >>> select sel)
select (Child sss sel) =
select sss >>> getChildren >>> isElemN >>> select sel
select (AdjSibling sss sel) =
select sss >>> nextSibling >>> select sel
select (FolSibling sss sel) =
select sss >>> followingSiblingAxis >>> select sel
instance Css SimpleSelectorSeq where
select (SimpleSelectorSeq simpSels) =
foldr ((>>>) . select) this simpSels
instance Css SimpleSelector where
select UniversalSelector = this
select (TypeSelector tagName) = withoutNav $ hasName tagName
select (IdSelector nodeId) =
withoutNav $ hasAttrValue "id" (== nodeId)
select (ClassSelector className) =
withoutNav $ hasAttrValue "class" (hasWord className)
select (AttrSelector attrb sel) =
withoutNav $ hasAttrValue attrb p
where
p = case sel of
AttrExists -> const True
AttrEq val -> (== val)
AttrContainsSp val -> hasWord val
AttrBeginHy val -> hypenPrefix val
AttrPrefix val -> isPrefixOf val
AttrSuffix val -> isSuffixOf val
AttrSubstr val -> isInfixOf val
hypenPrefix s1 s2 =
case wordsBy (== '-') s2 of
w : _ | s1 == w -> True
_ -> False
select (Pseudo pseudo) = select pseudo
select (PseudoNth pseudo) = select pseudo
select (Negation simple) = neg (select simple)
instance Css PseudoClass where
select PseudoFirstChild = nthChild (== 1)
select PseudoLastChild = nthLastChild (== 1)
select PseudoOnlyChild =
nthChild (== 1) >>> nthLastChild (== 1)
select PseudoFirstOfType = nthOfType (== 1)
select PseudoLastOfType = nthLastOfType (== 1)
select PseudoOnlyOfType =
nthOfType (== 1) >>> nthLastOfType (== 1)
select PseudoEmpty = neg notEmpty
where
notEmpty = filterA $ getChildren >>>
withoutNav (isElem <+> isText <+> isCdata <+> isEntityRef)
select PseudoRoot = filterA (moveUp' >>> isRootN)
instance Css PseudoNthClass where
select (PseudoNthChild nth) = nthChild (testNth nth)
select (PseudoNthLastChild nth) = nthLastChild (testNth nth)
select (PseudoNthOfType nth) = nthOfType (testNth nth)
select (PseudoNthLastOfType nth) = nthLastOfType (testNth nth)
moveUp' :: (ArrowList a, NavigatableTree t) => a (t a1) (t a1)
moveUp' = arrL $ maybeToList . mvUp
skipXmlRoot :: ArrowXml a => a XmlNavTree XmlNavTree
skipXmlRoot = ifA isRootN (getChildren >>> isElemN) this
hasParent :: ArrowXml a => a XmlNavTree XmlNavTree
hasParent = filterA $ moveUp' >>> neg isRootN
isRootN :: ArrowXml a => a XmlNavTree XmlNavTree
isRootN = withoutNav isRoot
nextSibling :: ArrowList a => a XmlNavTree XmlNavTree
nextSibling = arrL go
where
go x =
case mvRight x of
Just x'
| isElemNodeN x' -> [x']
| otherwise -> go x'
Nothing -> []
nthChild :: ArrowXml a => (Int -> Bool) -> a XmlNavTree XmlNavTree
nthChild p = arrL (nthElemFun T.precedingSiblingAxis p) >>> hasParent
nthLastChild :: ArrowXml a => (Int -> Bool) -> a XmlNavTree XmlNavTree
nthLastChild p = arrL (nthElemFun T.followingSiblingAxis p) >>> hasParent
nthOfType :: ArrowXml a => (Int -> Bool) -> a XmlNavTree XmlNavTree
nthOfType p = arrL (nthElemOfTypeFun T.precedingSiblingAxis p) >>> hasParent
nthLastOfType :: ArrowXml a => (Int -> Bool) -> a XmlNavTree XmlNavTree
nthLastOfType p = arrL (nthElemOfTypeFun T.followingSiblingAxis p) >>> hasParent
nthElemOfTypeFun
:: (XmlNavTree -> [XmlNavTree])
-> (Int -> Bool) -> XmlNavTree -> [XmlNavTree]
nthElemOfTypeFun axis p x = nthElemFun axis' p x
where
axis' = filter ((== xNm) . getNm) . axis
xNm = getNm x
getNm = XN.getQualifiedName . (\(XN.NTree n _) -> n) . ntree
nthElemFun
:: (XmlNavTree -> [XmlNavTree])
-> (Int -> Bool) -> XmlNavTree -> [XmlNavTree]
nthElemFun axis p x = [x | p n]
where
n = 1 + length (filter isElemNodeN $ axis x)
isElemNodeN :: XmlNavTree -> Bool
isElemNodeN = isElemNode . ntree
isElemN :: ArrowXml a => a XmlNavTree XmlNavTree
isElemN = withoutNav isElem
hasWord :: String -> String -> Bool
hasWord w = any (== w) . wordsBy isSpace