Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: c3425dd128
Fetching contributors…

Cannot retrieve contributors at this time

239 lines (199 sloc) 7.591 kb
#-*- coding:utf-8 -*-
#
# Copyright (C) 2008 - Olivier Lauzanne <olauzanne@gmail.com>
#
# Distributed under the BSD license, see LICENSE.txt
from lxml.cssselect import Pseudo, XPathExpr, XPathExprOr, Function, css_to_xpath, Element
from lxml import cssselect
class JQueryPseudo(Pseudo):
"""This class is used to implement the css pseudo classes
(:first, :last, ...) that are not defined in the css standard,
but are defined in the jquery API.
"""
def _xpath_first(self, xpath):
"""Matches the first selected element.
"""
xpath.add_post_condition('position() = 1')
return xpath
def _xpath_last(self, xpath):
"""Matches the last selected element.
"""
xpath.add_post_condition('position() = last()')
return xpath
def _xpath_even(self, xpath):
"""Matches even elements, zero-indexed.
"""
# the first element is 1 in xpath and 0 in python and js
xpath.add_post_condition('position() mod 2 = 1')
return xpath
def _xpath_odd(self, xpath):
"""Matches odd elements, zero-indexed.
"""
xpath.add_post_condition('position() mod 2 = 0')
return xpath
def _xpath_checked(self, xpath):
"""Matches odd elements, zero-indexed.
"""
xpath.add_condition("@checked and name(.) = 'input'")
return xpath
def _xpath_selected(self, xpath):
"""Matches all elements that are selected.
"""
xpath.add_condition("@selected and name(.) = 'option'")
return xpath
def _xpath_disabled(self, xpath):
"""Matches all elements that are disabled.
"""
xpath.add_condition("@disabled")
return xpath
def _xpath_enabled(self, xpath):
"""Matches all elements that are enabled.
"""
xpath.add_condition("not(@disabled) and name(.) = 'input'")
return xpath
def _xpath_file(self, xpath):
"""Matches all input elements of type file.
"""
xpath.add_condition("@type = 'file' and name(.) = 'input'")
return xpath
def _xpath_input(self, xpath):
"""Matches all input elements.
"""
xpath.add_condition("(name(.) = 'input' or name(.) = 'select') "
+ "or (name(.) = 'textarea' or name(.) = 'button')")
return xpath
def _xpath_button(self, xpath):
"""Matches all button input elements and the button element.
"""
xpath.add_condition("(@type = 'button' and name(.) = 'input') "
+ "or name(.) = 'button'")
return xpath
def _xpath_radio(self, xpath):
"""Matches all radio input elements.
"""
xpath.add_condition("@type = 'radio' and name(.) = 'input'")
return xpath
def _xpath_text(self, xpath):
"""Matches all text input elements.
"""
xpath.add_condition("@type = 'text' and name(.) = 'input'")
return xpath
def _xpath_checkbox(self, xpath):
"""Matches all checkbox input elements.
"""
xpath.add_condition("@type = 'checkbox' and name(.) = 'input'")
return xpath
def _xpath_password(self, xpath):
"""Matches all password input elements.
"""
xpath.add_condition("@type = 'password' and name(.) = 'input'")
return xpath
def _xpath_submit(self, xpath):
"""Matches all submit input elements.
"""
xpath.add_condition("@type = 'submit' and name(.) = 'input'")
return xpath
def _xpath_image(self, xpath):
"""Matches all image input elements.
"""
xpath.add_condition("@type = 'image' and name(.) = 'input'")
return xpath
def _xpath_reset(self, xpath):
"""Matches all reset input elements.
"""
xpath.add_condition("@type = 'reset' and name(.) = 'input'")
return xpath
def _xpath_header(self, xpath):
"""Matches all header elelements (h1, ..., h6)
"""
# this seems kind of brute-force, is there a better way?
xpath.add_condition("(name(.) = 'h1' or name(.) = 'h2' or name (.) = 'h3') "
+ "or (name(.) = 'h4' or name (.) = 'h5' or name(.) = 'h6')")
return xpath
def _xpath_parent(self, xpath):
"""Match all elements that contain other elements
"""
xpath.add_condition("count(child::*) > 0")
return xpath
def _xpath_empty(self, xpath):
"""Match all elements that do not contain other elements
"""
xpath.add_condition("count(child::*) = 0")
return xpath
cssselect.Pseudo = JQueryPseudo
class JQueryFunction(Function):
"""Represents selector:name(expr) that are present in JQuery but not in the
css standard.
"""
def _xpath_eq(self, xpath, expr):
"""Matches a single element by its index.
"""
xpath.add_post_condition('position() = %s' % int(expr+1))
return xpath
def _xpath_gt(self, xpath, expr):
"""Matches all elements with an index over the given one.
"""
xpath.add_post_condition('position() > %s' % int(expr+1))
return xpath
def _xpath_lt(self, xpath, expr):
"""Matches all elements with an index below the given one.
"""
xpath.add_post_condition('position() < %s' % int(expr+1))
return xpath
def _xpath_contains(self, xpath, expr):
"""Matches all elements that contain the given text
"""
xpath.add_post_condition("contains(text(), '%s')" % str(expr))
return xpath
cssselect.Function = JQueryFunction
class AdvancedXPathExpr(XPathExpr):
def __init__(self, prefix=None, path=None, element='*', condition=None,
post_condition=None, star_prefix=False):
self.prefix = prefix
self.path = path
self.element = element
self.condition = condition
self.post_condition = post_condition
self.star_prefix = star_prefix
def add_post_condition(self, post_condition):
if self.post_condition:
self.post_condition = '%s and (%s)' % (self.post_condition,
post_condition)
else:
self.post_condition = post_condition
def __str__(self):
path = XPathExpr.__str__(self)
if self.post_condition:
path = '(%s)[%s]' % (path, self.post_condition)
return path
def join(self, combiner, other):
XPathExpr.join(self, combiner, other)
self.post_condition = other.post_condition
cssselect.XPathExpr = AdvancedXPathExpr
class AdvancedXPathExprOr(XPathExprOr):
def __init__(self, items, prefix=None):
self.prefix = prefix = prefix or ''
self.items = items
def __str__(self):
prefix = self.prefix or ''
for item in self.items:
item.prefix = prefix
return ' | '.join([str(i) for i in self.items])
cssselect.XPathExprOr = AdvancedXPathExprOr
class JQueryElement(Element):
"""
Represents namespace|element
"""
def xpath(self):
if self.namespace == '*':
el = self.element
else:
# FIXME: Should we lowercase here?
el = '%s:%s' % (self.namespace, self.element)
return AdvancedXPathExpr(element=el)
cssselect.Element = JQueryElement
def selector_to_xpath(selector, prefix='descendant-or-self::'):
"""JQuery selector to xpath.
"""
selector = selector.replace('[@', '[')
return css_to_xpath(selector, prefix)
Jump to Line
Something went wrong with that request. Please try again.