From 1d56e253d707580831ce37c061963707c5f0e7d3 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Fri, 24 Aug 2012 22:17:49 -0500 Subject: [PATCH] DIV.elements(replace=...), thanks Anthony --- CHANGELOG | 1 + VERSION | 2 +- gluon/html.py | 111 +++++++++++++++++++++++++++++++++++++------------- 3 files changed, 84 insertions(+), 30 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 41b5f636..eaf4baec 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -76,6 +76,7 @@ ### Other Improvements +- DIV(..).elements(...replace=...), thanks Anthony - new layout based on Twitter Bootstrap - New generic views: generic.ics (Mac Mail Calendar) and generic.map (Google Maps) - request.args(0,default=0, cast=int, otherwise=URL(...)), thanks Anthony diff --git a/VERSION b/VERSION index 31364eab..c517cf07 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-24 19:24:54) dev +Version 2.00.0 (2012-08-24 22:17:44) dev diff --git a/gluon/html.py b/gluon/html.py index e88d8604..8522b7a5 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -962,6 +962,48 @@ class DIV(XmlComponent): >>> for c in a.elements('input, select, textarea'): c['_disabled'] = 'disabled' >>> a.xml() '
' + + Elements that are matched can also be replaced or removed by specifying + a "replace" argument (note, a list of the original matching elements + is still returned as usual). + + >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc')))) + >>> b = a.elements('span.abc', replace=P('x', _class='xyz')) + >>> print a +

x

x

x

+ + "replace" can be a callable, which will be passed the original element and + should return a new element to replace it. + + >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc')))) + >>> b = a.elements('span.abc', replace=lambda el: P(el[0], _class='xyz')) + >>> print a +

x

y

z

+ + If replace=None, matching elements will be removed completely. + + >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc')))) + >>> b = a.elements('span', find='y', replace=None) + >>> print a +
x
z
+ + If a "find_text" argument is specified, elements will be searched for text + components that match find_text, and any matching text components will be + replaced (find_text is ignored if "replace" is not also specified). + Like the "find" argument, "find_text" can be a string or a compiled regex. + + >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc')))) + >>> b = a.elements(find_text=re.compile('x|y|z'), replace='hello') + >>> print a +
hello
hellohello
+ + If other attributes are specified along with find_text, then only components + that match the specified attributes will be searched for find_text. + + >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='efg'), SPAN('z', _class='abc')))) + >>> b = a.elements('span.efg', find_text=re.compile('x|y|z'), replace='hello') + >>> print a +
x
helloz
""" if len(args)==1: args = [a.strip() for a in args[0].split(',')] @@ -990,49 +1032,60 @@ class DIV(XmlComponent): return self.elements(*args,**kargs) # make a copy of the components matches = [] - first_only = False - if kargs.has_key("first_only"): - first_only = kargs["first_only"] - del kargs["first_only"] # check if the component has an attribute with the same # value as provided check = True - tag = getattr(self,'tag').replace("/","") + tag = getattr(self,'tag').replace('/', '') if args and tag not in args: check = False for (key, value) in kargs.items(): - if isinstance(value,(str,int)): - if self[key] != str(value): + if key not in ['first_only', 'replace', 'find_text']: + if isinstance(value, (str, int)): + if self[key] != str(value): + check = False + elif key in self.attributes: + if not value.search(str(self[key])): + check = False + else: check = False - elif key in self.attributes: - if not value.search(str(self[key])): - check = False - else: - check = False if 'find' in kargs: find = kargs['find'] + is_regex = not isinstance(find, (str, int)) for c in self.components: - if isinstance(find,(str,int)): - if isinstance(c,str) and str(find) in c: - check = True - else: - if isinstance(c,str) and find.search(c): - check = True + if (isinstance(c, str) and ((is_regex and find.search(c)) or + (str(find) in c))): + check = True # if found, return the component if check: matches.append(self) - if first_only: - return matches - # loop the copy - for c in self.components: - if isinstance(c, XmlComponent): - kargs['first_only'] = first_only - child_matches = c.elements( *args, **kargs ) - if first_only and len(child_matches) != 0: - return child_matches - matches.extend( child_matches ) - return matches + first_only = kargs.get('first_only', False) + replace = kargs.get('replace', False) + find_text = replace is not False and kargs.get('find_text', False) + is_regex = not isinstance(find_text, (str, int, bool)) + find_components = not (check and first_only) + def replace_component(i): + if replace is None: + del self[i] + elif callable(replace): + self[i] = replace(self[i]) + else: + self[i] = replace + # loop the components + if find_text or find_components: + for i, c in enumerate(self.components): + if check and find_text and isinstance(c, str) and \ + ((is_regex and find_text.search(c)) or (str(find_text) in c)): + replace_component(i) + if find_components and isinstance(c, XmlComponent): + child_matches = c.elements(*args, **kargs) + if len(child_matches): + if not find_text and replace is not False and child_matches[0] is c: + replace_component(i) + if first_only: + return child_matches + matches.extend(child_matches) + return matches def element(self, *args, **kargs): """