Commits

Olivier Lauzanne committed e4eb183

append and prepend methods

Comments (0)

Files changed (1)

pyquery/__init__.py

 #
 # Distributed under the BSD license, see LICENSE.txt
 
-"""pyquery: a jquery-like library
+'''pyquery: a jquery-like library
 ====================================
 
 pyquery allows you to make jquery queries on xml documents.
 
 This is not (or at least not yet) a library to produce or interact with
 javascript code. I just liked the jquery API and I missed it in python so I
-told myself "Hey let's make jquery in python". This is the result.
+told myself 'Hey let's make jquery in python'. This is the result.
 
 It can be used for many purposes, one idea that I might try in the future is to
 use it for templating with pure http templates that you modify using pyquery.
 You can use the PyQuery class to load an xml document from a string, from a
 file or from an url.
 
-    >>> d = PyQuery(html="<html></html>")
+    >>> d = PyQuery(html='<html></html>')
     >>> d = PyQuery(url='http://w3c.org/')
-    >>> d = PyQuery(filename="test.html")
+    >>> d = PyQuery(filename='test.html')
 
 Now d is like the $ in jquery.
 
-    >>> d("#hello")
+    >>> d('#hello')
     [<p#hello.hello>]
-    >>> p = d("#hello")
+    >>> p = d('#hello')
     >>> p.html()
     'Hello world !'
-    >>> p.html("you know <a href='http://python.org/'>Python</a> rocks")
+    >>> p.html('you know <a href="http://python.org/">Python</a> rocks')
     [<p#hello.hello>]
     >>> p.html()
     'you know <a href="http://python.org/">Python</a> rocks'
 
 You can play with the attributes
 
-    >>> p.attr("id")
+    >>> p.attr('id')
     'hello'
-    >>> p.attr("id", "plop")
+    >>> p.attr('id', 'plop')
     [<p#plop.hello>]
-    >>> p.attr("id", "hello")
+    >>> p.attr('id', 'hello')
     [<p#hello.hello>]
 
 And the class
 
-    >>> p.addClass("toto")
+    >>> p.addClass('toto')
     [<p#hello.toto.hello>]
-    >>> p.toggleClass("titi toto")
+    >>> p.toggleClass('titi toto')
     [<p#hello.titi.hello>]
-    >>> p.removeClass("titi")
+    >>> p.removeClass('titi')
     [<p#hello.hello>]
 
 Or the style
 
-    >>> p.css("font-size", "15px")
+    >>> p.css('font-size', '15px')
     [<p#hello.hello>]
-    >>> p.attr("style")
+    >>> p.attr('style')
     'font-size: 15px'
-    >>> p.css({"font-size": "17px"})
+    >>> p.css({'font-size': '17px'})
     [<p#hello.hello>]
-    >>> p.attr("style")
+    >>> p.attr('style')
     'font-size: 17px'
 
-You can also add content to the end of a tag
+You can also add content to the end of tags
 
-    >>> p.append("hey there !")
+    >>> d('p').append('check out <a href="http://reddit.com/r/python"><span>reddit</span></a>')
+    [<p#hello.hello>, <p#test>]
+    >>> print d
+    <html>
+    ...
+    <p class="hello" id="hello" style="font-size: 17px">you know <a href="http://python.org/">Python</a> rockscheck out <a href="http://reddit.com/r/python"><span>reddit</span></a></p><p id="test">
+    hello <a href="http://python.org">python</a> !
+    check out <a href="http://python.org/">Python</a> rockscheck out <a href="http://reddit.com/r/python"><span>reddit</span></a></p>
+    ...
+
+
+Or to the beginning
+
+    >>> p.prepend('check out <a href="http://reddit.com/r/python">reddit</a>')
     [<p#hello.hello>]
-    >>> p.text()
-    'you know Python rocks'
-    >>> p.html("")
+    >>> p.html()
+    'check out <a href="http://reddit.com/r/python">reddit</a>you know ...'
+
+Prepend or append an element into an other
+
+    >>> p.prependTo(d('#test'))
     [<p#hello.hello>]
-    >>> p.append("hey there !")
-    [<p#hello.hello>]
-    >>> p.text()
-    ''
+    >>> d('#test').html()
+    '<p class="hello" ...</p>...hello...python...'
+
 
 And you can get back the modified html
 
     ...style="font-size: 17px"...
     </html>
 
-
-
 For more documentation about the API use the jquery website http://jquery.com/
 
 You can run the doctests that you just read by running the test function or by
-running "$ python pyquery.py" in the pyquery source folder.
+running '$ python pyquery.py' in the pyquery source folder.
 
 The reference I'm using for the API now is ... the color cheat sheet
 http://colorcharge.com/wp-content/uploads/2007/12/jquery12_colorcharge.png
 - CORE UI EFFECTS: did hide and show the rest doesn't really makes sense on
   server side
 - AJAX: don't make sense on server side
-"""
-
-from types import DictionaryType
+'''
 
 from lxml.cssselect import css_to_xpath
 from lxml import etree
+from copy import deepcopy
 
 def selector_to_xpath(selector):
-    """JQuery selector to xpath.
+    '''JQuery selector to xpath.
     TODO: patch cssselect to add :first, :last, ...
-    """
+    '''
     selector = selector.replace('[@', '[')
     return css_to_xpath(selector)
 
 
 class PyQuery(object):
-    """See the pyquery module docstring.
-    """
+    '''See the pyquery module docstring.
+    '''
     def __init__(self, html=None, filename=None, url=None):
         if html:
             pass
             html = urlopen(url).read()
         self.root = etree.fromstring(html)
 
-    def __call__(self, selector="", context=None):
+    def __call__(self, selector='', context=None):
         if context == None:
             context = PyQueryResults([self.root])
         if not selector:
 
 
 class PyQueryResults(list):
-    """Class returned when calling an instance of PyQuery.
+    '''Class returned when calling an instance of PyQuery.
 
     See the pyquery module docstring for more details.
-    """
+    '''
     def __repr__(self):
         r = []
         for el in self:
             return self[0].get(name)
         elif value == '':
             return self.removeAttr(name)
-        elif type(name) == DictionaryType:
+        elif type(name) == dict:
             for tag in self:
                 for key, value in name.items():
                     tag.set(key, value)
     # CSS #
     #######
     def height(self, value=None):
-        return self.attr("height", value)
+        return self.attr('height', value)
 
     def width(self, value=None):
-        return self.attr("width", value)
+        return self.attr('width', value)
 
     def addClass(self, value):
         for tag in self:
         return self
 
     def css(self, attr, value=None):
-        if type(attr) == DictionaryType:
+        if type(attr) == dict:
             for tag in self:
                 stripped_keys = [key.strip() for key in attr.keys()]
                 current = [el.strip()
     # HTML #
     ########
     def val(self, value=None):
-        return self.attr("value", value)
+        return self.attr('value', value)
 
     def html(self, value=None):
         if value == None:
             children = tag.getchildren()
             if not children:
                 return tag.text
-            html = '\n'.join(map(etree.tostring, children))
-            if tag.text and tag.text.strip():
-                html = tag.text + html
-            if tag.tail and tag.tail.strip():
-                html = html + tag.tail
+            html = tag.text or ''
+            html += ''.join(map(etree.tostring, children))
             return html
 
         for tag in self:
     ################
 
     def append(self, value):
-        root = etree.fromstring('<root>' + value + '</root>')
-        children = root.getchildren()
-        for tag in self:
-            tag.text += root.text
-            tag.extend(children)
-            if tag.tail and root.tail:
-                tag.tail += root.tail
-            elif root.tail:
-                tag.tail = root.tail
+        is_pyquery_results = isinstance(value, PyQueryResults)
+        is_string = isinstance(value, basestring)
+        assert is_string or is_pyquery_results
+        if is_string:
+            root = etree.fromstring('<root>' + value + '</root>')
+        elif is_pyquery_results:
+            root = value
+        if hasattr(root, 'text') and isinstance(root.text, basestring):
+            root_text = root.text
+        else:
+            root_text = ''
+        for i, tag in enumerate(self):
+            if len(tag) > 0: # if the tag has children
+                last_child = tag[-1]
+                if not last_child.tail:
+                    last_child.tail = ''
+                last_child.tail += root_text
+            else:
+                if not tag.text:
+                    tag.text = ''
+                tag.text += root_text
+            if i > 0:
+                root = deepcopy(list(root))
+            tag.extend(root)
+            root = tag[-len(root):]
+
         return self
 
+    def appendTo(self, value):
+        value.append(self)
+        return self
+
+    def prepend(self, value):
+        is_pyquery_results = isinstance(value, PyQueryResults)
+        if isinstance(value, basestring):
+            root = etree.fromstring('<root>' + value + '</root>')
+        elif is_pyquery_results:
+            root = value
+        if hasattr(root, 'text') and isinstance(root.text, basestring):
+            root_text = root.text
+        else:
+            root_text = ''
+        for i, tag in enumerate(self):
+            if not tag.text:
+                tag.text = ''
+            if len(root) > 0:
+                root[-1].tail = tag.text
+                tag.text = root_text
+            else:
+                tag.text = root_text + tag.text
+            if i > 0:
+                root = deepcopy(list(root))
+            tag[:0] = root
+            root = tag[:len(root)]
+
+        return self
+
+    def prependTo(self, value):
+        value.prepend(self)
+        return self
 
 def test():
     import doctest
-    doctest.testmod(optionflags=doctest.ELLIPSIS)
+    print '%s fails out of %s' % doctest.testmod(optionflags=doctest.ELLIPSIS)
 
 if __name__ == '__main__':
     test()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.