home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / xhtmltools.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  6.8 KB  |  182 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. import xml.sax.saxutils
  19. import xml.dom
  20. import re
  21. from urllib import quote, quote_plus, unquote
  22. from HTMLParser import HTMLParser
  23. import types
  24. import random
  25.  
  26. ##
  27. # very simple parser to convert HTML to XHTML
  28. class XHTMLifier(HTMLParser):
  29.     def convert(self,data, addTopTags=False, filterFontTags=False):
  30.         if addTopTags:
  31.             self.output = u'<html><head></head><body>'
  32.         else:
  33.             self.output = ''
  34.         self.stack = []
  35.         self.filterFontTags = filterFontTags
  36.         self.feed(data)
  37.         try:
  38.             self.close()
  39.         except:
  40.             print 'DTV: unexpected error while parsing html data.'
  41.         while len(self.stack) > 0:
  42.             temp = self.stack.pop()
  43.             self.output += u'</'+temp+'>'
  44.         if addTopTags:
  45.             self.output += u'</body></html>'
  46.         return self.output
  47.     def handle_starttag(self, tag, attrs):
  48.         if tag.lower() == 'br':
  49.             self.output += u'<br/>'
  50.         else:
  51.             if not (tag.lower() == 'font' and self.filterFontTags):
  52.                 self.output += u'<'+tag
  53.                 for attr in attrs:
  54.                     if attr[1] == None:
  55.                         self.output += u' '+attr[0]+u'='+xml.sax.saxutils.quoteattr(attr[0])
  56.                     else:
  57.                         self.output += u' '+attr[0]+u'='+xml.sax.saxutils.quoteattr(attr[1])
  58.                 self.output += u'>'
  59.             self.stack.append(tag)
  60.     def handle_endtag(self, tag):
  61.         if tag.lower() != 'br' and len(self.stack) > 1:
  62.             temp = self.stack.pop()
  63.             if not (tag.lower() == 'font' and self.filterFontTags):
  64.                 self.output += u'</'+temp+u'>'
  65.                 while temp != tag and len(self.stack) > 1:
  66.                     temp = self.stack.pop()
  67.                     self.output += u'</'+temp+u'>'
  68.     def handle_startendtag(self, tag, attrs):
  69.         self.output += u'<'+tag+u'/>'
  70.     def handle_data(self, data):
  71.         data = data.replace(u'&',u'&')
  72.         data = data.replace(u'<',u'<')
  73.         self.output += data
  74.     def handle_charref(self, name):
  75.         self.output += u'&#'+name+';'
  76.     def handle_entityref(self, name):
  77.         self.output += u'&'+name+';'
  78.  
  79. ##
  80. # Parses HTML entities in data
  81. def unescape(data):
  82.     return xml.sax.saxutils.unescape(data)
  83.  
  84. #
  85. # encodes string for use in a URL
  86. def urlencode(data):
  87.     if type(data) == unicode:
  88.         data = data.encode('utf-8','replace')
  89.     else:
  90.         data = str(data)
  91.     return unicode(quote(data))
  92.  
  93. #
  94. # gets a string from a URL
  95. def urldecode(data):
  96.     return unquote(data)
  97.  
  98. ##
  99. # Returns XHTMLified version of HTML document
  100. def xhtmlify(data,addTopTags=False, filterFontTags=False):
  101.     x = XHTMLifier()
  102.     return x.convert(data, addTopTags, filterFontTags)
  103.  
  104. xmlheaderRE = re.compile("^\<\?xml\s*(.*?)\s*\?\>(.*)", re.S)
  105. ##
  106. # Adds a <?xml ?> header to the given xml data or replaces an
  107. # existing one without a charset with one that has a charset
  108. def fixXMLHeader(data,charset):
  109.     header = xmlheaderRE.match(data)
  110.     if header is None:
  111.         #print "Adding header %s" % charset
  112.         return '<?xml version="1.0" encoding="%s"?>%s' % (charset,data)
  113.     else:
  114.         xmlDecl = header.expand('\\1')
  115.         theRest = header.expand('\\2')
  116.         if xmlDecl.find('encoding'):
  117.             return data
  118.         else:
  119.             #print "Changing header to include charset"
  120.             return '<?xml %s encoding="%s"?>%s' % (xmlDecl,charset,theRest)
  121.  
  122.  
  123. HTMLHeaderRE = re.compile(u"^(.*)\<\s*head\s*(.*?)\s*\>(.*?)\</\s*head\s*\>(.*)",re.I | re.S)
  124.  
  125. ##
  126. # Adds a <meta http-equiv="Content-Type" content="text/html;
  127. # charset=blah"> tag to an HTML document
  128. #
  129. # Since we're only feeding this to our own HTML Parser anyway, we
  130. # don't care that it might bung up XHTML
  131. def fixHTMLHeader(data,charset):
  132.     header = HTMLHeaderRE.match(data)
  133.     if header is None:
  134.         #Something is very wrong with this HTML
  135.         return data
  136.     else:
  137.         headTags = header.expand('\\3')
  138.         #This isn't exactly robust, but neither is scraping HTML
  139.         if headTags.lower().find('content-type') != -1:
  140.             return data
  141.         else:
  142.             #print " adding %s Content-Type to HTML" % charset
  143.             return header.expand('\\1<head \\2><meta http-equiv="Content-Type" content="text/html; charset=')+charset+header.expand('">\\3</head>\\4')
  144.  
  145. # Converts a Python dictionary to data suitable for a POST or GET submission
  146. def URLEncodeDict(orig):
  147.     output = []
  148.     for key in orig.keys():
  149.         if type(orig[key]) is types.ListType:
  150.             for value in orig[key]:
  151.                 output.append('%s=%s' % (quote_plus(key), quote_plus(value)))
  152.         else:
  153.             output.append('%s=%s' % (quote_plus(key), quote_plus(orig[key])))
  154.     return '&'.join(output)
  155.  
  156. def multipartEncode(postVars, files):
  157.     # Generate a random 64bit number for our boundaries
  158.     boundary = 'dp%s'% (hex(random.getrandbits(64))[2:-1])
  159.     output = []
  160.     if postVars is not None:
  161.         for key, value in postVars.items():
  162.             output.append('--%s\r\n' % boundary)
  163.             output.append('Content-Disposition: form-data; name="%s"\r\n\r\n' %
  164.                           quote_plus(key))
  165.             if isinstance(value, unicode):
  166.                 value = value.encode('utf8', 'xmlcharrefreplace')
  167.             output.append(value)
  168.             output.append('\r\n')
  169.     if files is not None:
  170.         for key in files.keys():
  171.             output.append('--%s\r\n' % boundary)
  172.             output.append('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' %
  173.                           (quote_plus(key),
  174.                            quote_plus(files[key]['filename'])))
  175.             output.append('Content-Type: %s\r\n\r\n' % files[key]['mimetype'])
  176.             
  177.             output.append(files[key]['handle'].read())
  178.             output.append('\r\n')
  179.             files[key]['handle'].close()
  180.     output.append('--%s--' % boundary)
  181.     return (''.join(output), boundary)
  182.