| 2957 | return "Selector(%s)" % repr(self.string) |
| 2958 | |
| 2959 | class SelectorChain(list): |
| 2960 | |
| 2961 | def __init__(self, s): |
| 2962 | """ A selector is a chain of one or more simple selectors, |
| 2963 | separated by combinators (e.g., ">"). |
| 2964 | """ |
| 2965 | self.string = s |
| 2966 | for s in s.split(","): |
| 2967 | s = s.lower() |
| 2968 | s = s.strip() |
| 2969 | s = re.sub(r" +", " ", s) |
| 2970 | s = re.sub(r" *\> *", " >", s) |
| 2971 | s = re.sub(r" *\< *", " <", s) |
| 2972 | s = re.sub(r" *\+ *", " +", s) |
| 2973 | self.append([]) |
| 2974 | for s in s.split(" "): |
| 2975 | if not s.startswith((">", "<", "+")): |
| 2976 | self[-1].append((" ", Selector(s))) |
| 2977 | elif s.startswith(">"): |
| 2978 | self[-1].append((">", Selector(s[1:]))) |
| 2979 | elif s.startswith("<"): |
| 2980 | self[-1].append(("<", Selector(s[1:]))) |
| 2981 | elif s.startswith("+"): |
| 2982 | self[-1].append(("+", Selector(s[1:]))) |
| 2983 | |
| 2984 | def search(self, e): |
| 2985 | """ Returns the nested elements that match the CSS selector chain. |
| 2986 | """ |
| 2987 | m, root = [], e |
| 2988 | for chain in self: |
| 2989 | e = [root] |
| 2990 | for combinator, s in chain: |
| 2991 | if combinator == " ": |
| 2992 | # X Y => X is ancestor of Y |
| 2993 | e = map(s.search, e) |
| 2994 | e = list(itertools.chain(*e)) |
| 2995 | if combinator == ">": |
| 2996 | # X > Y => X is parent of Y |
| 2997 | e = map(lambda e: filter(s.match, e.children), e) |
| 2998 | e = list(itertools.chain(*e)) |
| 2999 | if combinator == "<": |
| 3000 | # X < Y => X is child of Y |
| 3001 | e = map(lambda e: e.parent, e) |
| 3002 | e = filter(s.match, e) |
| 3003 | if combinator == "+": |
| 3004 | # X + Y => X directly precedes Y |
| 3005 | e = map(s._first_sibling, e) |
| 3006 | e = filter(s.match, e) |
| 3007 | m.extend(e) |
| 3008 | return m |
| 3009 | |
| 3010 | #dom = DOM(""" |
| 3011 | #<html> |
no outgoing calls
no test coverage detected
searching dependent graphs…