Merge from trunk

This commit is contained in:
Charles Haley 2013-01-19 07:05:08 +01:00
commit 2cd7cec3bd
110 changed files with 21651 additions and 21109 deletions

View File

@ -19,6 +19,81 @@
# new recipes: # new recipes:
# - title: # - title:
- version: 0.9.15
date: 2013-01-18
new features:
- title: "Linux MTP driver: Detect devices that have MTP interfaces even if their USB ids are not known"
- title: "Content server: Allow picking a random book by clicking the 'Random book' link on the start page. You can also refresh the random book page to get a new random book"
- title: "E-book viewer: Add an option to hide the toolbars in the viewer window (Preferences->Miscellaneous->Show controls in the viewr preferences). You can unhide them by right clicking in the viewer window."
- title: "Kobo driver: Speedup initial connect by avoiding unnecessary update of series metadata in some situations."
tickets: [1099190]
- title: "Get Books: Allow the store plugins to be dynamically loaded so that future website changes of a store dont require a calibre update to fix Get Books."
- title: "Wireless driver: Always replace file when resending a previously sent book to the device, even if the title/author have changed."
- title: "Add PocketBook Pro 912 driver."
tickets: [1099571]
- title: "When creating/exporting epub and mobi files, add the calibre book identifier as a special field in the book's metadata. This allows third party tools to identify the book record in calibre to which the file belongs."
- title: "Wireless driver: Add support for using the book uuid as the filename"
- title: "Remove the experimental tag from the subset fonts feature, since there has been only one reported problem (now fixed) with it in the two months since it was released"
bug fixes:
- title: "Get Books: Update the amazon, waterstones and libri.de plugins to account for website changes"
- title: "MOBI Input: Do not choke on MOBI files with incorrectly encoded titles."
tickets: [1100601]
- title: "Font subsetting: Fix a bug in the parsing of the GSUB table that could cause some ligatures to not be included in the subset font"
- title: "E-book-viewer: Fix TOC links without anchors not scrolling to the top of the current flow"
- title: "LIT Input: Handle lit files that set an incorrect XML mimetype for their text."
tickets: [1099621]
- title: "Catalogs: Fix 'X' being droppen from isbns on export"
tickets: [1098325]
- title: "Fix an error when editing date in the main book list and all visible dates are blank."
tickets: [1098675]
- title: "Fix calibre-smtp using incorrect escaping for non-ascii attachment filenames"
tickets: [1098478]
- title: "Conversion: When subsetting fonts, handle multiple @font-face rules referring to the same physical font"
- title: "Content server: Update metadata when serving azw3 files"
- title: "CHM Input: Handle chm files that contain files with url unsafe filenames."
tickets: [1100610]
- title: "Content server: Fix custom icons for top level categories incorrect."
tickets: [1095016]
- title: "Kobo driver: When resending a file to the device, update the filesize in the Kobo db to prevent the device from deleting the file."
tickets: [1100607]
improved recipes:
- The Chronicle of Higher Education
- Smithsonian Magazine
- Philosophy Now
- The Economist
- Business Week Magazine
new recipes:
- title: Asco de Vida
author: Krittika Goyal
- title: Schattenblick
author: ThB
- version: 0.9.14 - version: 0.9.14
date: 2013-01-11 date: 2013-01-11

View File

@ -38,7 +38,7 @@ class BusinessWeekMagazine(BasicNewsRecipe):
title=self.tag_to_string(div.a).strip() title=self.tag_to_string(div.a).strip()
url=div.a['href'] url=div.a['href']
soup0 = self.index_to_soup(url) soup0 = self.index_to_soup(url)
urlprint=soup0.find('li', attrs={'class':'print'}).a['href'] urlprint=soup0.find('li', attrs={'class':'print tracked'}).a['href']
articles.append({'title':title, 'url':urlprint, 'description':'', 'date':''}) articles.append({'title':title, 'url':urlprint, 'description':'', 'date':''})
@ -55,7 +55,7 @@ class BusinessWeekMagazine(BasicNewsRecipe):
title=self.tag_to_string(div.a).strip() title=self.tag_to_string(div.a).strip()
url=div.a['href'] url=div.a['href']
soup0 = self.index_to_soup(url) soup0 = self.index_to_soup(url)
urlprint=soup0.find('li', attrs={'class':'print'}).a['href'] urlprint=soup0.find('li', attrs={'class':'print tracked'}).a['href']
articles.append({'title':title, 'url':urlprint, 'description':desc, 'date':''}) articles.append({'title':title, 'url':urlprint, 'description':desc, 'date':''})
if articles: if articles:

View File

@ -41,10 +41,11 @@ class Economist(BasicNewsRecipe):
remove_tags = [ remove_tags = [
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']), dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
dict(attrs={'class':['dblClkTrk', 'ec-article-info', dict(attrs={'class':['dblClkTrk', 'ec-article-info',
'share_inline_header', 'related-items']}), 'share_inline_header', 'related-items',
'main-content-container']}),
{'class': lambda x: x and 'share-links-header' in x}, {'class': lambda x: x and 'share-links-header' in x},
] ]
keep_only_tags = [dict(id='ec-article-body')] keep_only_tags = [dict(name='article')]
no_stylesheets = True no_stylesheets = True
preprocess_regexps = [(re.compile('</html>.*', re.DOTALL), preprocess_regexps = [(re.compile('</html>.*', re.DOTALL),
lambda x:'</html>')] lambda x:'</html>')]

View File

@ -41,10 +41,11 @@ class Economist(BasicNewsRecipe):
remove_tags = [ remove_tags = [
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']), dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
dict(attrs={'class':['dblClkTrk', 'ec-article-info', dict(attrs={'class':['dblClkTrk', 'ec-article-info',
'share_inline_header', 'related-items']}), 'share_inline_header', 'related-items',
'main-content-container']}),
{'class': lambda x: x and 'share-links-header' in x}, {'class': lambda x: x and 'share-links-header' in x},
] ]
keep_only_tags = [dict(id='ec-article-body')] keep_only_tags = [dict(name='article')]
no_stylesheets = True no_stylesheets = True
preprocess_regexps = [(re.compile('</html>.*', re.DOTALL), preprocess_regexps = [(re.compile('</html>.*', re.DOTALL),
lambda x:'</html>')] lambda x:'</html>')]

View File

@ -9,14 +9,14 @@ msgstr ""
"Project-Id-Version: calibre\n" "Project-Id-Version: calibre\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-11-25 14:01+0000\n" "POT-Creation-Date: 2011-11-25 14:01+0000\n"
"PO-Revision-Date: 2012-12-28 09:13+0000\n" "PO-Revision-Date: 2013-01-12 08:34+0000\n"
"Last-Translator: Jellby <Unknown>\n" "Last-Translator: Jellby <Unknown>\n"
"Language-Team: Español; Castellano <>\n" "Language-Team: Español; Castellano <>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-29 05:00+0000\n" "X-Launchpad-Export-Date: 2013-01-13 04:37+0000\n"
"X-Generator: Launchpad (build 16378)\n" "X-Generator: Launchpad (build 16420)\n"
#. name for aaa #. name for aaa
msgid "Ghotuo" msgid "Ghotuo"
@ -9652,7 +9652,7 @@ msgstr "Haruku"
#. name for hrm #. name for hrm
msgid "Miao; Horned" msgid "Miao; Horned"
msgstr "" msgstr "Miao blanco"
#. name for hro #. name for hro
msgid "Haroi" msgid "Haroi"
@ -9756,7 +9756,7 @@ msgstr ""
#. name for huj #. name for huj
msgid "Miao; Northern Guiyang" msgid "Miao; Northern Guiyang"
msgstr "" msgstr "Miao de Guiyang septentrional"
#. name for huk #. name for huk
msgid "Hulung" msgid "Hulung"
@ -16280,7 +16280,7 @@ msgstr ""
#. name for mmr #. name for mmr
msgid "Miao; Western Xiangxi" msgid "Miao; Western Xiangxi"
msgstr "" msgstr "Miao de Xiangxi occidental"
#. name for mmt #. name for mmt
msgid "Malalamai" msgid "Malalamai"
@ -17064,7 +17064,7 @@ msgstr ""
#. name for muq #. name for muq
msgid "Miao; Eastern Xiangxi" msgid "Miao; Eastern Xiangxi"
msgstr "" msgstr "Miao de Xiangxi oriental"
#. name for mur #. name for mur
msgid "Murle" msgid "Murle"
@ -22836,7 +22836,7 @@ msgstr ""
#. name for sfm #. name for sfm
msgid "Miao; Small Flowery" msgid "Miao; Small Flowery"
msgstr "" msgstr "Pequeño miao florido"
#. name for sfs #. name for sfs
msgid "South African Sign Language" msgid "South African Sign Language"

View File

@ -4,7 +4,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = u'calibre' __appname__ = u'calibre'
numeric_version = (0, 9, 14) numeric_version = (0, 9, 15)
__version__ = u'.'.join(map(unicode, numeric_version)) __version__ = u'.'.join(map(unicode, numeric_version))
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>" __author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"

View File

@ -1402,7 +1402,6 @@ class StoreEmpikStore(StoreBase):
headquarters = 'PL' headquarters = 'PL'
formats = ['EPUB', 'MOBI', 'PDF'] formats = ['EPUB', 'MOBI', 'PDF']
affiliate = True
class StoreEscapeMagazineStore(StoreBase): class StoreEscapeMagazineStore(StoreBase):
name = 'EscapeMagazine' name = 'EscapeMagazine'

View File

@ -1706,6 +1706,7 @@ class KOBOTOUCH(KOBO):
def upload_books(self, files, names, on_card=None, end_session=True, def upload_books(self, files, names, on_card=None, end_session=True,
metadata=None): metadata=None):
debug_print('KoboTouch:upload_books - %d books'%(len(files))) debug_print('KoboTouch:upload_books - %d books'%(len(files)))
debug_print('KoboTouch:upload_books - files=', files)
result = super(KOBOTOUCH, self).upload_books(files, names, on_card, end_session, metadata) result = super(KOBOTOUCH, self).upload_books(files, names, on_card, end_session, metadata)
# debug_print('KoboTouch:upload_books - result=', result) # debug_print('KoboTouch:upload_books - result=', result)
@ -1717,7 +1718,7 @@ class KOBOTOUCH(KOBO):
'.kobo/KoboReader.sqlite'))) as connection: '.kobo/KoboReader.sqlite'))) as connection:
connection.text_factory = lambda x: unicode(x, "utf-8", "ignore") connection.text_factory = lambda x: unicode(x, "utf-8", "ignore")
cursor = connection.cursor() cursor = connection.cursor()
query = "DELETE FROM content WHERE ContentID = ? AND Accessibility = 1 AND IsDownloaded = 'false'" cleanup_query = "DELETE FROM content WHERE ContentID = ? AND Accessibility = 1 AND IsDownloaded = 'false'"
for fname, cycle in result: for fname, cycle in result:
show_debug = self.is_debugging_title(fname) show_debug = self.is_debugging_title(fname)
@ -1726,9 +1727,11 @@ class KOBOTOUCH(KOBO):
debug_print('KoboTouch:upload_books: fname=', fname) debug_print('KoboTouch:upload_books: fname=', fname)
debug_print('KoboTouch:upload_books: contentID=', contentID) debug_print('KoboTouch:upload_books: contentID=', contentID)
t = (contentID,) cleanup_values = (contentID,)
# debug_print('KoboTouch:upload_books: Delete record left if deleted on Touch') # debug_print('KoboTouch:upload_books: Delete record left if deleted on Touch')
cursor.execute(query, t) cursor.execute(cleanup_query, cleanup_values)
self.set_filesize_in_device_database(connection, contentID, fname)
connection.commit() connection.commit()
@ -2183,6 +2186,43 @@ class KOBOTOUCH(KOBO):
connection.commit() connection.commit()
cursor.close() cursor.close()
def set_filesize_in_device_database(self, connection, contentID, fpath):
show_debug = self.is_debugging_title(fpath)
if show_debug:
debug_print('KoboTouch:set_filesize_in_device_database contentID="%s"'%contentID)
test_query = 'SELECT ___FileSize ' \
'FROM content ' \
'WHERE ContentID = ? ' \
' AND ContentType = 6'
test_values = (contentID, )
updatequery = 'UPDATE content ' \
'SET ___FileSize = ? ' \
'WHERE ContentId = ? ' \
'AND ContentType = 6'
cursor = connection.cursor()
cursor.execute(test_query, test_values)
result = cursor.fetchone()
if result is None:
if show_debug:
debug_print(' Did not find a record - new book on device')
elif os.path.exists(fpath):
file_size = os.stat(self.normalize_path(fpath)).st_size
if show_debug:
debug_print(' Found a record - will update - ___FileSize=', result[0], ' file_size=', file_size)
if file_size != int(result[0]):
update_values = (file_size, contentID, )
cursor.execute(updatequery, update_values)
if show_debug:
debug_print(' Size updated.')
connection.commit()
cursor.close()
# debug_print("KoboTouch:set_filesize_in_device_database - end")
def delete_empty_bookshelves(self, connection): def delete_empty_bookshelves(self, connection):
debug_print("KoboTouch:delete_empty_bookshelves - start") debug_print("KoboTouch:delete_empty_bookshelves - start")

View File

@ -100,7 +100,7 @@ class CHMReader(CHMFile):
def ExtractFiles(self, output_dir=os.getcwdu(), debug_dump=False): def ExtractFiles(self, output_dir=os.getcwdu(), debug_dump=False):
html_files = set([]) html_files = set([])
try: try:
x = self.GetEncoding() x = self.get_encoding()
codecs.lookup(x) codecs.lookup(x)
enc = x enc = x
except: except:

View File

@ -7,8 +7,6 @@ import os
from calibre.customize.conversion import InputFormatPlugin from calibre.customize.conversion import InputFormatPlugin
from calibre.ptempfile import TemporaryDirectory from calibre.ptempfile import TemporaryDirectory
from calibre.utils.localization import get_lang
from calibre.utils.filenames import ascii_filename
from calibre.constants import filesystem_encoding from calibre.constants import filesystem_encoding
class CHMInput(InputFormatPlugin): class CHMInput(InputFormatPlugin):
@ -57,22 +55,39 @@ class CHMInput(InputFormatPlugin):
mainpath = os.path.join(tdir, mainname) mainpath = os.path.join(tdir, mainname)
metadata = get_metadata_from_reader(self._chm_reader) metadata = get_metadata_from_reader(self._chm_reader)
encoding = self._chm_reader.get_encoding() or options.input_encoding or 'cp1252'
self._chm_reader.CloseCHM() self._chm_reader.CloseCHM()
#print tdir # print tdir, mainpath
#from calibre import ipython # from calibre import ipython
#ipython() # ipython()
options.debug_pipeline = None options.debug_pipeline = None
options.input_encoding = 'utf-8' options.input_encoding = 'utf-8'
# try a custom conversion: htmlpath, toc = self._create_html_root(mainpath, log, encoding)
#oeb = self._create_oebbook(mainpath, tdir, options, log, metadata)
# try using html converter:
htmlpath = self._create_html_root(mainpath, log)
oeb = self._create_oebbook_html(htmlpath, tdir, options, log, metadata) oeb = self._create_oebbook_html(htmlpath, tdir, options, log, metadata)
options.debug_pipeline = odi options.debug_pipeline = odi
#log.debug('DEBUG: Not removing tempdir %s' % tdir) if toc.count() > 1:
oeb.toc = self.parse_html_toc(oeb.spine[0])
oeb.manifest.remove(oeb.spine[0])
oeb.auto_generated_toc = False
return oeb return oeb
def parse_html_toc(self, item):
from calibre.ebooks.oeb.base import TOC, XPath
dx = XPath('./h:div')
ax = XPath('./h:a[1]')
def do_node(parent, div):
for child in dx(div):
a = ax(child)[0]
c = parent.add(a.text, a.attrib['href'])
do_node(c, child)
toc = TOC()
root = XPath('//h:div[1]')(item.data)[0]
do_node(toc, root)
return toc
def _create_oebbook_html(self, htmlpath, basedir, opts, log, mi): def _create_oebbook_html(self, htmlpath, basedir, opts, log, mi):
# use HTMLInput plugin to generate book # use HTMLInput plugin to generate book
from calibre.customize.builtins import HTMLInput from calibre.customize.builtins import HTMLInput
@ -81,104 +96,71 @@ class CHMInput(InputFormatPlugin):
oeb = htmlinput.create_oebbook(htmlpath, basedir, opts, log, mi) oeb = htmlinput.create_oebbook(htmlpath, basedir, opts, log, mi)
return oeb return oeb
def _create_html_root(self, hhcpath, log, encoding):
def _create_oebbook(self, hhcpath, basedir, opts, log, mi):
import uuid
from lxml import html from lxml import html
from calibre.ebooks.conversion.plumber import create_oebbook from urllib import unquote as _unquote
from calibre.ebooks.oeb.base import DirContainer from calibre.ebooks.oeb.base import urlquote
oeb = create_oebbook(log, None, opts, from calibre.ebooks.chardet import xml_to_unicode
encoding=opts.input_encoding, populate=False)
self.oeb = oeb
metadata = oeb.metadata
if mi.title:
metadata.add('title', mi.title)
if mi.authors:
for a in mi.authors:
metadata.add('creator', a, attrib={'role':'aut'})
if mi.publisher:
metadata.add('publisher', mi.publisher)
if mi.isbn:
metadata.add('identifier', mi.isbn, attrib={'scheme':'ISBN'})
if not metadata.language:
oeb.logger.warn(u'Language not specified')
metadata.add('language', get_lang().replace('_', '-'))
if not metadata.creator:
oeb.logger.warn('Creator not specified')
metadata.add('creator', _('Unknown'))
if not metadata.title:
oeb.logger.warn('Title not specified')
metadata.add('title', _('Unknown'))
bookid = str(uuid.uuid4())
metadata.add('identifier', bookid, id='uuid_id', scheme='uuid')
for ident in metadata.identifier:
if 'id' in ident.attrib:
self.oeb.uid = metadata.identifier[0]
break
hhcdata = self._read_file(hhcpath) hhcdata = self._read_file(hhcpath)
hhcdata = hhcdata.decode(encoding)
hhcdata = xml_to_unicode(hhcdata, verbose=True,
strip_encoding_pats=True, resolve_entities=True)[0]
hhcroot = html.fromstring(hhcdata) hhcroot = html.fromstring(hhcdata)
chapters = self._process_nodes(hhcroot) toc = self._process_nodes(hhcroot)
#print "=============================" #print "============================="
#print "Printing hhcroot" #print "Printing hhcroot"
#print etree.tostring(hhcroot, pretty_print=True) #print etree.tostring(hhcroot, pretty_print=True)
#print "=============================" #print "============================="
log.debug('Found %d section nodes' % len(chapters)) log.debug('Found %d section nodes' % toc.count())
if len(chapters) > 0:
path0 = chapters[0][1]
subpath = os.path.dirname(path0)
htmlpath = os.path.join(basedir, subpath)
oeb.container = DirContainer(htmlpath, log)
for chapter in chapters:
title = chapter[0]
basename = os.path.basename(chapter[1])
self._add_item(oeb, title, basename)
oeb.container = DirContainer(htmlpath, oeb.log)
return oeb
def _create_html_root(self, hhcpath, log):
from lxml import html
hhcdata = self._read_file(hhcpath)
hhcroot = html.fromstring(hhcdata)
chapters = self._process_nodes(hhcroot)
#print "============================="
#print "Printing hhcroot"
#print etree.tostring(hhcroot, pretty_print=True)
#print "============================="
log.debug('Found %d section nodes' % len(chapters))
htmlpath = os.path.splitext(hhcpath)[0] + ".html" htmlpath = os.path.splitext(hhcpath)[0] + ".html"
with open(htmlpath, 'wb') as f: base = os.path.dirname(os.path.abspath(htmlpath))
if chapters:
f.write('<html><head><meta http-equiv="Content-type"'
' content="text/html;charset=UTF-8" /></head><body>\n')
path0 = chapters[0][1]
subpath = os.path.dirname(path0)
base = os.path.dirname(f.name)
for chapter in chapters: def unquote(x):
title = chapter[0] if isinstance(x, unicode):
rsrcname = os.path.basename(chapter[1]) x = x.encode('utf-8')
return _unquote(x).decode('utf-8')
def unquote_path(x):
y = unquote(x)
if (not os.path.exists(os.path.join(base, x)) and
os.path.exists(os.path.join(base, y))):
x = y
return x
def donode(item, parent, base, subpath):
for child in item:
title = child.title
if not title: continue
raw = unquote_path(child.href or '')
rsrcname = os.path.basename(raw)
rsrcpath = os.path.join(subpath, rsrcname) rsrcpath = os.path.join(subpath, rsrcname)
if (not os.path.exists(os.path.join(base, rsrcpath)) and if (not os.path.exists(os.path.join(base, rsrcpath)) and
os.path.exists(os.path.join(base, chapter[1]))): os.path.exists(os.path.join(base, raw))):
rsrcpath = chapter[1] rsrcpath = raw
# title should already be url encoded if '%' not in rsrcpath:
url = "<br /><a href=" + rsrcpath + ">" + title + " </a>\n" rsrcpath = urlquote(rsrcpath)
if isinstance(url, unicode): if not raw:
url = url.encode('utf-8') rsrcpath = ''
f.write(url) c = DIV(A(title, href=rsrcpath))
donode(child, c, base, subpath)
parent.append(c)
f.write("</body></html>") with open(htmlpath, 'wb') as f:
if toc.count() > 1:
from lxml.html.builder import HTML, BODY, DIV, A
path0 = toc[0].href
path0 = unquote_path(path0)
subpath = os.path.dirname(path0)
base = os.path.dirname(f.name)
root = DIV()
donode(toc, root, base, subpath)
raw = html.tostring(HTML(BODY(root)), encoding='utf-8',
pretty_print=True)
f.write(raw)
else: else:
f.write(hhcdata) f.write(hhcdata)
return htmlpath return htmlpath, toc
def _read_file(self, name): def _read_file(self, name):
f = open(name, 'rb') f = open(name, 'rb')
@ -186,41 +168,27 @@ class CHMInput(InputFormatPlugin):
f.close() f.close()
return data return data
def _visit_node(self, node, chapters, depth): def add_node(self, node, toc, ancestor_map):
# check that node is a normal node (not a comment, DOCTYPE, etc.)
# (normal nodes have string tags)
if isinstance(node.tag, basestring):
from calibre.ebooks.chm.reader import match_string from calibre.ebooks.chm.reader import match_string
if match_string(node.attrib['type'], 'text/sitemap'):
chapter_path = None p = node.xpath('ancestor::ul[1]/ancestor::li[1]/object[1]')
if match_string(node.tag, 'object') and match_string(node.attrib['type'], 'text/sitemap'): parent = p[0] if p else None
chapter_title = None toc = ancestor_map.get(parent, toc)
for child in node: title = href = u''
if match_string(child.tag,'param') and match_string(child.attrib['name'], 'name'): for param in node.xpath('./param'):
chapter_title = child.attrib['value'] if match_string(param.attrib['name'], 'name'):
if match_string(child.tag,'param') and match_string(child.attrib['name'],'local'): title = param.attrib['value']
chapter_path = child.attrib['value'] elif match_string(param.attrib['name'], 'local'):
if chapter_title is not None and chapter_path is not None: href = param.attrib['value']
chapter = [chapter_title, chapter_path, depth] child = toc.add(title or _('Unknown'), href)
chapters.append(chapter) ancestor_map[node] = child
if node.tag=="UL":
depth = depth + 1
if node.tag=="/UL":
depth = depth - 1
def _process_nodes(self, root): def _process_nodes(self, root):
chapters = [] from calibre.ebooks.oeb.base import TOC
depth = 0 toc = TOC()
for node in root.iter(): ancestor_map = {}
self._visit_node(node, chapters, depth) for node in root.xpath('//object'):
return chapters self.add_node(node, toc, ancestor_map)
return toc
def _add_item(self, oeb, title, path):
bname = os.path.basename(path)
id, href = oeb.manifest.generate(id='html',
href=ascii_filename(bname))
item = oeb.manifest.add(id, href, 'text/html')
item.html_input_href = bname
oeb.spine.add(item, True)
oeb.toc.add(title, item.href)

View File

@ -194,8 +194,7 @@ class TOC(list):
content = content_path(np) content = content_path(np)
if content and text: if content and text:
content = content[0] content = content[0]
src = get_attr(content, attr='src') # if get_attr(content, attr='src'):
if src:
purl = urlparse(content.get('src')) purl = urlparse(content.get('src'))
href, fragment = unquote(purl[2]), unquote(purl[5]) href, fragment = unquote(purl[2]), unquote(purl[5])
nd = dest.add_item(href, fragment, text) nd = dest.add_item(href, fragment, text)

View File

@ -13,6 +13,7 @@ from calibre.utils.date import parse_date
from calibre.ebooks.mobi import MobiError from calibre.ebooks.mobi import MobiError
from calibre.ebooks.metadata import MetaInformation, check_isbn from calibre.ebooks.metadata import MetaInformation, check_isbn
from calibre.ebooks.mobi.langcodes import main_language, sub_language, mobi2iana from calibre.ebooks.mobi.langcodes import main_language, sub_language, mobi2iana
from calibre.utils.cleantext import clean_ascii_chars
from calibre.utils.localization import canonicalize_lang from calibre.utils.localization import canonicalize_lang
NULL_INDEX = 0xffffffff NULL_INDEX = 0xffffffff
@ -31,6 +32,8 @@ class EXTHHeader(object): # {{{
self.kf8_header = None self.kf8_header = None
self.uuid = self.cdetype = None self.uuid = self.cdetype = None
self.decode = lambda x : clean_ascii_chars(x.decode(codec, 'replace'))
while left > 0: while left > 0:
left -= 1 left -= 1
idx, size = struct.unpack('>LL', raw[pos:pos + 8]) idx, size = struct.unpack('>LL', raw[pos:pos + 8])
@ -66,7 +69,7 @@ class EXTHHeader(object): # {{{
# title contains non ASCII chars or non filename safe chars # title contains non ASCII chars or non filename safe chars
# they are messed up in the PDB header # they are messed up in the PDB header
try: try:
title = content.decode(codec) title = self.decode(content)
except: except:
pass pass
elif idx == 524: # Lang code elif idx == 524: # Lang code
@ -80,31 +83,30 @@ class EXTHHeader(object): # {{{
#else: #else:
# print 'unknown record', idx, repr(content) # print 'unknown record', idx, repr(content)
if title: if title:
self.mi.title = replace_entities(title) self.mi.title = replace_entities(clean_ascii_chars(title))
def process_metadata(self, idx, content, codec): def process_metadata(self, idx, content, codec):
if idx == 100: if idx == 100:
if self.mi.is_null('authors'): if self.mi.is_null('authors'):
self.mi.authors = [] self.mi.authors = []
au = content.decode(codec, 'ignore').strip() au = self.decode(content).strip()
self.mi.authors.append(au) self.mi.authors.append(au)
if self.mi.is_null('author_sort') and re.match(r'\S+?\s*,\s+\S+', au.strip()): if self.mi.is_null('author_sort') and re.match(r'\S+?\s*,\s+\S+', au.strip()):
self.mi.author_sort = au.strip() self.mi.author_sort = au.strip()
elif idx == 101: elif idx == 101:
self.mi.publisher = content.decode(codec, 'ignore').strip() self.mi.publisher = self.decode(content).strip()
if self.mi.publisher in {'Unknown', _('Unknown')}: if self.mi.publisher in {'Unknown', _('Unknown')}:
self.mi.publisher = None self.mi.publisher = None
elif idx == 103: elif idx == 103:
self.mi.comments = content.decode(codec, 'ignore') self.mi.comments = self.decode(content).strip()
elif idx == 104: elif idx == 104:
raw = check_isbn(content.decode(codec, 'ignore').strip().replace('-', '')) raw = check_isbn(self.decode(content).strip().replace('-', ''))
if raw: if raw:
self.mi.isbn = raw self.mi.isbn = raw
elif idx == 105: elif idx == 105:
if not self.mi.tags: if not self.mi.tags:
self.mi.tags = [] self.mi.tags = []
self.mi.tags.extend([x.strip() for x in content.decode(codec, self.mi.tags.extend([x.strip() for x in self.decode(content).split(';')])
'ignore').split(';')])
self.mi.tags = list(set(self.mi.tags)) self.mi.tags = list(set(self.mi.tags))
elif idx == 106: elif idx == 106:
try: try:
@ -112,7 +114,7 @@ class EXTHHeader(object): # {{{
except: except:
pass pass
elif idx == 108: elif idx == 108:
self.mi.book_producer = content.decode(codec, 'ignore').strip() self.mi.book_producer = self.decode(content).strip()
elif idx == 112: # dc:source set in some EBSP amazon samples elif idx == 112: # dc:source set in some EBSP amazon samples
try: try:
content = content.decode(codec).strip() content = content.decode(codec).strip()

View File

@ -249,7 +249,10 @@ class MobiReader(object):
head.insert(0, m) head.insert(0, m)
if not title: if not title:
title = head.makeelement('title', {}) title = head.makeelement('title', {})
try:
title.text = self.book_header.title title.text = self.book_header.title
except ValueError:
title.text = clean_ascii_chars(self.book_header.title)
title.tail = '\n\t' title.tail = '\n\t'
head.insert(0, title) head.insert(0, title)
head.text = '\n\t' head.text = '\n\t'

View File

@ -373,16 +373,12 @@ class OEBReader(object):
if not title: if not title:
self._toc_from_navpoint(item, toc, child) self._toc_from_navpoint(item, toc, child)
continue continue
if not href: if (not href or not href[0]) and not xpath(child, 'ncx:navPoint'):
gc = xpath(child, 'ncx:navPoint')
if not gc:
# This node is useless # This node is useless
continue continue
href = 'missing.html' href = item.abshref(urlnormalize(href[0])) if href and href[0] else ''
href = item.abshref(urlnormalize(href[0]))
path, _ = urldefrag(href) path, _ = urldefrag(href)
if path not in self.oeb.manifest.hrefs: if href and path not in self.oeb.manifest.hrefs:
self.logger.warn('TOC reference %r not found' % href) self.logger.warn('TOC reference %r not found' % href)
gc = xpath(child, 'ncx:navPoint') gc = xpath(child, 'ncx:navPoint')
if not gc: if not gc:

View File

@ -31,7 +31,7 @@ from calibre.utils.logging import GUILog as Log
from calibre.ebooks.metadata.sources.identify import urls_from_identifiers from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.book.base import Metadata
from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata.opf2 import OPF
from calibre.gui2 import error_dialog, NONE, rating_font from calibre.gui2 import error_dialog, NONE, rating_font, gprefs
from calibre.utils.date import (utcnow, fromordinal, format_date, from calibre.utils.date import (utcnow, fromordinal, format_date,
UNDEFINED_DATE, as_utc) UNDEFINED_DATE, as_utc)
from calibre.library.comments import comments_to_html from calibre.library.comments import comments_to_html
@ -264,6 +264,15 @@ class ResultsView(QTableView): # {{{
sm = self.selectionModel() sm = self.selectionModel()
sm.select(idx, sm.ClearAndSelect|sm.Rows) sm.select(idx, sm.ClearAndSelect|sm.Rows)
def resize_delegate(self):
self.rt_delegate.max_width = int(self.width()/2.1)
self.resizeColumnsToContents()
def resizeEvent(self, ev):
ret = super(ResultsView, self).resizeEvent(ev)
self.resize_delegate()
return ret
def currentChanged(self, current, previous): def currentChanged(self, current, previous):
ret = QTableView.currentChanged(self, current, previous) ret = QTableView.currentChanged(self, current, previous)
self.show_details(current) self.show_details(current)
@ -385,7 +394,7 @@ class IdentifyWorker(Thread): # {{{
def sample_results(self): def sample_results(self):
m1 = Metadata('The Great Gatsby', ['Francis Scott Fitzgerald']) m1 = Metadata('The Great Gatsby', ['Francis Scott Fitzgerald'])
m2 = Metadata('The Great Gatsby', ['F. Scott Fitzgerald']) m2 = Metadata('The Great Gatsby - An extra long title to test resizing', ['F. Scott Fitzgerald'])
m1.has_cached_cover_url = True m1.has_cached_cover_url = True
m2.has_cached_cover_url = False m2.has_cached_cover_url = False
m1.comments = 'Some comments '*10 m1.comments = 'Some comments '*10
@ -963,12 +972,16 @@ class FullFetch(QDialog): # {{{
self.covers_widget.chosen.connect(self.ok_clicked) self.covers_widget.chosen.connect(self.ok_clicked)
self.stack.addWidget(self.covers_widget) self.stack.addWidget(self.covers_widget)
self.resize(850, 600)
geom = gprefs.get('metadata_single_gui_geom', None)
if geom is not None and geom:
self.restoreGeometry(geom)
# Workaround for Qt 4.8.0 bug that causes the frame of the window to go # Workaround for Qt 4.8.0 bug that causes the frame of the window to go
# off the top of the screen if a max height is not set for the # off the top of the screen if a max height is not set for the
# QWebView. Seems to only happen on windows, but keep it for all # QWebView. Seems to only happen on windows, but keep it for all
# platforms just in case. # platforms just in case.
self.identify_widget.comments_view.setMaximumHeight(500) self.identify_widget.comments_view.setMaximumHeight(self.height()-100)
self.resize(850, 600)
self.finished.connect(self.cleanup) self.finished.connect(self.cleanup)
@ -995,12 +1008,14 @@ class FullFetch(QDialog): # {{{
self.covers_widget.reset_covers() self.covers_widget.reset_covers()
def accept(self): def accept(self):
gprefs['metadata_single_gui_geom'] = bytearray(self.saveGeometry())
if self.stack.currentIndex() == 1: if self.stack.currentIndex() == 1:
return QDialog.accept(self) return QDialog.accept(self)
# Prevent the usual dialog accept mechanisms from working # Prevent the usual dialog accept mechanisms from working
pass pass
def reject(self): def reject(self):
gprefs['metadata_single_gui_geom'] = bytearray(self.saveGeometry())
self.identify_widget.cancel() self.identify_widget.cancel()
self.covers_widget.cancel() self.covers_widget.cancel()
return QDialog.reject(self) return QDialog.reject(self)

View File

@ -7,6 +7,7 @@ __license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>' __copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import random
from contextlib import closing from contextlib import closing
from lxml import html from lxml import html

View File

@ -25,17 +25,12 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog
class EmpikStore(BasicStoreConfig, StorePlugin): class EmpikStore(BasicStoreConfig, StorePlugin):
def open(self, parent=None, detail_item=None, external=False): def open(self, parent=None, detail_item=None, external=False):
plain_url = 'http://www.empik.com/ebooki' url = 'http://www.empik.com/ebooki'
url = 'https://ssl.afiliant.com/affskrypt,,2f9de2,,23c7f,,,?u=(' + plain_url + ')'
detail_url = None
if detail_item:
detail_url = 'https://ssl.afiliant.com/affskrypt,,2f9de2,,23c7f,,,?u=(' + detail_item + ')'
if external or self.config.get('open_external', False): if external or self.config.get('open_external', False):
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url))) open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url)))
else: else:
d = WebStoreDialog(self.gui, url, parent, detail_url) d = WebStoreDialog(self.gui, url, parent, detail_item)
d.setWindowTitle(self.name) d.setWindowTitle(self.name)
d.set_tags(self.config.get('tags', '')) d.set_tags(self.config.get('tags', ''))
d.exec_() d.exec_()

View File

@ -70,6 +70,7 @@ def config(defaults=None):
c.add_opt('bottom_margin', default=20) c.add_opt('bottom_margin', default=20)
c.add_opt('text_color', default=None) c.add_opt('text_color', default=None)
c.add_opt('background_color', default=None) c.add_opt('background_color', default=None)
c.add_opt('show_controls', default=True)
fonts = c.add_group('FONTS', _('Font options')) fonts = c.add_group('FONTS', _('Font options'))
fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif', fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif',
@ -221,6 +222,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
for x in ('text', 'background'): for x in ('text', 'background'):
setattr(self, 'current_%s_color'%x, getattr(opts, '%s_color'%x)) setattr(self, 'current_%s_color'%x, getattr(opts, '%s_color'%x))
self.update_sample_colors() self.update_sample_colors()
self.opt_show_controls.setChecked(opts.show_controls)
def change_color(self, which, reset=False): def change_color(self, which, reset=False):
if reset: if reset:
@ -292,6 +294,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
self.opt_override_book_margins.isChecked()) self.opt_override_book_margins.isChecked())
c.set('text_color', self.current_text_color) c.set('text_color', self.current_text_color)
c.set('background_color', self.current_background_color) c.set('background_color', self.current_background_color)
c.set('show_controls', self.opt_show_controls.isChecked())
for x in ('top', 'bottom', 'side'): for x in ('top', 'bottom', 'side'):
c.set(x+'_margin', int(getattr(self, 'opt_%s_margin'%x).value())) c.set(x+'_margin', int(getattr(self, 'opt_%s_margin'%x).value()))

View File

@ -347,8 +347,8 @@ QToolBox::tab:hover {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>811</width> <width>352</width>
<height>352</height> <height>176</height>
</rect> </rect>
</property> </property>
<attribute name="label"> <attribute name="label">
@ -573,8 +573,8 @@ QToolBox::tab:hover {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>352</width> <width>811</width>
<height>123</height> <height>352</height>
</rect> </rect>
</property> </property>
<attribute name="label"> <attribute name="label">
@ -605,20 +605,27 @@ QToolBox::tab:hover {
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="opt_remember_window_size"> <widget class="QCheckBox" name="opt_remember_window_size">
<property name="text"> <property name="text">
<string>Remember last used &amp;window size and layout</string> <string>Remember last used &amp;window size and layout</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0" colspan="2"> <item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="opt_remember_current_page"> <widget class="QCheckBox" name="opt_remember_current_page">
<property name="text"> <property name="text">
<string>Remember the &amp;current page when quitting</string> <string>Remember the &amp;current page when quitting</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="opt_show_controls">
<property name="text">
<string>Show &amp;controls in the viewer window</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>

View File

@ -33,6 +33,7 @@ class Document(QWebPage): # {{{
page_turn = pyqtSignal(object) page_turn = pyqtSignal(object)
mark_element = pyqtSignal(QWebElement) mark_element = pyqtSignal(QWebElement)
settings_changed = pyqtSignal()
def set_font_settings(self, opts): def set_font_settings(self, opts):
settings = self.settings() settings = self.settings()
@ -57,6 +58,7 @@ class Document(QWebPage): # {{{
self.set_font_settings(opts) self.set_font_settings(opts)
self.set_user_stylesheet(opts) self.set_user_stylesheet(opts)
self.misc_config(opts) self.misc_config(opts)
self.settings_changed.emit()
self.after_load() self.after_load()
def __init__(self, shortcuts, parent=None, debug_javascript=False): def __init__(self, shortcuts, parent=None, debug_javascript=False):
@ -153,6 +155,7 @@ class Document(QWebPage): # {{{
self.cols_per_screen = opts.cols_per_screen self.cols_per_screen = opts.cols_per_screen
self.side_margin = opts.side_margin self.side_margin = opts.side_margin
self.top_margin, self.bottom_margin = opts.top_margin, opts.bottom_margin self.top_margin, self.bottom_margin = opts.top_margin, opts.bottom_margin
self.show_controls = opts.show_controls
def fit_images(self): def fit_images(self):
if self.do_fit_images and not self.in_paged_mode: if self.do_fit_images and not self.in_paged_mode:
@ -676,7 +679,7 @@ class DocumentView(QWebView): # {{{
if not text and img.isNull() and self.manager is not None: if not text and img.isNull() and self.manager is not None:
menu.addSeparator() menu.addSeparator()
if self.document.in_fullscreen_mode and self.manager is not None: if (not self.document.show_controls or self.document.in_fullscreen_mode) and self.manager is not None:
menu.addAction(self.manager.toggle_toolbar_action) menu.addAction(self.manager.toggle_toolbar_action)
menu.addAction(self.manager.action_full_screen) menu.addAction(self.manager.action_full_screen)

View File

@ -303,6 +303,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.toggle_toolbar_action = QAction(_('Show/hide controls'), self) self.toggle_toolbar_action = QAction(_('Show/hide controls'), self)
self.toggle_toolbar_action.setCheckable(True) self.toggle_toolbar_action.setCheckable(True)
self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars) self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars)
self.toolbar_hidden = None
self.addAction(self.toggle_toolbar_action) self.addAction(self.toggle_toolbar_action)
self.full_screen_label_anim = QPropertyAnimation( self.full_screen_label_anim = QPropertyAnimation(
self.full_screen_label, 'size') self.full_screen_label, 'size')
@ -359,7 +360,10 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
# continue to function even when the toolbars are hidden # continue to function even when the toolbars are hidden
self.addAction(action) self.addAction(action)
self.view.document.settings_changed.connect(self.settings_changed)
self.restore_state() self.restore_state()
self.settings_changed()
self.action_toggle_paged_mode.toggled[bool].connect(self.toggle_paged_mode) self.action_toggle_paged_mode.toggled[bool].connect(self.toggle_paged_mode)
if (start_in_fullscreen or self.view.document.start_in_fullscreen): if (start_in_fullscreen or self.view.document.start_in_fullscreen):
self.action_full_screen.trigger() self.action_full_screen.trigger()
@ -373,6 +377,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if at_start: return if at_start: return
self.reload() self.reload()
def settings_changed(self):
for x in ('', '2'):
x = getattr(self, 'tool_bar'+x)
x.setVisible(self.view.document.show_controls)
def reload(self): def reload(self):
if hasattr(self, 'current_index') and self.current_index > -1: if hasattr(self, 'current_index') and self.current_index > -1:
self.view.document.page_position.save(overwrite=False) self.view.document.page_position.save(overwrite=False)
@ -575,8 +584,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.vertical_scrollbar.setVisible(True) self.vertical_scrollbar.setVisible(True)
self.window_mode_changed = 'normal' self.window_mode_changed = 'normal'
self.esc_full_screen_action.setEnabled(False) self.esc_full_screen_action.setEnabled(False)
self.tool_bar.setVisible(True) self.settings_changed()
self.tool_bar2.setVisible(True)
self.full_screen_label.setVisible(False) self.full_screen_label.setVisible(False)
if hasattr(self, '_original_frame_margins'): if hasattr(self, '_original_frame_margins'):
om = self._original_frame_margins om = self._original_frame_margins

View File

@ -56,7 +56,7 @@ class TOCItem(QStandardItem):
self.title = text self.title = text
self.parent = parent self.parent = parent
QStandardItem.__init__(self, text if text else '') QStandardItem.__init__(self, text if text else '')
self.abspath = toc.abspath self.abspath = toc.abspath if toc.href else None
self.fragment = toc.fragment self.fragment = toc.fragment
all_items.append(self) all_items.append(self)
self.bold_font = QFont(self.font()) self.bold_font = QFont(self.font())
@ -70,11 +70,13 @@ class TOCItem(QStandardItem):
if si == self.abspath: if si == self.abspath:
spos = i spos = i
break break
am = {}
if self.abspath is not None:
try: try:
am = getattr(spine[i], 'anchor_map', {}) am = getattr(spine[i], 'anchor_map', {})
except UnboundLocalError: except UnboundLocalError:
# Spine was empty? # Spine was empty?
am = {} pass
frag = self.fragment if (self.fragment and self.fragment in am) else None frag = self.fragment if (self.fragment and self.fragment in am) else None
self.starts_at = spos self.starts_at = spos
self.start_anchor = frag self.start_anchor = frag

View File

@ -372,8 +372,10 @@ class BrowseServer(object):
if meta['is_custom'] and category not in displayed_custom_fields: if meta['is_custom'] and category not in displayed_custom_fields:
continue continue
# get the icon files # get the icon files
if category in self.icon_map: main_cat = (category.partition('.')[0]) if hasattr(category,
icon = '_'+quote(self.icon_map[category]) 'partition') else category
if main_cat in self.icon_map:
icon = '_'+quote(self.icon_map[main_cat])
elif category in category_icon_map: elif category in category_icon_map:
icon = category_icon_map[category] icon = category_icon_map[category]
elif meta['is_custom']: elif meta['is_custom']:

View File

@ -77,7 +77,7 @@ def build_navigation(start, num, total, url_base): # {{{
right_buttons = TD(CLASS('button', style='text-align:right')) right_buttons = TD(CLASS('button', style='text-align:right'))
if start > 1: if start > 1:
for t,s in [('First', 1), ('Previous', max(start-(num+1),1))]: for t,s in [('First', 1), ('Previous', max(start-num,1))]:
left_buttons.append(A(t, href='%s;start=%d'%(url_base, s))) left_buttons.append(A(t, href='%s;start=%d'%(url_base, s)))
if total > start + num: if total > start + num:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More