[ADD] hasclass() xpath function
Server-side, view extension is done via xpath. This includes "template" views full of HTML. HTML elements often have a bunch of classes, sometimes even semantic (!). XPath is generally great, but specifically lousy at dealing with space-separated values: in standard XPath 1.0 to know if an element has a class 'foo' the predicate is: contains(concat(' ', normalize-space(@class), ' '), ' foo ') and this has to be fully duplicated if there's a second class involved. Things are slightly better with EXSLT/XPath 2.0 and tokenize, but still not great: tokenize(@class, '\s+') = 'foo' and the equality check is very weird when unaware of XPath's evaluation rules. ``hasclass`` makes this much simpler to deal with: to get any ``foo`` node with the class ``bar`` is as simple as: //foo[hasclass('bar') and it can take multiple class, as with e.g. jquery it will return elements with all specified classes. Beware though, the predicate function will be called once for each element to check, since it's implemented in pure python and not profiled elements should be filtered as much as possible before this point.
This commit is contained in:
parent
9a1413576a
commit
9cefa76988
|
@ -86,6 +86,16 @@ class view_custom(osv.osv):
|
|||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX ir_ui_view_custom_user_id_ref_id ON ir_ui_view_custom (user_id, ref_id)')
|
||||
|
||||
def _hasclass(context, *cls):
|
||||
""" Checks if the context node has all the classes passed as arguments
|
||||
"""
|
||||
node_classes = set(context.context_node.attrib.get('class', '').split())
|
||||
|
||||
return node_classes.issuperset(cls)
|
||||
|
||||
xpath_utils = etree.FunctionNamespace(None)
|
||||
xpath_utils['hasclass'] = _hasclass
|
||||
|
||||
class view(osv.osv):
|
||||
_name = 'ir.ui.view'
|
||||
|
||||
|
|
|
@ -804,3 +804,26 @@ class test_views(ViewCase):
|
|||
E.button(name="action_next", type="object", string="New button")),
|
||||
string="Replacement title", version="7.0"
|
||||
))
|
||||
|
||||
class TestXPathExtentions(common.BaseCase):
|
||||
def test_hasclass(self):
|
||||
tree = E.node(
|
||||
E.node({'class': 'foo bar baz'}),
|
||||
E.node({'class': 'foo bar'}),
|
||||
{'class': "foo"})
|
||||
|
||||
self.assertEqual(
|
||||
len(tree.xpath('//node[hasclass("foo")]')),
|
||||
3)
|
||||
self.assertEqual(
|
||||
len(tree.xpath('//node[hasclass("bar")]')),
|
||||
2)
|
||||
self.assertEqual(
|
||||
len(tree.xpath('//node[hasclass("baz")]')),
|
||||
1)
|
||||
self.assertEqual(
|
||||
len(tree.xpath('//node[hasclass("foo")][not(hasclass("bar"))]')),
|
||||
1)
|
||||
self.assertEqual(
|
||||
len(tree.xpath('//node[hasclass("foo", "baz")]')),
|
||||
1)
|
||||
|
|
Loading…
Reference in New Issue