home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_1996 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  13.6 KB  |  556 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. import re
  5. import copy
  6.  
  7. try:
  8.     from urlparse import urlsplit
  9. except ImportError:
  10.     from urllib.parse import urlsplit
  11.  
  12. from lxml import etree
  13. from lxml.html import defs
  14. from lxml.html import fromstring, tostring, XHTML_NAMESPACE
  15. from lxml.html import _nons, _transform_result
  16.  
  17. try:
  18.     set
  19. except NameError:
  20.     from sets import Set as set
  21.  
  22.  
  23. try:
  24.     unichr = __builtins__['unichr']
  25. except (NameError, KeyError):
  26.     unichr = chr
  27.  
  28.  
  29. try:
  30.     unicode = __builtins__['unicode']
  31. except (NameError, KeyError):
  32.     unicode = str
  33.  
  34.  
  35. try:
  36.     bytes = __builtins__['bytes']
  37. except (NameError, KeyError):
  38.     bytes = str
  39.  
  40.  
  41. try:
  42.     basestring = __builtins__['basestring']
  43. except (NameError, KeyError):
  44.     basestring = (str, bytes)
  45.  
  46. __all__ = [
  47.     'clean_html',
  48.     'clean',
  49.     'Cleaner',
  50.     'autolink',
  51.     'autolink_html',
  52.     'word_break',
  53.     'word_break_html']
  54. _css_javascript_re = re.compile('expression\\s*\\(.*?\\)', re.S | re.I)
  55. _css_import_re = re.compile('@\\s*import', re.I)
  56. _javascript_scheme_re = re.compile('\\s*(?:javascript|jscript|livescript|vbscript|about|mocha):', re.I)
  57. _substitute_whitespace = re.compile('\\s+').sub
  58. _conditional_comment_re = re.compile('\\[if[\\s\\n\\r]+.*?][\\s\\n\\r]*>', re.I | re.S)
  59. _find_styled_elements = etree.XPath('descendant-or-self::*[@style]')
  60. _find_external_links = etree.XPath("descendant-or-self::a  [normalize-space(@href) and substring(normalize-space(@href),1,1) != '#'] |descendant-or-self::x:a[normalize-space(@href) and substring(normalize-space(@href),1,1) != '#']", namespaces = {
  61.     'x': XHTML_NAMESPACE })
  62.  
  63. class Cleaner(object):
  64.     scripts = True
  65.     javascript = True
  66.     comments = True
  67.     style = False
  68.     links = True
  69.     meta = True
  70.     page_structure = True
  71.     processing_instructions = True
  72.     embedded = True
  73.     frames = True
  74.     forms = True
  75.     annoying_tags = True
  76.     remove_tags = None
  77.     allow_tags = None
  78.     remove_unknown_tags = True
  79.     safe_attrs_only = True
  80.     add_nofollow = False
  81.     host_whitelist = ()
  82.     whitelist_tags = set([
  83.         'iframe',
  84.         'embed'])
  85.     
  86.     def __init__(self, **kw):
  87.         for name, value in kw.items():
  88.             if not hasattr(self, name):
  89.                 raise TypeError('Unknown parameter: %s=%r' % (name, value))
  90.             hasattr(self, name)
  91.             setattr(self, name, value)
  92.         
  93.  
  94.     _tag_link_attrs = dict(script = 'src', link = 'href', applet = [
  95.         'code',
  96.         'object'], iframe = 'src', embed = 'src', layer = 'src', a = 'href')
  97.     
  98.     def __call__(self, doc):
  99.         if hasattr(doc, 'getroot'):
  100.             doc = doc.getroot()
  101.         
  102.         for el in doc.iter():
  103.             tag = el.tag
  104.             if isinstance(tag, basestring):
  105.                 el.tag = _nons(tag)
  106.                 continue
  107.         
  108.         for el in doc.iter('image'):
  109.             el.tag = 'img'
  110.         
  111.         if not self.comments:
  112.             self.kill_conditional_comments(doc)
  113.         
  114.         kill_tags = set()
  115.         if not self.remove_tags:
  116.             pass
  117.         remove_tags = set(())
  118.         if self.allow_tags:
  119.             allow_tags = set(self.allow_tags)
  120.         else:
  121.             allow_tags = set()
  122.         if self.scripts:
  123.             kill_tags.add('script')
  124.         
  125.         if self.safe_attrs_only:
  126.             safe_attrs = set(defs.safe_attrs)
  127.             for el in doc.iter():
  128.                 attrib = el.attrib
  129.                 for aname in attrib.keys():
  130.                     if aname not in safe_attrs:
  131.                         del attrib[aname]
  132.                         continue
  133.                 
  134.             
  135.         
  136.         if self.javascript:
  137.             if not self.safe_attrs_only:
  138.                 for el in doc.iter():
  139.                     attrib = el.attrib
  140.                     for aname in attrib.keys():
  141.                         if aname.startswith('on'):
  142.                             del attrib[aname]
  143.                             continue
  144.                     
  145.                 
  146.             
  147.             doc.rewrite_links(self._remove_javascript_link, resolve_base_href = False)
  148.             if not self.style:
  149.                 for el in _find_styled_elements(doc):
  150.                     old = el.get('style')
  151.                     new = _css_javascript_re.sub('', old)
  152.                     new = _css_import_re.sub('', old)
  153.                     if self._has_sneaky_javascript(new):
  154.                         del el.attrib['style']
  155.                         continue
  156.                     if new != old:
  157.                         el.set('style', new)
  158.                         continue
  159.                 
  160.                 for el in list(doc.iter('style')):
  161.                     if el.get('type', '').lower().strip() == 'text/javascript':
  162.                         el.drop_tree()
  163.                         continue
  164.                     
  165.                     if not el.text:
  166.                         pass
  167.                     old = ''
  168.                     new = _css_javascript_re.sub('', old)
  169.                     new = _css_import_re.sub('', old)
  170.                     if self._has_sneaky_javascript(new):
  171.                         el.text = '/* deleted */'
  172.                         continue
  173.                     if new != old:
  174.                         el.text = new
  175.                         continue
  176.                 
  177.             
  178.         
  179.         if self.comments or self.processing_instructions:
  180.             kill_tags.add(etree.Comment)
  181.         
  182.         if self.processing_instructions:
  183.             kill_tags.add(etree.ProcessingInstruction)
  184.         
  185.         if self.style:
  186.             kill_tags.add('style')
  187.             etree.strip_attributes(doc, 'style')
  188.         
  189.         if self.links:
  190.             kill_tags.add('link')
  191.         elif self.style or self.javascript:
  192.             for el in list(doc.iter('link')):
  193.                 if 'stylesheet' in el.get('rel', '').lower():
  194.                     el.drop_tree()
  195.                     continue
  196.             
  197.         
  198.         if self.meta:
  199.             kill_tags.add('meta')
  200.         
  201.         if self.page_structure:
  202.             remove_tags.update(('head', 'html', 'title'))
  203.         
  204.         if self.embedded:
  205.             for el in list(doc.iter('param')):
  206.                 found_parent = False
  207.                 parent = el.getparent()
  208.                 while parent is not None and parent.tag not in ('applet', 'object'):
  209.                     parent = parent.getparent()
  210.                 if parent is None:
  211.                     el.drop_tree()
  212.                     continue
  213.             
  214.             kill_tags.update(('applet',))
  215.             remove_tags.update(('iframe', 'embed', 'layer', 'object', 'param'))
  216.         
  217.         if self.frames:
  218.             kill_tags.update(defs.frame_tags)
  219.         
  220.         if self.forms:
  221.             remove_tags.add('form')
  222.             kill_tags.update(('button', 'input', 'select', 'textarea'))
  223.         
  224.         if self.annoying_tags:
  225.             remove_tags.update(('blink', 'marque'))
  226.         
  227.         _remove = []
  228.         _kill = []
  229.         for el in doc.iter():
  230.             if el.tag in kill_tags:
  231.                 if self.allow_element(el):
  232.                     continue
  233.                 
  234.                 _kill.append(el)
  235.                 continue
  236.             if el.tag in remove_tags:
  237.                 if self.allow_element(el):
  238.                     continue
  239.                 
  240.                 _remove.append(el)
  241.                 continue
  242.         
  243.         if _remove and _remove[0] == doc:
  244.             el = _remove.pop(0)
  245.             el.tag = 'div'
  246.             el.attrib.clear()
  247.         elif _kill and _kill[0] == doc:
  248.             el = _kill.pop(0)
  249.             if el.tag != 'html':
  250.                 el.tag = 'div'
  251.             
  252.             el.clear()
  253.         
  254.         for el in _kill:
  255.             el.drop_tree()
  256.         
  257.         for el in _remove:
  258.             el.drop_tag()
  259.         
  260.         allow_tags = self.allow_tags
  261.         if self.remove_unknown_tags:
  262.             if allow_tags:
  263.                 raise ValueError('It does not make sense to pass in both allow_tags and remove_unknown_tags')
  264.             allow_tags
  265.             allow_tags = set(defs.tags)
  266.         
  267.         if allow_tags:
  268.             bad = []
  269.             for el in doc.iter():
  270.                 if el.tag not in allow_tags:
  271.                     bad.append(el)
  272.                     continue
  273.             
  274.             for el in bad:
  275.                 el.drop_tag()
  276.             
  277.         
  278.         if self.add_nofollow:
  279.             for el in _find_external_links(doc):
  280.                 if not self.allow_follow(el):
  281.                     el.set('rel', 'nofollow')
  282.                     continue
  283.             
  284.         
  285.  
  286.     
  287.     def allow_follow(self, anchor):
  288.         return False
  289.  
  290.     
  291.     def allow_element(self, el):
  292.         if el.tag not in self._tag_link_attrs:
  293.             return False
  294.         attr = self._tag_link_attrs[el.tag]
  295.         if isinstance(attr, (list, tuple)):
  296.             for one_attr in attr:
  297.                 url = el.get(one_attr)
  298.                 if not url:
  299.                     return False
  300.                 if not self.allow_embedded_url(el, url):
  301.                     return False
  302.             
  303.             return True
  304.         url = el.get(attr)
  305.         if not url:
  306.             return False
  307.         return self.allow_embedded_url(el, url)
  308.  
  309.     
  310.     def allow_embedded_url(self, el, url):
  311.         if self.whitelist_tags is not None and el.tag not in self.whitelist_tags:
  312.             return False
  313.         (scheme, netloc, path, query, fragment) = urlsplit(url)
  314.         netloc = netloc.lower().split(':', 1)[0]
  315.         if scheme not in ('http', 'https'):
  316.             return False
  317.         if netloc in self.host_whitelist:
  318.             return True
  319.         return False
  320.  
  321.     
  322.     def kill_conditional_comments(self, doc):
  323.         bad = []
  324.         self._kill_elements(doc, (lambda el: _conditional_comment_re.search(el.text)), etree.Comment)
  325.  
  326.     
  327.     def _kill_elements(self, doc, condition, iterate = None):
  328.         bad = []
  329.         for el in doc.iter(iterate):
  330.             if condition(el):
  331.                 bad.append(el)
  332.                 continue
  333.         
  334.         for el in bad:
  335.             el.drop_tree()
  336.         
  337.  
  338.     
  339.     def _remove_javascript_link(self, link):
  340.         new = _substitute_whitespace('', link)
  341.         if _javascript_scheme_re.search(new):
  342.             return ''
  343.         return link
  344.  
  345.     _substitute_comments = re.compile('/\\*.*?\\*/', re.S).sub
  346.     
  347.     def _has_sneaky_javascript(self, style):
  348.         style = self._substitute_comments('', style)
  349.         style = style.replace('\\', '')
  350.         style = _substitute_whitespace('', style)
  351.         style = style.lower()
  352.         if 'javascript:' in style:
  353.             return True
  354.         if 'expression(' in style:
  355.             return True
  356.         return False
  357.  
  358.     
  359.     def clean_html(self, html):
  360.         result_type = type(html)
  361.         if isinstance(html, basestring):
  362.             doc = fromstring(html)
  363.         else:
  364.             doc = copy.deepcopy(html)
  365.         self(doc)
  366.         return _transform_result(result_type, doc)
  367.  
  368.  
  369. clean = Cleaner()
  370. clean_html = clean.clean_html
  371. _link_regexes = [
  372.     re.compile('(?P<body>https?://(?P<host>[a-z0-9._-]+)(?:/[/\\-_.,a-z0-9%&?;=~]*)?(?:\\([/\\-_.,a-z0-9%&?;=~]*\\))?)', re.I),
  373.     re.compile('mailto:(?P<body>[a-z0-9._-]+@(?P<host>[a-z0-9_._]+[a-z]))', re.I)]
  374. _avoid_elements = [
  375.     'textarea',
  376.     'pre',
  377.     'code',
  378.     'head',
  379.     'select',
  380.     'a']
  381. _avoid_hosts = [
  382.     re.compile('^localhost', re.I),
  383.     re.compile('\\bexample\\.(?:com|org|net)$', re.I),
  384.     re.compile('^127\\.0\\.0\\.1$')]
  385. _avoid_classes = [
  386.     'nolink']
  387.  
  388. def autolink(el, link_regexes = _link_regexes, avoid_elements = _avoid_elements, avoid_hosts = _avoid_hosts, avoid_classes = _avoid_classes):
  389.     if el.tag in avoid_elements:
  390.         return None
  391.     class_name = el.get('class')
  392.     for child in list(el):
  393.         autolink(child, link_regexes = link_regexes, avoid_elements = avoid_elements, avoid_hosts = avoid_hosts, avoid_classes = avoid_classes)
  394.         if child.tail:
  395.             (text, tail_children) = _link_text(child.tail, link_regexes, avoid_hosts, factory = el.makeelement)
  396.             if tail_children:
  397.                 child.tail = text
  398.                 index = el.index(child)
  399.                 el[index + 1:index + 1] = tail_children
  400.             
  401.         tail_children
  402.     
  403.     if el.text:
  404.         (text, pre_children) = _link_text(el.text, link_regexes, avoid_hosts, factory = el.makeelement)
  405.         if pre_children:
  406.             el.text = text
  407.             el[:0] = pre_children
  408.         
  409.     
  410.  
  411.  
  412. def _link_text(text, link_regexes, avoid_hosts, factory):
  413.     leading_text = ''
  414.     links = []
  415.     last_pos = 0
  416.     while None:
  417.         (best_match, best_pos) = (None, None)
  418.         for regex in link_regexes:
  419.             regex_pos = last_pos
  420.             while None:
  421.                 match = regex.search(text, pos = regex_pos)
  422.                 if match is None:
  423.                     break
  424.                 
  425.                 host = match.group('host')
  426.                 for host_regex in avoid_hosts:
  427.                     if host_regex.search(host):
  428.                         regex_pos = match.end()
  429.                         break
  430.                         continue
  431.                 else:
  432.                     break
  433.                 continue
  434.                 if match is None:
  435.                     continue
  436.                 
  437.             if best_pos is None or match.start() < best_pos:
  438.                 best_match = match
  439.                 best_pos = match.start()
  440.                 continue
  441.         
  442.         if best_match is None:
  443.             if links:
  444.                 links[-1].tail = text
  445.             else:
  446.                 leading_text = text
  447.             break
  448.         
  449.         link = best_match.group(0)
  450.         end = best_match.end()
  451.         if link.endswith('.') or link.endswith(','):
  452.             end -= 1
  453.             link = link[:-1]
  454.         
  455.         prev_text = text[:best_match.start()]
  456.         if links:
  457.             links[-1].tail = prev_text
  458.         else:
  459.             leading_text = prev_text
  460.         anchor = factory('a')
  461.         body = best_match.group('body')
  462.         if not body:
  463.             body = link
  464.         
  465.         if body.endswith('.') or body.endswith(','):
  466.             body = body[:-1]
  467.         
  468.         anchor.text = body
  469.         links.append(anchor)
  470.         text = text[end:]
  471.         continue
  472.         return (leading_text, links)
  473.  
  474.  
  475. def autolink_html(html, *args, **kw):
  476.     result_type = type(html)
  477.     if isinstance(html, basestring):
  478.         doc = fromstring(html)
  479.     else:
  480.         doc = copy.deepcopy(html)
  481.     autolink(doc, *args, **kw)
  482.     return _transform_result(result_type, doc)
  483.  
  484. autolink_html.__doc__ = autolink.__doc__
  485. _avoid_word_break_elements = [
  486.     'pre',
  487.     'textarea',
  488.     'code']
  489. _avoid_word_break_classes = [
  490.     'nobreak']
  491.  
  492. def word_break(el, max_width = 40, avoid_elements = _avoid_word_break_elements, avoid_classes = _avoid_word_break_classes, break_character = unichr(8203)):
  493.     if el.tag in _avoid_word_break_elements:
  494.         return None
  495.     class_name = el.get('class')
  496.     if class_name:
  497.         dont_break = False
  498.         class_name = class_name.split()
  499.         for avoid in avoid_classes:
  500.             if avoid in class_name:
  501.                 dont_break = True
  502.                 break
  503.                 continue
  504.             el.tag in _avoid_word_break_elements
  505.         
  506.         if dont_break:
  507.             return None
  508.     
  509.     if el.text:
  510.         el.text = _break_text(el.text, max_width, break_character)
  511.     
  512.     for child in el:
  513.         word_break(child, max_width = max_width, avoid_elements = avoid_elements, avoid_classes = avoid_classes, break_character = break_character)
  514.         if child.tail:
  515.             child.tail = _break_text(child.tail, max_width, break_character)
  516.             continue
  517.     
  518.  
  519.  
  520. def word_break_html(html, *args, **kw):
  521.     result_type = type(html)
  522.     doc = fromstring(html)
  523.     word_break(doc, *args, **kw)
  524.     return _transform_result(result_type, doc)
  525.  
  526.  
  527. def _break_text(text, max_width, break_character):
  528.     words = text.split()
  529.     for word in words:
  530.         if len(word) > max_width:
  531.             replacement = _insert_break(word, max_width, break_character)
  532.             text = text.replace(word, replacement)
  533.             continue
  534.     
  535.     return text
  536.  
  537. _break_prefer_re = re.compile('[^a-z]', re.I)
  538.  
  539. def _insert_break(word, width, break_character):
  540.     orig_word = word
  541.     result = ''
  542.     while len(word) > width:
  543.         start = word[:width]
  544.         breaks = list(_break_prefer_re.finditer(start))
  545.         if breaks:
  546.             last_break = breaks[-1]
  547.             if last_break.end() > width - 10:
  548.                 start = word[:last_break.end()]
  549.             
  550.         
  551.         result += start + break_character
  552.         word = word[len(start):]
  553.     result += word
  554.     return result
  555.  
  556.