From e602d726ab39c8c589db53c5400a2efe668259d1 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Wed, 29 Sep 2010 15:01:39 +0800 Subject: [PATCH 001/140] [SNBOutput] Add stub for SNB format output support. SNB is the only format supported by the E Ink device Bambook manufactured by Shanda (NASDAQ: SNDA) --- resources/images/mimetypes/snb.png | Bin 0 -> 6245 bytes src/calibre/customize/builtins.py | 2 + src/calibre/ebooks/snb/__init__.py | 9 +++ src/calibre/ebooks/snb/output.py | 70 +++++++++++++++++++++++ src/calibre/gui2/convert/snb_output.py | 36 ++++++++++++ src/calibre/gui2/convert/snb_output.ui | 74 +++++++++++++++++++++++++ 6 files changed, 191 insertions(+) create mode 100644 resources/images/mimetypes/snb.png create mode 100644 src/calibre/ebooks/snb/__init__.py create mode 100644 src/calibre/ebooks/snb/output.py create mode 100644 src/calibre/gui2/convert/snb_output.py create mode 100644 src/calibre/gui2/convert/snb_output.ui diff --git a/resources/images/mimetypes/snb.png b/resources/images/mimetypes/snb.png new file mode 100644 index 0000000000000000000000000000000000000000..41b55f4343ae14d4bccaeede12193c0cee478a01 GIT binary patch literal 6245 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 + + Form + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ac1c9d52cb8ef0b034837ece1edcdbcd4574e0a9 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 9 Oct 2010 06:04:48 -0700 Subject: [PATCH 002/140] GwR patch adding get_all_user_metadata() --- src/calibre/ebooks/metadata/opf2.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 5c2477c3dc..dadca610ae 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -529,6 +529,14 @@ class OPF(object): # {{{ self.find_toc() self.read_user_metadata() + def get_all_user_metadata(self, make_copy): + ''' + return a dict containing all the custom field metadata associated with + the book. + ''' + self.read_user_metadata() + return self._user_metadata_ + def read_user_metadata(self): self._user_metadata_ = {} temp = Metadata('x', ['x']) From dd69e247476fa000e7f5b2d4edb60034d862dbdd Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Sat, 9 Oct 2010 22:30:38 +0800 Subject: [PATCH 003/140] [SNBOutput] Add basic output support for SNB file. --- src/calibre/ebooks/snb/output.py | 193 ++++++++++++++++--- src/calibre/ebooks/snb/snbfile.py | 300 ++++++++++++++++++++++++++++++ src/calibre/ebooks/snb/snbml.py | 160 ++++++++++++++++ 3 files changed, 629 insertions(+), 24 deletions(-) create mode 100644 src/calibre/ebooks/snb/snbfile.py create mode 100644 src/calibre/ebooks/snb/snbml.py diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index 4b94b65405..c302c17729 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -4,10 +4,29 @@ __license__ = 'GPL 3' __copyright__ = '2010, Li Fanxi ' __docformat__ = 'restructuredtext en' -import os +import os, string -from calibre.customize.conversion import OutputFormatPlugin, \ - OptionRecommendation +from lxml import etree +from calibre.customize.conversion import OutputFormatPlugin, OptionRecommendation +from calibre.ptempfile import TemporaryDirectory +from calibre.constants import __appname__, __version__ +from calibre.ebooks.oeb.base import XHTML, XHTML_NS, barename, namespace +from calibre.ebooks.snb.snbfile import SNBFile +from calibre.ebooks.snb.snbml import SNBMLizer + +def ProcessFileName(fileName): + # Flat the path + fileName = fileName.replace("/", "_").replace(os.sep, "_") + # Handle bookmark for HTML file + fileName = fileName.replace("#", "_") + # Make it lower case + fileName = fileName.lower() + # Change extension from jpeg to jpg + root, ext = os.path.splitext(fileName) + if ext in [ '.jpeg', '.jpg', '.gif', '.svg' ]: + fileName = root + '.png' + return fileName + class SNBOutput(OutputFormatPlugin): @@ -45,26 +64,152 @@ class SNBOutput(OutputFormatPlugin): ]) def convert(self, oeb_book, output_path, input_plugin, opts, log): + # Create temp dir + with TemporaryDirectory('_snb_output') as tdir: + # Create stub directories + snbfDir = os.path.join(tdir, 'snbf') + snbcDir = os.path.join(tdir, 'snbc') + snbiDir = os.path.join(tdir, 'snbc/images') + os.mkdir(snbfDir) + os.mkdir(snbcDir) + os.mkdir(snbiDir) + + # Process Meta data + meta = oeb_book.metadata + if meta.title: + title = unicode(meta.title[0]) + else: + title = '' + authors = [unicode(x) for x in meta.creator if x.role == 'aut'] + if meta.publisher: + publishers = unicode(meta.publisher[0]) + else: + publishers = '' + if meta.language: + lang = unicode(meta.language[0]).upper() + else: + lang = '' + if meta.description: + abstract = unicode(meta.description[0]) + else: + abstract = '' + + # Process Cover + from calibre.ebooks.oeb.base import urldefrag + g, m, s = oeb_book.guide, oeb_book.manifest, oeb_book.spine + href = None + if 'titlepage' not in g: + if 'cover' in g: + href = g['cover'].href + + # Output book info file + bookInfoTree = etree.Element("book-snbf", version="1.0") + headTree = etree.SubElement(bookInfoTree, "head") + etree.SubElement(headTree, "name").text = title + etree.SubElement(headTree, "author").text = ' '.join(authors) + etree.SubElement(headTree, "language").text = lang + etree.SubElement(headTree, "rights") + etree.SubElement(headTree, "publisher").text = publishers + etree.SubElement(headTree, "generator").text = __appname__ + ' ' + __version__ + etree.SubElement(headTree, "created") + etree.SubElement(headTree, "abstract").text = abstract + if href != None: + etree.SubElement(headTree, "cover").text = ProcessFileName(href) + else: + etree.SubElement(headTree, "cover") + bookInfoFile = open(os.path.join(snbfDir, 'book.snbf'), 'wb') + bookInfoFile.write(etree.tostring(bookInfoTree, pretty_print=True, encoding='utf-8')) + bookInfoFile.close() + + # Output TOC + tocInfoTree = etree.Element("toc-snbf") + tocHead = etree.SubElement(tocInfoTree, "head") + tocBody = etree.SubElement(tocInfoTree, "body") + outputFiles = { } + if oeb_book.toc.count() == 0: + log.warn('This SNB file has no Table of Contents. ' + 'Creating a default TOC') + first = iter(oeb_book.spine).next() + oeb_book.toc.add(_('Start'), first.href) + + for tocitem in oeb_book.toc: + ch = etree.SubElement(tocBody, "chapter") + ch.set("src", ProcessFileName(tocitem.href) + ".snbc") + ch.text = tocitem.title + if tocitem.href.find('#') != -1: + item = string.split(tocitem.href, '#') + if len(item) != 2: + log.error('Error in TOC item: %s' % tocitem) + else: + if item[0] in outputFiles: + outputFiles[item[0]].append((item[1], tocitem.title)) + else: + outputFiles[item[0]] = [] + outputFiles[item[0]].append((item[1], tocitem.title)) + else: + if tocitem.href in outputFiles: + outputFiles[tocitem.href].append(("", tocitem)) + else: + outputFiles[tocitem.href] = [] + outputFiles[tocitem.href].append(("", tocitem)) + + etree.SubElement(tocHead, "chapters").text = '%d' % len(tocBody) + + tocInfoFile = open(os.path.join(snbfDir, 'toc.snbf'), 'wb') + tocInfoFile.write(etree.tostring(tocInfoTree, pretty_print=True, encoding='utf-8')) + tocInfoFile.close() + + # Output Files + for item in s: + from calibre.ebooks.oeb.base import OEB_DOCS, OEB_IMAGES, PNG_MIME + if m.hrefs[item.href].media_type in OEB_DOCS: + if not item.href in outputFiles: + log.debug('Skipping %s because unused in TOC.' % item.href) + continue + log.debug('Converting %s to snbc...' % item.href) + snbwriter = SNBMLizer(log) + snbcTrees = snbwriter.extract_content(oeb_book, item, outputFiles[item.href], opts) + for subName in snbcTrees: + postfix = '' + if subName != '': + postfix = '_' + subName + outputFile = open(os.path.join(snbcDir, ProcessFileName(item.href + postfix + ".snbc")), 'wb') + outputFile.write(etree.tostring(snbcTrees[subName], pretty_print=True, encoding='utf-8')) + outputFile.close() + for item in m: + if m.hrefs[item.href].media_type in OEB_IMAGES: + log.debug('Converting image: %s ...' % item.href) + content = m.hrefs[item.href].data + if m.hrefs[item.href].media_type != PNG_MIME: + # Convert + from calibre.utils.magick import Image + img = Image() + img.load(content) + img.save(os.path.join(snbiDir, ProcessFileName(item.href))) + else: + outputFile = open(os.path.join(snbiDir, ProcessFileName(item.href)), 'wb') + outputFile.write(content) + outputFile.close() + + # Package as SNB File + snbFile = SNBFile() + snbFile.FromDir(tdir) + snbFile.Output(output_path) + +if __name__ == '__main__': + from calibre.ebooks.oeb.reader import OEBReader + from calibre.ebooks.oeb.base import OEBBook + from calibre.ebooks.conversion.preprocess import HTMLPreProcessor + from calibre.customize.profiles import HanlinV3Output + class OptionValues(object): pass - # writer = TXTMLizer(log) - # txt = writer.extract_content(oeb_book, opts) - - # log.debug('\tReplacing newlines with selected type...') - # txt = specified_newlines(TxtNewlines(opts.newline).newline, txt) - - # close = False - # if not hasattr(output_path, 'write'): - # close = True - # if not os.path.exists(os.path.dirname(output_path)) and os.path.dirname(output_path) != '': - # os.makedirs(os.path.dirname(output_path)) - # out_stream = open(output_path, 'wb') - # else: - # out_stream = output_path - - # out_stream.seek(0) - # out_stream.truncate() - # out_stream.write(txt.encode(opts.output_encoding, 'replace')) - - # if close: - # out_stream.close() + opts = OptionValues() + opts.output_profile = HanlinV3Output(None) + + html_preprocessor = HTMLPreProcessor(None, None, opts) + from calibre.utils.logging import default_log + oeb = OEBBook(default_log, html_preprocessor) + reader = OEBReader + reader()(oeb, '/tmp/bbb/processed/') + SNBOutput(None).convert(oeb, '/tmp/test.snb', None, None, default_log); diff --git a/src/calibre/ebooks/snb/snbfile.py b/src/calibre/ebooks/snb/snbfile.py new file mode 100644 index 0000000000..aa690fb92b --- /dev/null +++ b/src/calibre/ebooks/snb/snbfile.py @@ -0,0 +1,300 @@ +# -*- coding: utf-8 -*- + +__license__ = 'GPL 3' +__copyright__ = '2010, Li Fanxi ' +__docformat__ = 'restructuredtext en' + +import sys, struct, zlib, bz2, os, math + +class FileStream: + def IsBinary(self): + return self.attr & 0x41000000 != 0x41000000 + +def compareFileStream(file1, file2): + return cmp(file1.fileName, file2.fileName) + +class BlockData: + pass + +class SNBFile: + + files = [] + blocks = [] + + MAGIC = 'SNBP000B' + REV80 = 0x00008000 + REVA3 = 0x00A3A3A3 + REVZ1 = 0x00000000 + REVZ2 = 0x00000000 + + def __init__(self, inputFile = None): + if inputFile != None: + self.Parse(inputFile); + + def Parse(self, inputFile): + self.fileName = inputFile + + snbFile = open(self.fileName, "rb") + snbFile.seek(0) + + # Read header + vmbr = snbFile.read(44) + (self.magic, self.rev80, self.revA3, self.revZ1, + self.fileCount, self.vfatSize, self.vfatCompressed, + self.binStreamSize, self.plainStreamSizeUncompressed, + self.revZ2) = struct.unpack('>8siiiiiiiii', vmbr) + + # Read FAT + self.vfat = zlib.decompress(snbFile.read(self.vfatCompressed)) + self.ParseFile(self.vfat, self.fileCount) + + # Read tail + snbFile.seek(-16, os.SEEK_END) + #plainStreamEnd = snbFile.tell() + tailblock = snbFile.read(16) + (self.tailSize, self.tailOffset, self.tailMagic) = struct.unpack('>ii8s', tailblock) + snbFile.seek(self.tailOffset) + self.vTailUncompressed = zlib.decompress(snbFile.read(self.tailSize)) + self.tailSizeUncompressed = len(self.vTailUncompressed) + self.ParseTail(self.vTailUncompressed, self.fileCount) + + # Uncompress file data + # Read files + binPos = 0 + plainPos = 0 + uncompressedData = None + for f in self.files: + if f.attr & 0x41000000 == 0x41000000: + # Compressed Files + if uncompressedData == None: + uncompressedData = "" + for i in range(self.plainBlock): + bzdc = bz2.BZ2Decompressor() + if (i < self.plainBlock - 1): + bSize = self.blocks[self.binBlock + i + 1].Offset - self.blocks[self.binBlock + i].Offset; + else: + bSize = self.tailOffset - self.blocks[self.binBlock + i].Offset; + snbFile.seek(self.blocks[self.binBlock + i].Offset); + try: + data = snbFile.read(bSize) + uncompressedData += bzdc.decompress(data) + except EOFError, e: + print e + f.fileBody = uncompressedData[plainPos:plainPos+f.fileSize] + plainPos += f.fileSize + elif f.attr & 0x01000000 == 0x01000000: + # Binary Files + snbFile.seek(44 + self.vfatCompressed + binPos) + f.fileBody = snbFile.read(f.fileSize) + binPos += f.fileSize + else: + print f.attr, f.fileName + raise Exception("Invalid file") + snbFile.close() + + def ParseFile(self, vfat, fileCount): + fileNames = vfat[fileCount*12:].split('\0'); + for i in range(fileCount): + f = FileStream() + (f.attr, f.fileNameOffset, f.fileSize) = struct.unpack('>iii', vfat[i * 12 : (i+1)*12]) + f.fileName = fileNames[i] + self.files.append(f) + + def ParseTail(self, vtail, fileCount): + self.binBlock = (self.binStreamSize + 0x8000 - 1) / 0x8000; + self.plainBlock = (self.plainStreamSizeUncompressed + 0x8000 - 1) / 0x8000; + for i in range(self.binBlock + self.plainBlock): + block = BlockData() + (block.Offset,) = struct.unpack('>i', vtail[i * 4 : (i+1) * 4]) + self.blocks.append(block) + for i in range(fileCount): + (self.files[i].blockIndex, self.files[i].contentOffset) = struct.unpack('>ii', vtail[(self.binBlock + self.plainBlock) * 4 + i * 8 : (self.binBlock + self.plainBlock) * 4 + (i+1) * 8]) + + def IsValid(self): + if self.magic != SNBFile.MAGIC: + return False + if self.rev80 != SNBFile.REV80: + return False + if self.revA3 != SNBFile.REVA3: + return False + if self.revZ1 != SNBFile.REVZ1: + return False + if self.revZ2 != SNBFile.REVZ2: + return False + if self.vfatSize != len(self.vfat): + return False + if self.fileCount != len(self.files): + return False + if (self.binBlock + self.plainBlock) * 4 + self.fileCount * 8 != self.tailSizeUncompressed: + return False + if self.tailMagic != SNBFile.MAGIC: + print self.tailMagic + return False + return True + + def FromDir(self, tdir): + for root, dirs, files in os.walk(tdir): + for name in files: + print name + p, ext = os.path.splitext(name) + if ext in [ ".snbf", ".snbc" ]: + self.AppendPlain(os.path.relpath(os.path.join(root, name), tdir), tdir) + else: + self.AppendBinary(os.path.relpath(os.path.join(root, name), tdir), tdir) + + def AppendPlain(self, fileName, tdir): + f = FileStream() + f.attr = 0x41000000 + f.fileSize = os.path.getsize(os.path.join(tdir,fileName)) + f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() + f.fileName = fileName + print f.fileSize + self.files.append(f) + + def AppendBinary(self, fileName, tdir): + f = FileStream() + f.attr = 0x01000000 + f.fileSize = os.path.getsize(os.path.join(tdir,fileName)) + f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() + f.fileName = fileName + print f.fileSize + self.files.append(f) + + def Output(self, outputFile): + + # Sort the files in file buffer, + # requried by the SNB file format + self.files.sort(compareFileStream) + + outputFile = open(outputFile, 'wb') + # File header part 1 + vmbrp1 = struct.pack('>8siiii', SNBFile.MAGIC, SNBFile.REV80, SNBFile.REVA3, SNBFile.REVZ1, len(self.files)) + + # Create VFAT & file stream + vfat = '' + fileNameTable = '' + plainStream = '' + binStream = '' + for f in self.files: + vfat += struct.pack('>iii', f.attr, len(fileNameTable), f.fileSize); + fileNameTable += (f.fileName + '\0') + + if f.attr & 0x41000000 == 0x41000000: + # Plain Files + f.contentOffset = len(plainStream) + plainStream += f.fileBody + elif f.attr & 0x01000000 == 0x01000000: + # Binary Files + f.contentOffset = len(binStream) + binStream += f.fileBody + else: + print f.attr, f.fileName + raise Exception("Unknown file type") + vfatCompressed = zlib.compress(vfat+fileNameTable) + + # File header part 2 + vmbrp2 = struct.pack('>iiiii', len(vfat+fileNameTable), len(vfatCompressed), len(binStream), len(plainStream), SNBFile.REVZ2) + # Write header + outputFile.write(vmbrp1 + vmbrp2) + # Write vfat + outputFile.write(vfatCompressed) + + # Generate block information + binBlockOffset = 0x2C + len(vfatCompressed) + plainBlockOffset = binBlockOffset + len(binStream) + + binBlock = (len(binStream) + 0x8000 - 1) / 0x8000 + plainBlock = (len(plainStream) + 0x8000 - 1) / 0x8000 + + offset = 0 + tailBlock = '' + for i in range(binBlock): + tailBlock += struct.pack('>i', binBlockOffset + offset) + offset += 0x8000; + tailRec = '' + for f in self.files: + t = 0 + if f.IsBinary(): + t = 0 + else: + t = binBlock + tailRec += struct.pack('>ii', f.contentOffset / 0x8000 + t, f.contentOffset % 0x8000); + + # Write binary stream + outputFile.write(binStream) + + # Write plain stream + pos = 0 + offset = 0 + while pos < len(plainStream): + tailBlock += struct.pack('>i', plainBlockOffset + offset); + block = plainStream[pos:pos+0x8000]; + compressed = bz2.compress(block) + outputFile.write(compressed) + offset += len(compressed) + pos += 0x8000 + + # Write tail block + compressedTail = zlib.compress(tailBlock + tailRec) + outputFile.write(compressedTail) + + # Write tail pointer + veom = struct.pack('>ii', len(compressedTail), plainBlockOffset + offset) + outputFile.write(veom) + + # Write file end mark + outputFile.write(SNBFile.MAGIC); + + # Close + outputFile.close() + return + + def Dump(self): + print "File Name:\t", self.fileName + print "File Count:\t", self.fileCount + print "VFAT Size(Compressed):\t%d(%d)" % (self.vfatSize, self.vfatCompressed) + print "Binary Stream Size:\t", self.binStreamSize + print "Plain Stream Uncompressed Size:\t", self.plainStreamSizeUncompressed + print "Binary Block Count:\t", self.binBlock + print "Plain Block Count:\t", self.plainBlock + for i in range(self.fileCount): + print "File ", i + f = self.files[i] + print "File Name: ", f.fileName + print "File Attr: ", f.attr + print "File Size: ", f.fileSize + print "Block Index: ", f.blockIndex + print "Content Offset: ", f.contentOffset + tempFile = open("/tmp/" + f.fileName, 'wb') + tempFile.write(f.fileBody) + tempFile.close() + +def usage(): + print "This unit test is for INTERNAL usage only!" + print "This unit test accept two parameters." + print "python snbfile.py " + print "The input file will be extracted and write to dest file. " + print "Meta data of the file will be shown during this process." + +def main(): + if len(sys.argv) != 3: + usage() + sys.exit(0) + inputFile = sys.argv[1] + outputFile = sys.argv[2] + + print "Input file: ", inputFile + print "Output file: ", outputFile + + snbFile = SNBFile(inputFile) + if snbFile.IsValid(): + snbFile.Dump() + snbFile.Output(outputFile) + else: + print "The input file is invalid." + return 1 + return 0 + +if __name__ == "__main__": + """SNB file unit test""" + sys.exit(main()) diff --git a/src/calibre/ebooks/snb/snbml.py b/src/calibre/ebooks/snb/snbml.py new file mode 100644 index 0000000000..e1956b5937 --- /dev/null +++ b/src/calibre/ebooks/snb/snbml.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- + +__license__ = 'GPL 3' +__copyright__ = '2010, Li Fanxi ' +__docformat__ = 'restructuredtext en' + +''' +Transform OEB content into SNB format +''' + +import os +import re + +from lxml import etree + +from calibre.ebooks.oeb.base import XHTML, XHTML_NS, barename, namespace +from calibre.ebooks.oeb.stylizer import Stylizer + +def ProcessFileName(fileName): + # Flat the path + fileName = fileName.replace("/", "_").replace(os.sep, "_") + # Handle bookmark for HTML file + fileName = fileName.replace("#", "_") + # Make it lower case + fileName = fileName.lower() + # Change extension from jpeg to jpg + root, ext = os.path.splitext(fileName) + if ext in [ '.jpeg', '.jpg', '.gif', '.svg' ]: + fileName = root + '.png' + return fileName + + +BLOCK_TAGS = [ + 'div', + 'p', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'li', + 'tr', +] + +BLOCK_STYLES = [ + 'block', +] + +SPACE_TAGS = [ + 'td', +] + +CLIABRE_SNB_IMG_TAG = "" + +class SNBMLizer(object): + + curSubItem = "" + curText = [ ] + + def __init__(self, log): + self.log = log + + def extract_content(self, oeb_book, item, subitems, opts): + self.log.info('Converting XHTML to SNBC...') + self.oeb_book = oeb_book + self.opts = opts + self.item = item + self.subitems = subitems + return self.mlize(); + + + def mlize(self): + stylizer = Stylizer(self.item.data, self.item.href, self.oeb_book, self.opts, self.opts.output_profile) + content = unicode(etree.tostring(self.item.data.find(XHTML('body')), encoding=unicode)) + content = self.remove_newlines(content) + trees = { } + for subitem, subtitle in self.subitems: + snbcTree = etree.Element("snbc") + etree.SubElement(etree.SubElement(snbcTree, "head"), "title").text = subtitle + etree.SubElement(snbcTree, "body") + trees[subitem] = snbcTree + + self.dump_text(trees, self.subitems, etree.fromstring(content), stylizer) + self.Output(trees) + return trees + + def remove_newlines(self, text): + self.log.debug('\tRemove newlines for processing...') + text = text.replace('\r\n', ' ') + text = text.replace('\n', ' ') + text = text.replace('\r', ' ') + + return text + + def dump_text(self, trees, subitems, elem, stylizer, end=''): + ''' + @elem: The element in the etree that we are working on. + @stylizer: The style information attached to the element. + @end: The last two characters of the text from the previous element. + This is used to determine if a blank line is needed when starting + a new block element. + ''' + if not isinstance(elem.tag, basestring) \ + or namespace(elem.tag) != XHTML_NS: + return [''] + + if elem.attrib.get('id') != None and elem.attrib['id'] in [ href for href, title in subitems ]: + if self.curSubItem != None and self.curSubItem != elem.attrib['id']: + self.Output(trees) + self.curSubItem = elem.attrib['id'] + self.curText = [ ] + + style = stylizer.style(elem) + + if style['display'] in ('none', 'oeb-page-head', 'oeb-page-foot') \ + or style['visibility'] == 'hidden': + return [''] + + tag = barename(elem.tag) + in_block = False + + # Are we in a paragraph block? + if tag in BLOCK_TAGS or style['display'] in BLOCK_STYLES: + in_block = True + if not end.endswith(u'\n\n') and hasattr(elem, 'text') and elem.text: + self.curText.append(u'\n\n') + + if tag in SPACE_TAGS: + if not end.endswith('u ') and hasattr(elem, 'text') and elem.text: + self.curText.append(u' ') + + if tag == 'img': + self.curText.append(u'%s%s' % (CLIABRE_SNB_IMG_TAG, ProcessFileName(elem.attrib['src']))) + + # Process tags that contain text. + if hasattr(elem, 'text') and elem.text: + self.curText.append(elem.text) + + for item in elem: + en = u'' + if len(self.curText) >= 2: + en = self.curText[-1][-2:] + self.dump_text(trees, subitems, item, stylizer, en) + + if in_block: + self.curText.append(u'\n\n') + + if hasattr(elem, 'tail') and elem.tail: + self.curText.append(elem.tail) + + def Output(self, trees): + if self.curSubItem == None or not self.curSubItem in trees: + return + for t in self.curText: + if len(t.strip(' \t\n\r')) != 0: + if t.find(CLIABRE_SNB_IMG_TAG) == 0: + etree.SubElement(trees[self.curSubItem], "img").text = t[len(CLIABRE_SNB_IMG_TAG):] + else: + etree.SubElement(trees[self.curSubItem], "text").text = etree.CDATA(unicode('' + t)) From 603b8811893fc080b02b182e52b20cbf0aab8f33 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Sat, 9 Oct 2010 23:04:03 +0800 Subject: [PATCH 004/140] [SNBOutputProfile] Add a Bambook Output Profile --- src/calibre/customize/profiles.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/calibre/customize/profiles.py b/src/calibre/customize/profiles.py index 0310f09242..937cb9c3b4 100644 --- a/src/calibre/customize/profiles.py +++ b/src/calibre/customize/profiles.py @@ -642,11 +642,24 @@ class NookOutput(OutputProfile): fbase = 16 fsizes = [12, 12, 14, 16, 18, 20, 22, 24] +class BambookOutput(OutputProfile): + + name = 'Sanda Bambook' + short_name = 'bambook' + description = _('This profile is intended for the Sanda Bambook.') + + # Screen size is a best guess + screen_size = (800, 600) + dpi = 168.451 + fbase = 12 + fsizes = [10, 12, 14, 16] + output_profiles = [OutputProfile, SonyReaderOutput, SonyReader300Output, SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output, HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput, iPadOutput, KoboReaderOutput, SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput, - IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,] + IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput, + BambookOutput, ] output_profiles.sort(cmp=lambda x,y:cmp(x.name.lower(), y.name.lower())) From 6a301a1a9a97a7bb769b05f12b5574e9e83a5116 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Sun, 10 Oct 2010 11:27:27 +0800 Subject: [PATCH 005/140] [SNBOutput] Add two spaces for each paragraph. --- src/calibre/ebooks/snb/snbml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/snb/snbml.py b/src/calibre/ebooks/snb/snbml.py index e1956b5937..15f3413489 100644 --- a/src/calibre/ebooks/snb/snbml.py +++ b/src/calibre/ebooks/snb/snbml.py @@ -157,4 +157,4 @@ class SNBMLizer(object): if t.find(CLIABRE_SNB_IMG_TAG) == 0: etree.SubElement(trees[self.curSubItem], "img").text = t[len(CLIABRE_SNB_IMG_TAG):] else: - etree.SubElement(trees[self.curSubItem], "text").text = etree.CDATA(unicode('' + t)) + etree.SubElement(trees[self.curSubItem], "text").text = etree.CDATA(unicode(u'\u3000\u3000' + t)) From 8fd3f0ebaab8f9f104842e671e1e2312695780d4 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Sun, 10 Oct 2010 11:28:43 +0800 Subject: [PATCH 006/140] [SNBOutput] Fix the path error on different OSes. --- src/calibre/ebooks/snb/snbfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/snb/snbfile.py b/src/calibre/ebooks/snb/snbfile.py index aa690fb92b..6d2c627fbb 100644 --- a/src/calibre/ebooks/snb/snbfile.py +++ b/src/calibre/ebooks/snb/snbfile.py @@ -147,7 +147,7 @@ class SNBFile: f.attr = 0x41000000 f.fileSize = os.path.getsize(os.path.join(tdir,fileName)) f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() - f.fileName = fileName + f.fileName = fileName.replace(os.sep, '/') print f.fileSize self.files.append(f) @@ -156,7 +156,7 @@ class SNBFile: f.attr = 0x01000000 f.fileSize = os.path.getsize(os.path.join(tdir,fileName)) f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() - f.fileName = fileName + f.fileName = fileName.replace(os.sep, '/') print f.fileSize self.files.append(f) From 0c387834f43b108ee02f839d1122e44f6758a4d6 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Sun, 10 Oct 2010 16:25:22 +0800 Subject: [PATCH 007/140] [SNBOutput] The conetent in html before the first bookmark should also be outputted. --- src/calibre/ebooks/snb/output.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index a02d085b5e..65f06c7994 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -133,9 +133,6 @@ class SNBOutput(OutputFormatPlugin): oeb_book.toc.add(_('Start'), first.href) for tocitem in oeb_book.toc: - ch = etree.SubElement(tocBody, "chapter") - ch.set("src", ProcessFileName(tocitem.href) + ".snbc") - ch.text = tocitem.title if tocitem.href.find('#') != -1: item = string.split(tocitem.href, '#') if len(item) != 2: @@ -145,6 +142,11 @@ class SNBOutput(OutputFormatPlugin): outputFiles[item[0]].append((item[1], tocitem.title)) else: outputFiles[item[0]] = [] + if not "" in outputFiles[item[0]]: + outputFiles[item[0]].append(("", _("Start"))) + ch = etree.SubElement(tocBody, "chapter") + ch.set("src", ProcessFileName(item[0]) + ".snbc") + ch.text = _("Start") outputFiles[item[0]].append((item[1], tocitem.title)) else: if tocitem.href in outputFiles: @@ -152,6 +154,10 @@ class SNBOutput(OutputFormatPlugin): else: outputFiles[tocitem.href] = [] outputFiles[tocitem.href].append(("", tocitem.title)) + ch = etree.SubElement(tocBody, "chapter") + ch.set("src", ProcessFileName(tocitem.href) + ".snbc") + ch.text = tocitem.title + etree.SubElement(tocHead, "chapters").text = '%d' % len(tocBody) From b4c69ba6343cae44110ea28e364d82ba57c313ae Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Sun, 10 Oct 2010 17:47:23 +0800 Subject: [PATCH 008/140] [SNBOutput] Reduce the size of the images --- src/calibre/ebooks/snb/output.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index 65f06c7994..08ede2ca37 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -21,7 +21,7 @@ def ProcessFileName(fileName): fileName = fileName.replace("#", "_") # Make it lower case fileName = fileName.lower() - # Change extension from jpeg to jpg + # Change extension for image files to png root, ext = os.path.splitext(fileName) if ext in [ '.jpeg', '.jpg', '.gif', '.svg' ]: fileName = root + '.png' @@ -187,11 +187,8 @@ class SNBOutput(OutputFormatPlugin): log.debug('Converting image: %s ...' % item.href) content = m.hrefs[item.href].data if m.hrefs[item.href].media_type != PNG_MIME: - # Convert - from calibre.utils.magick import Image - img = Image() - img.load(content) - img.save(os.path.join(snbiDir, ProcessFileName(item.href))) + # Convert & Resize image + self.HandleImage(content, os.path.join(snbiDir, ProcessFileName(item.href))) else: outputFile = open(os.path.join(snbiDir, ProcessFileName(item.href)), 'wb') outputFile.write(content) @@ -202,6 +199,29 @@ class SNBOutput(OutputFormatPlugin): snbFile.FromDir(tdir) snbFile.Output(output_path) + def HandleImage(self, imageData, imagePath): + from calibre.utils.magick import Image + img = Image() + img.load(imageData) + print img.size + (x,y) = img.size + # TODO use the data from device profile + SCREEN_X = 540 + SCREEN_Y = 700 + # Handle big image only + if x > SCREEN_X or y > SCREEN_Y: + SCREEN_RATIO = float(SCREEN_Y) / SCREEN_X + imgRatio = float(y) / x + xScale = float(x) / SCREEN_X + yScale = float(y) / SCREEN_Y + scale = max(xScale, yScale) + # TODO : intelligent image rotation + # img = img.rotate(90) + # x,y = y,x + img.size = (x / scale, y / scale) + print img.size + img.save(imagePath) + if __name__ == '__main__': from calibre.ebooks.oeb.reader import OEBReader from calibre.ebooks.oeb.base import OEBBook From e72c3ce0f83532688d26bda3d3fefb6e4e901729 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Sun, 10 Oct 2010 19:40:55 +0800 Subject: [PATCH 009/140] [[SNBOutput] Reuse the original html->txt algorithm in txtml.py to get better output. Removed some unnecessary debug prints. --- src/calibre/ebooks/snb/output.py | 2 - src/calibre/ebooks/snb/snbfile.py | 3 - src/calibre/ebooks/snb/snbml.py | 134 ++++++++++++++++++++++-------- 3 files changed, 100 insertions(+), 39 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index 08ede2ca37..a682062de2 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -203,7 +203,6 @@ class SNBOutput(OutputFormatPlugin): from calibre.utils.magick import Image img = Image() img.load(imageData) - print img.size (x,y) = img.size # TODO use the data from device profile SCREEN_X = 540 @@ -219,7 +218,6 @@ class SNBOutput(OutputFormatPlugin): # img = img.rotate(90) # x,y = y,x img.size = (x / scale, y / scale) - print img.size img.save(imagePath) if __name__ == '__main__': diff --git a/src/calibre/ebooks/snb/snbfile.py b/src/calibre/ebooks/snb/snbfile.py index 6d2c627fbb..ca10f800c7 100644 --- a/src/calibre/ebooks/snb/snbfile.py +++ b/src/calibre/ebooks/snb/snbfile.py @@ -135,7 +135,6 @@ class SNBFile: def FromDir(self, tdir): for root, dirs, files in os.walk(tdir): for name in files: - print name p, ext = os.path.splitext(name) if ext in [ ".snbf", ".snbc" ]: self.AppendPlain(os.path.relpath(os.path.join(root, name), tdir), tdir) @@ -148,7 +147,6 @@ class SNBFile: f.fileSize = os.path.getsize(os.path.join(tdir,fileName)) f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() f.fileName = fileName.replace(os.sep, '/') - print f.fileSize self.files.append(f) def AppendBinary(self, fileName, tdir): @@ -157,7 +155,6 @@ class SNBFile: f.fileSize = os.path.getsize(os.path.join(tdir,fileName)) f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() f.fileName = fileName.replace(os.sep, '/') - print f.fileSize self.files.append(f) def Output(self, outputFile): diff --git a/src/calibre/ebooks/snb/snbml.py b/src/calibre/ebooks/snb/snbml.py index 15f3413489..c357971b5e 100644 --- a/src/calibre/ebooks/snb/snbml.py +++ b/src/calibre/ebooks/snb/snbml.py @@ -51,12 +51,13 @@ SPACE_TAGS = [ 'td', ] -CLIABRE_SNB_IMG_TAG = "" +CALIBRE_SNB_IMG_TAG = "<$$calibre_snb_temp_img$$>" +CALIBRE_SNB_BM_TAG = "<$$calibre_snb_bm_tag$$>" class SNBMLizer(object): curSubItem = "" - curText = [ ] +# curText = [ ] def __init__(self, log): self.log = log @@ -71,6 +72,7 @@ class SNBMLizer(object): def mlize(self): + output = [ u'' ] stylizer = Stylizer(self.item.data, self.item.href, self.oeb_book, self.opts, self.opts.output_profile) content = unicode(etree.tostring(self.item.data.find(XHTML('body')), encoding=unicode)) content = self.remove_newlines(content) @@ -80,9 +82,20 @@ class SNBMLizer(object): etree.SubElement(etree.SubElement(snbcTree, "head"), "title").text = subtitle etree.SubElement(snbcTree, "body") trees[subitem] = snbcTree + output.append(u'%s%s\n\n' % (CALIBRE_SNB_BM_TAG, "")) + output += self.dump_text(self.subitems, etree.fromstring(content), stylizer) + output = self.cleanup_text(u''.join(output)) - self.dump_text(trees, self.subitems, etree.fromstring(content), stylizer) - self.Output(trees) + subitem = '' + for line in output.splitlines(): + line = line.strip(' \t\n\r') + if len(line) != 0: + if line.find(CALIBRE_SNB_IMG_TAG) == 0: + etree.SubElement(trees[subitem], "img").text = line[len(CALIBRE_SNB_IMG_TAG):] + elif line.find(CALIBRE_SNB_BM_TAG) == 0: + subitem = line[len(CALIBRE_SNB_BM_TAG):] + else: + etree.SubElement(trees[subitem], "text").text = etree.CDATA(unicode(u'\u3000\u3000' + line)) return trees def remove_newlines(self, text): @@ -93,25 +106,86 @@ class SNBMLizer(object): return text - def dump_text(self, trees, subitems, elem, stylizer, end=''): - ''' - @elem: The element in the etree that we are working on. - @stylizer: The style information attached to the element. - @end: The last two characters of the text from the previous element. - This is used to determine if a blank line is needed when starting - a new block element. - ''' + def cleanup_text(self, text): + self.log.debug('\tClean up text...') + # Replace bad characters. + text = text.replace(u'\xc2', '') + text = text.replace(u'\xa0', ' ') + text = text.replace(u'\xa9', '(C)') + + # Replace tabs, vertical tags and form feeds with single space. + text = text.replace('\t+', ' ') + text = text.replace('\v+', ' ') + text = text.replace('\f+', ' ') + + # Single line paragraph. + text = re.sub('(?<=.)%s(?=.)' % os.linesep, ' ', text) + + # Remove multiple spaces. + text = re.sub('[ ]{2,}', ' ', text) + + # Remove excessive newlines. + text = re.sub('\n[ ]+\n', '\n\n', text) + if self.opts.remove_paragraph_spacing: + text = re.sub('\n{2,}', '\n', text) + text = re.sub('(?imu)^(?=.)', '\t', text) + else: + text = re.sub('\n{3,}', '\n\n', text) + + # Replace spaces at the beginning and end of lines + text = re.sub('(?imu)^[ ]+', '', text) + text = re.sub('(?imu)[ ]+$', '', text) + + if self.opts.max_line_length: + max_length = self.opts.max_line_length + if self.opts.max_line_length < 25 and not self.opts.force_max_line_length: + max_length = 25 + short_lines = [] + lines = text.splitlines() + for line in lines: + while len(line) > max_length: + space = line.rfind(' ', 0, max_length) + if space != -1: + # Space was found. + short_lines.append(line[:space]) + line = line[space + 1:] + else: + # Space was not found. + if self.opts.force_max_line_length: + # Force breaking at max_lenght. + short_lines.append(line[:max_length]) + line = line[max_length:] + else: + # Look for the first space after max_length. + space = line.find(' ', max_length, len(line)) + if space != -1: + # Space was found. + short_lines.append(line[:space]) + line = line[space + 1:] + else: + # No space was found cannot break line. + short_lines.append(line) + line = '' + # Add the text that was less than max_lengh to the list + short_lines.append(line) + text = '\n'.join(short_lines) + + return text + + def dump_text(self, subitems, elem, stylizer, end=''): + if not isinstance(elem.tag, basestring) \ or namespace(elem.tag) != XHTML_NS: return [''] + + text = [''] + style = stylizer.style(elem) + if elem.attrib.get('id') != None and elem.attrib['id'] in [ href for href, title in subitems ]: if self.curSubItem != None and self.curSubItem != elem.attrib['id']: - self.Output(trees) self.curSubItem = elem.attrib['id'] - self.curText = [ ] - - style = stylizer.style(elem) + text.append(u'%s%s\n\n' % (CALIBRE_SNB_BM_TAG, self.curSubItem)) if style['display'] in ('none', 'oeb-page-head', 'oeb-page-foot') \ or style['visibility'] == 'hidden': @@ -124,37 +198,29 @@ class SNBMLizer(object): if tag in BLOCK_TAGS or style['display'] in BLOCK_STYLES: in_block = True if not end.endswith(u'\n\n') and hasattr(elem, 'text') and elem.text: - self.curText.append(u'\n\n') + text.append(u'\n\n') if tag in SPACE_TAGS: if not end.endswith('u ') and hasattr(elem, 'text') and elem.text: - self.curText.append(u' ') + text.append(u' ') if tag == 'img': - self.curText.append(u'%s%s' % (CLIABRE_SNB_IMG_TAG, ProcessFileName(elem.attrib['src']))) + text.append(u'%s%s' % (CALIBRE_SNB_IMG_TAG, ProcessFileName(elem.attrib['src']))) # Process tags that contain text. if hasattr(elem, 'text') and elem.text: - self.curText.append(elem.text) + text.append(elem.text) for item in elem: en = u'' - if len(self.curText) >= 2: - en = self.curText[-1][-2:] - self.dump_text(trees, subitems, item, stylizer, en) + if len(text) >= 2: + en = text[-1][-2:] + text += self.dump_text(subitems, item, stylizer, en) if in_block: - self.curText.append(u'\n\n') + text.append(u'\n\n') if hasattr(elem, 'tail') and elem.tail: - self.curText.append(elem.tail) + text.append(elem.tail) - def Output(self, trees): - if self.curSubItem == None or not self.curSubItem in trees: - return - for t in self.curText: - if len(t.strip(' \t\n\r')) != 0: - if t.find(CLIABRE_SNB_IMG_TAG) == 0: - etree.SubElement(trees[self.curSubItem], "img").text = t[len(CLIABRE_SNB_IMG_TAG):] - else: - etree.SubElement(trees[self.curSubItem], "text").text = etree.CDATA(unicode(u'\u3000\u3000' + t)) + return text From 081c5385f2862be4d0a7e73f76b9c8f9477156ae Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Sun, 10 Oct 2010 21:24:27 +0800 Subject: [PATCH 010/140] [SNBOutput] Improve TOC handling. If an spice is not referenced in TOC, it will be appended to the last TOC item. --- src/calibre/ebooks/snb/output.py | 51 +++++++++++++++++++++++++------- src/calibre/ebooks/snb/snbml.py | 13 ++++++-- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index a682062de2..c8457347ec 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -131,6 +131,10 @@ class SNBOutput(OutputFormatPlugin): 'Creating a default TOC') first = iter(oeb_book.spine).next() oeb_book.toc.add(_('Start'), first.href) + else: + first = iter(oeb_book.spine).next() + if oeb_book.toc[0].href != first.href: + oeb_book.toc.add(_('Start'), first.href) for tocitem in oeb_book.toc: if tocitem.href.find('#') != -1: @@ -166,22 +170,49 @@ class SNBOutput(OutputFormatPlugin): tocInfoFile.close() # Output Files + oldTree = None + mergeLast = False + lastName = None for item in s: from calibre.ebooks.oeb.base import OEB_DOCS, OEB_IMAGES, PNG_MIME if m.hrefs[item.href].media_type in OEB_DOCS: if not item.href in outputFiles: - log.debug('Skipping %s because unused in TOC.' % item.href) - continue + log.debug('File %s is unused in TOC. Continue in last chapter' % item.href) + mergeLast = True + else: + log.debug('Output the modified chapter again: %s' % lastName) + if oldTree != None and mergeLast: + outputFile = open(os.path.join(snbcDir, lastName), 'wb') + outputFile.write(etree.tostring(oldTree, pretty_print=True, encoding='utf-8')) + outputFile.close() + mergeLast = False + log.debug('Converting %s to snbc...' % item.href) snbwriter = SNBMLizer(log) - snbcTrees = snbwriter.extract_content(oeb_book, item, outputFiles[item.href], opts) - for subName in snbcTrees: - postfix = '' - if subName != '': - postfix = '_' + subName - outputFile = open(os.path.join(snbcDir, ProcessFileName(item.href + postfix + ".snbc")), 'wb') - outputFile.write(etree.tostring(snbcTrees[subName], pretty_print=True, encoding='utf-8')) - outputFile.close() + snbcTrees = None + if not mergeLast: + snbcTrees = snbwriter.extract_content(oeb_book, item, outputFiles[item.href], opts) + for subName in snbcTrees: + postfix = '' + if subName != '': + postfix = '_' + subName + lastName = ProcessFileName(item.href + postfix + ".snbc") + oldTree = snbcTrees[subName] + outputFile = open(os.path.join(snbcDir, lastName), 'wb') + outputFile.write(etree.tostring(oldTree, pretty_print=True, encoding='utf-8')) + outputFile.close() + else: + log.debug('Merge %s with last TOC item...' % item.href) + snbwriter.merge_content(oldTree, oeb_book, item, [('', _("Start"))], opts) + + # Output the last one if needed + log.debug('Output the last modified chapter again: %s' % lastName) + if oldTree != None and mergeLast: + outputFile = open(os.path.join(snbcDir, lastName), 'wb') + outputFile.write(etree.tostring(oldTree, pretty_print=True, encoding='utf-8')) + outputFile.close() + mergeLast = False + for item in m: if m.hrefs[item.href].media_type in OEB_IMAGES: log.debug('Converting image: %s ...' % item.href) diff --git a/src/calibre/ebooks/snb/snbml.py b/src/calibre/ebooks/snb/snbml.py index c357971b5e..bfdaf53cae 100644 --- a/src/calibre/ebooks/snb/snbml.py +++ b/src/calibre/ebooks/snb/snbml.py @@ -70,6 +70,14 @@ class SNBMLizer(object): self.subitems = subitems return self.mlize(); + def merge_content(self, old_tree, oeb_book, item, subitems, opts): + newTrees = self.extract_content(oeb_book, item, subitems, opts) + body = old_tree.find(".//body") + if body != None: + for subName in newTrees: + newbody = newTrees[subName].find(".//body") + for entity in newbody: + body.append(entity) def mlize(self): output = [ u'' ] @@ -91,11 +99,12 @@ class SNBMLizer(object): line = line.strip(' \t\n\r') if len(line) != 0: if line.find(CALIBRE_SNB_IMG_TAG) == 0: - etree.SubElement(trees[subitem], "img").text = line[len(CALIBRE_SNB_IMG_TAG):] + etree.SubElement(trees[subitem].find(".//body"), "img").text = line[len(CALIBRE_SNB_IMG_TAG):] elif line.find(CALIBRE_SNB_BM_TAG) == 0: subitem = line[len(CALIBRE_SNB_BM_TAG):] else: - etree.SubElement(trees[subitem], "text").text = etree.CDATA(unicode(u'\u3000\u3000' + line)) + etree.SubElement(trees[subitem].find(".//body"), "text").text = \ + etree.CDATA(unicode(u'\u3000\u3000' + line)) return trees def remove_newlines(self, text): From 9be246500bfc1534d953753e56c338941886c9fc Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Mon, 11 Oct 2010 00:19:12 +0800 Subject: [PATCH 011/140] [SNBOutput] Removed a duplicated function. --- src/calibre/ebooks/snb/output.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index c8457347ec..bd27a0614e 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -12,21 +12,7 @@ from calibre.ptempfile import TemporaryDirectory from calibre.constants import __appname__, __version__ from calibre.ebooks.oeb.base import XHTML, XHTML_NS, barename, namespace from calibre.ebooks.snb.snbfile import SNBFile -from calibre.ebooks.snb.snbml import SNBMLizer - -def ProcessFileName(fileName): - # Flat the path - fileName = fileName.replace("/", "_").replace(os.sep, "_") - # Handle bookmark for HTML file - fileName = fileName.replace("#", "_") - # Make it lower case - fileName = fileName.lower() - # Change extension for image files to png - root, ext = os.path.splitext(fileName) - if ext in [ '.jpeg', '.jpg', '.gif', '.svg' ]: - fileName = root + '.png' - return fileName - +from calibre.ebooks.snb.snbml import SNBMLizer, ProcessFileName class SNBOutput(OutputFormatPlugin): From 29e133c61a9f6d813b4137fd94f4c8305c4de4f5 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Mon, 11 Oct 2010 00:41:24 +0800 Subject: [PATCH 012/140] [SNBOutput] Fixed a bug in image path handling. --- src/calibre/ebooks/snb/snbml.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/snb/snbml.py b/src/calibre/ebooks/snb/snbml.py index bfdaf53cae..72600fa4d2 100644 --- a/src/calibre/ebooks/snb/snbml.py +++ b/src/calibre/ebooks/snb/snbml.py @@ -99,7 +99,13 @@ class SNBMLizer(object): line = line.strip(' \t\n\r') if len(line) != 0: if line.find(CALIBRE_SNB_IMG_TAG) == 0: - etree.SubElement(trees[subitem].find(".//body"), "img").text = line[len(CALIBRE_SNB_IMG_TAG):] + prefix = ProcessFileName(os.path.dirname(self.item.href)) + if prefix != '': + etree.SubElement(trees[subitem].find(".//body"), "img").text = \ + prefix + '_' + line[len(CALIBRE_SNB_IMG_TAG):] + else: + etree.SubElement(trees[subitem].find(".//body"), "img").text = \ + line[len(CALIBRE_SNB_IMG_TAG):] elif line.find(CALIBRE_SNB_BM_TAG) == 0: subitem = line[len(CALIBRE_SNB_BM_TAG):] else: From 3ec09a3b40ac6eb637fb635d9ad2f81cff6d06c5 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Mon, 11 Oct 2010 00:49:11 +0800 Subject: [PATCH 013/140] [SBNOutput] Use jpg instead of png. --- src/calibre/ebooks/snb/output.py | 13 ++++--------- src/calibre/ebooks/snb/snbml.py | 4 ++-- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index bd27a0614e..7dd976ff25 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -160,7 +160,7 @@ class SNBOutput(OutputFormatPlugin): mergeLast = False lastName = None for item in s: - from calibre.ebooks.oeb.base import OEB_DOCS, OEB_IMAGES, PNG_MIME + from calibre.ebooks.oeb.base import OEB_DOCS, OEB_IMAGES if m.hrefs[item.href].media_type in OEB_DOCS: if not item.href in outputFiles: log.debug('File %s is unused in TOC. Continue in last chapter' % item.href) @@ -203,14 +203,9 @@ class SNBOutput(OutputFormatPlugin): if m.hrefs[item.href].media_type in OEB_IMAGES: log.debug('Converting image: %s ...' % item.href) content = m.hrefs[item.href].data - if m.hrefs[item.href].media_type != PNG_MIME: - # Convert & Resize image - self.HandleImage(content, os.path.join(snbiDir, ProcessFileName(item.href))) - else: - outputFile = open(os.path.join(snbiDir, ProcessFileName(item.href)), 'wb') - outputFile.write(content) - outputFile.close() - + # Convert & Resize image + self.HandleImage(content, os.path.join(snbiDir, ProcessFileName(item.href))) + # Package as SNB File snbFile = SNBFile() snbFile.FromDir(tdir) diff --git a/src/calibre/ebooks/snb/snbml.py b/src/calibre/ebooks/snb/snbml.py index 72600fa4d2..7be15d9fc6 100644 --- a/src/calibre/ebooks/snb/snbml.py +++ b/src/calibre/ebooks/snb/snbml.py @@ -23,10 +23,10 @@ def ProcessFileName(fileName): fileName = fileName.replace("#", "_") # Make it lower case fileName = fileName.lower() - # Change extension from jpeg to jpg + # Change all images to jpg root, ext = os.path.splitext(fileName) if ext in [ '.jpeg', '.jpg', '.gif', '.svg' ]: - fileName = root + '.png' + fileName = root + '.jpg' return fileName From 3c0673dcf5627dce63d568da615fda9dc614f98e Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Mon, 11 Oct 2010 00:51:34 +0800 Subject: [PATCH 014/140] [SBNOutput] Change debug log position to avoid confusion. --- src/calibre/ebooks/snb/output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index 7dd976ff25..7b661dfe7f 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -166,8 +166,8 @@ class SNBOutput(OutputFormatPlugin): log.debug('File %s is unused in TOC. Continue in last chapter' % item.href) mergeLast = True else: - log.debug('Output the modified chapter again: %s' % lastName) if oldTree != None and mergeLast: + log.debug('Output the modified chapter again: %s' % lastName) outputFile = open(os.path.join(snbcDir, lastName), 'wb') outputFile.write(etree.tostring(oldTree, pretty_print=True, encoding='utf-8')) outputFile.close() From dafa2c9034962d87ca03c4fd6302d658413751cf Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Mon, 11 Oct 2010 01:14:58 +0800 Subject: [PATCH 015/140] [SNBOutput] Improved unused pages handling. --- src/calibre/ebooks/snb/output.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index 7b661dfe7f..ef008013df 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -116,11 +116,19 @@ class SNBOutput(OutputFormatPlugin): log.warn('This SNB file has no Table of Contents. ' 'Creating a default TOC') first = iter(oeb_book.spine).next() - oeb_book.toc.add(_('Start'), first.href) + oeb_book.toc.add(_('Start Page'), first.href) else: first = iter(oeb_book.spine).next() if oeb_book.toc[0].href != first.href: - oeb_book.toc.add(_('Start'), first.href) + # The pages before the fist item in toc will be stored as + # "Cover Pages". + # oeb_book.toc does not support "insert", so we generate + # the tocInfoTree directly instead of modifying the toc + ch = etree.SubElement(tocBody, "chapter") + ch.set("src", ProcessFileName(first.href) + ".snbc") + ch.text = _('Cover Pages') + outputFiles[first.href] = [] + outputFiles[first.href].append(("", _("Cover Pages"))) for tocitem in oeb_book.toc: if tocitem.href.find('#') != -1: @@ -133,10 +141,10 @@ class SNBOutput(OutputFormatPlugin): else: outputFiles[item[0]] = [] if not "" in outputFiles[item[0]]: - outputFiles[item[0]].append(("", _("Start"))) + outputFiles[item[0]].append(("", _("Chapter Start"))) ch = etree.SubElement(tocBody, "chapter") ch.set("src", ProcessFileName(item[0]) + ".snbc") - ch.text = _("Start") + ch.text = _("Chapter Start") outputFiles[item[0]].append((item[1], tocitem.title)) else: if tocitem.href in outputFiles: From 63bb69d4ecf65834390887626ede389db76e15ef Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Mon, 11 Oct 2010 01:37:29 +0800 Subject: [PATCH 016/140] [SNBOutput] Change wording for the unused page content on each page before the first bookmark appeared in TOC. --- src/calibre/ebooks/snb/output.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index ef008013df..cbe785d384 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -141,10 +141,10 @@ class SNBOutput(OutputFormatPlugin): else: outputFiles[item[0]] = [] if not "" in outputFiles[item[0]]: - outputFiles[item[0]].append(("", _("Chapter Start"))) + outputFiles[item[0]].append(("", tocitem.title + _(" (Preface)"))) ch = etree.SubElement(tocBody, "chapter") ch.set("src", ProcessFileName(item[0]) + ".snbc") - ch.text = _("Chapter Start") + ch.text = tocitem.title + _(" (Preface)") outputFiles[item[0]].append((item[1], tocitem.title)) else: if tocitem.href in outputFiles: From cced0c3391e7efa85a46ac76767622ef8f5e31f3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 10 Oct 2010 11:53:08 -0600 Subject: [PATCH 017/140] Fix #7125 (Request: Date/time added sort in interface) --- src/calibre/gui2/dialogs/metadata_single.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index b39b752ac6..4c24365196 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -25,7 +25,7 @@ from calibre.ebooks.metadata.covers import download_cover from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata import MetaInformation from calibre.utils.config import prefs, tweaks -from calibre.utils.date import qt_to_dt, local_tz, utcfromtimestamp +from calibre.utils.date import qt_to_dt, local_tz, utcfromtimestamp, utc_tz from calibre.customize.ui import run_plugins_on_import, get_isbndb_key from calibre.gui2.preferences.social import SocialMetadata from calibre.gui2.custom_column_widgets import populate_metadata_page @@ -434,7 +434,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.pubdate.setDate(QDate(pubdate.year, pubdate.month, pubdate.day)) timestamp = db.timestamp(self.id, index_is_id=True) - self.orig_timestamp = timestamp + self.orig_timestamp = timestamp.astimezone(utc_tz) self.date.setDate(QDate(timestamp.year, timestamp.month, timestamp.day)) From 9d0121b8e3fec7caafa5b4ca99cdd02d558cd9d1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 10 Oct 2010 11:57:21 -0600 Subject: [PATCH 018/140] Revista El Cultural by Jefferson Frantz --- resources/recipes/el_cultural.recipe | 86 ++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 resources/recipes/el_cultural.recipe diff --git a/resources/recipes/el_cultural.recipe b/resources/recipes/el_cultural.recipe new file mode 100644 index 0000000000..124343398b --- /dev/null +++ b/resources/recipes/el_cultural.recipe @@ -0,0 +1,86 @@ +from calibre.web.feeds.recipes import BasicNewsRecipe + +class RevistaElCultural(BasicNewsRecipe): + + title = 'Revista El Cultural' + __author__ = 'Jefferson Frantz' + description = 'Revista de cultura' + timefmt = ' [%d %b, %Y]' + language = 'es' + + no_stylesheets = True + remove_javascript = True + + extra_css = 'h1{ font-family: sans-serif; font-size: large; font-weight: bolder; text-align: justify } h2{ font-family: sans-serif; font-size: small; font-weight: 500; text-align: justify } h3{ font-family: sans-serif; font-size: small; font-weight: 500; text-align: justify } h4{ font-family: sans-serif; font-weight: lighter; font-size: medium; font-style: italic; text-align: justify } .rtsArticuloFirma{ font-family: sans-serif; font-size: small; text-align: justify } .column span-13 last{ font-family: sans-serif; font-size: medium; text-align: justify } .rtsImgArticulo{font-family: serif; font-size: small; color: #000000; text-align: justify}' + + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + + return soup + + keep_only_tags = [dict(name='div', attrs={'class':['column span-13 last']}),dict(name='div', attrs={'class':['rtsImgArticulo']})] + + remove_tags = [ + dict(name=['object','link','script','ul']) + ,dict(name='div', attrs={'class':['rtsRating']}) + + ] + + + #TO GET ARTICLES IN SECTION + def ec_parse_section(self, url, titleSection): + print 'Section: '+ titleSection + soup = self.index_to_soup(url) + div = soup.find(attrs={'id':'gallery'}) + current_articles = [] + + for a in div.findAllNext('a', href=True): + if a is None: + continue + title = self.tag_to_string(a) + + url = a.get('href', False) + if not url or not title: + continue + + if not url.startswith('/version_papel/'+titleSection+'/'): + if len(current_articles) > 0 and not url.startswith('/secciones/'): + break + continue + + if url.startswith('/version_papel/'+titleSection+'/'): + url = 'http://www.elcultural.es'+url + + self.log('\t\tFound article:', title[0:title.find("|")-1]) + self.log('\t\t\t', url) + current_articles.append({'title': title[0:title.find("|")-1], 'url':url, + 'description':'', 'date':''}) + + return current_articles + + + # To GET SECTIONS + def parse_index(self): + feeds = [] + for title, url in [ + ('LETRAS', + 'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'), + ('ARTE', + 'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'), + ('CINE', + 'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'), + ('CIENCIA', + 'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'), +## ('OPINION', +## 'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'), + ('ESCENARIOS', + 'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'), + ]: + articles = self.ec_parse_section(url,title) + if articles: + feeds.append((title, articles)) + + + return feeds From 85ee22a1fca70b5ff7e69749732c5f077c829878 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 10 Oct 2010 12:10:07 -0600 Subject: [PATCH 019/140] ... --- src/calibre/manual/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/manual/index.rst b/src/calibre/manual/index.rst index d63b0b71a9..bc8e8a97c2 100644 --- a/src/calibre/manual/index.rst +++ b/src/calibre/manual/index.rst @@ -17,10 +17,10 @@ To get started with more advanced usage, you should read about the :ref:`Graphic You will find the list of :ref:`Frequently Asked Questions ` useful as well. -.. only:: html and online +.. only:: online + + An e-book version of this User Manual is available in `EPUB format `_. - An e-book version of this User Manual is available in `EPUB format `_. Because the User Manual uses advanced formatting, it is only suitable for use with the |app| e-book viewer. - Sections ------------ From 0030630453fa6efbcb7202bc56e740c266280b3f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 10 Oct 2010 12:24:29 -0600 Subject: [PATCH 020/140] Fix #7114 (Problem with title in metadata download) --- src/calibre/ebooks/metadata/isbndb.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/calibre/ebooks/metadata/isbndb.py b/src/calibre/ebooks/metadata/isbndb.py index 07a054eeaa..83cf6ee0ed 100644 --- a/src/calibre/ebooks/metadata/isbndb.py +++ b/src/calibre/ebooks/metadata/isbndb.py @@ -45,7 +45,7 @@ def fetch_metadata(url, max=100, timeout=5.): class ISBNDBMetadata(Metadata): def __init__(self, book): - Metadata.__init__(self, None, []) + Metadata.__init__(self, None) def tostring(e): if not hasattr(e, 'string'): @@ -58,21 +58,21 @@ class ISBNDBMetadata(Metadata): return ans self.isbn = unicode(book.get('isbn13', book.get('isbn'))) - self.title = tostring(book.find('titlelong')) - if not self.title: - self.title = tostring(book.find('title')) - if not self.title: - self.title = _('Unknown') + title = tostring(book.find('titlelong')) + if not title: + title = tostring(book.find('title')) + self.title = title self.title = unicode(self.title).strip() - self.authors = [] + authors = [] au = tostring(book.find('authorstext')) if au: au = au.strip() temp = au.split(',') for au in temp: if not au: continue - self.authors.extend([a.strip() for a in au.split('&')]) - + authors.extend([a.strip() for a in au.split('&')]) + if authors: + self.authors = authors try: self.author_sort = tostring(book.find('authors').find('person')) if self.authors and self.author_sort == self.authors[0]: From c650488bce88b105e25a8a3e3ef0cb2d0f3ff368 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 10 Oct 2010 12:56:48 -0600 Subject: [PATCH 021/140] RTF Input: Fix regression in conversion of WMF images on linux at least, maybe on other platforms as well --- src/calibre/utils/magick/__init__.py | 7 +++++++ src/calibre/utils/magick/magick.c | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/calibre/utils/magick/__init__.py b/src/calibre/utils/magick/__init__.py index 3a4fca09c0..bf0f48db7d 100644 --- a/src/calibre/utils/magick/__init__.py +++ b/src/calibre/utils/magick/__init__.py @@ -109,6 +109,13 @@ class Image(_magick.Image): # {{{ return _magick.Image.load(self, bytes(data)) def open(self, path_or_file): + if not hasattr(path_or_file, 'read') and \ + path_or_file.lower().endswith('.wmf'): + # Special handling for WMF files as ImageMagick seems + # to hand while reading them from a blob on linux + if isinstance(path_or_file, unicode): + path_or_file = path_or_file.encode(filesystem_encoding) + return _magick.Image.read(self, path_or_file) data = path_or_file if hasattr(data, 'read'): data = data.read() diff --git a/src/calibre/utils/magick/magick.c b/src/calibre/utils/magick/magick.c index b1436a830b..0aab5f1fd7 100644 --- a/src/calibre/utils/magick/magick.c +++ b/src/calibre/utils/magick/magick.c @@ -414,6 +414,24 @@ magick_Image_load(magick_Image *self, PyObject *args, PyObject *kwargs) { // }}} +// Image.load {{{ +static PyObject * +magick_Image_read(magick_Image *self, PyObject *args, PyObject *kwargs) { + const char *data; + MagickBooleanType res; + + if (!PyArg_ParseTuple(args, "s", &data)) return NULL; + + res = MagickReadImage(self->wand, data); + + if (!res) + return magick_set_exception(self->wand); + + Py_RETURN_NONE; +} + +// }}} + // Image.create_canvas {{{ static PyObject * magick_Image_create_canvas(magick_Image *self, PyObject *args, PyObject *kwargs) @@ -873,6 +891,10 @@ static PyMethodDef magick_Image_methods[] = { "Load an image from a byte buffer (string)" }, + {"read", (PyCFunction)magick_Image_read, METH_VARARGS, + "Read image from path. Path must be a bytestring in the filesystem encoding" + }, + {"export", (PyCFunction)magick_Image_export, METH_VARARGS, "export(format) -> bytestring\n\n Export the image as the specified format" }, From cd4950b98e572215e58da8cea39cee2ab63f4fce Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 10 Oct 2010 16:17:39 -0600 Subject: [PATCH 022/140] ... --- resources/content_server/{ => browse}/browse.css | 0 resources/content_server/{ => browse}/browse.html | 4 ++-- resources/content_server/{ => browse}/browse.js | 0 src/calibre/library/server/browse.py | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename resources/content_server/{ => browse}/browse.css (100%) rename resources/content_server/{ => browse}/browse.html (93%) rename resources/content_server/{ => browse}/browse.js (100%) diff --git a/resources/content_server/browse.css b/resources/content_server/browse/browse.css similarity index 100% rename from resources/content_server/browse.css rename to resources/content_server/browse/browse.css diff --git a/resources/content_server/browse.html b/resources/content_server/browse/browse.html similarity index 93% rename from resources/content_server/browse.html rename to resources/content_server/browse/browse.html index a71fff9e22..e278c20e39 100644 --- a/resources/content_server/browse.html +++ b/resources/content_server/browse/browse.html @@ -10,14 +10,14 @@ - + - + - + - + + + diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index e2a8dc934a..d534ea15c1 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -127,3 +127,16 @@ function toplevel() { }); } // }}} + +// Category feed {{{ +function category() { + $(".category li").corner("15px"); + + $(".category li").click(function() { + var href = $(this).children("span.href").html(); + window.location = href; + }); +} +// }}} + + diff --git a/resources/content_server/star-half.png b/resources/content_server/star-half.png new file mode 100644 index 0000000000000000000000000000000000000000..3c19e90a8a755e004424db205c8433f0f63048b4 GIT binary patch literal 667 zcmV;M0%ZM(P)5kxfVxVHn4M^Ulocu4}uCxq(JO)+Y9GD5Jat zbqNy5lR|3Ugd)g`R}H!Z-82#+xHAej^Uvb`)a1J$lTNk&_XNAE zBfTQ9v_v8Sa5IxmRs3@SYh=d-l+~gM@B>A4P0dc=P9~jd$fQ$sTUl-3B9(2Y;#Z9F ziv79~Bz4_Q3b9eCYwPF+d^xg&l#+bj;nT_rzOS%t8z2j`7`B7!=X2+jXB|TV=B8-| z5I_(F*tU&nN^1AkB4vn~=}AV%Mjv-|U3#ej40jVdS&P?6I*kPtxhy74l2*jJNmZ458vW9PWLT-hGu|=LysO@uBuA(2q79M zrYCoMu8-X7>n;ia!^1;Yo0`tX3%(WvK`H*P`T^?|->M&U?OXr=002ovPDHLkV1gTF BCtCmj literal 0 HcmV?d00001 diff --git a/resources/content_server/star-off.png b/resources/content_server/star-off.png new file mode 100644 index 0000000000000000000000000000000000000000..956fa7c637cddb4db6a091556cc63a0f6a186264 GIT binary patch literal 685 zcmV;e0#f~nP)R5*>Dl0QrnVHn1Ly)Enp;##exZh7()Uk zR1!C3adKe*!lVgta&se2n3x!5BLe|s!Nt*~X-#y7B3eoUik5~%+lD(Tz0ZL^0YjVU zv%Pu#yl=keg9gZ?Q_Q(|Z0g_5!WIoz~V%6X6SCq;x5j%6#A6o&%l(m4W2o+DSkrok{@j zfS9fu_%t7u6-EyR*{?cCDbX|y$Ei}>*}-*d*tQMu1GuLQBnNZ({Kj-B6r%H5Cv9zQ z1OftQ-v&TRiEZ1cs+abw?R0hbV45b`)$HqmfpE80>NxE=6Ci}>F?0hV0tABwPH{}X=zm$k-d TM0>cp00000NkvXXu0mjfLk}+u0K@`S+@BV*_sWrqlwFo8}(kQkaqzD;A z=u{lE-2?}_II1YR)S)U)E*26HCl}q?MW=Ld6)l=U=ui@ZMFoe7jcsaT?tgaXnG9{0ZQ-FLi$F8Gk$pICVsN_6A+-I}LP#xf!zP`KEQ*A|86(eYX?Vrxqg`*m?> zyc17#YQ&Emm{YDS9%UIE2;KXq0p(YRuVBj4qCoke?8ek~ZfK?*SF$toUz&(q^LzVF zc)m+{o=z$CtKX9fo)4nH88Gx=Z0LtIJw+2pF{o}Aa&I@D_|-bv)_zWzpnQfNU|%D| z2ONlib6`yct1JCRlQx>P4J2Q!lkr}!P)`rD5RWn;!ch^BrauSPHXPUh?BDz@Ut;od zmQ4>}ahc!a2^JEAoD+yQ#m?=B%^vZad=f}S8X&Tqm1m)@;e`m!IcA(C>>M0F#2E0tt%Gn#jhcX3%^aLBZN-xDTU?LbBKJ1?Kz|Loh!06cE>#5u?R;17H*+`wdH ROO^lt002ovPDHLkV1mP{6ITEL literal 0 HcmV?d00001 diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index e058ccd4f3..fc49a92025 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -5,12 +5,122 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import operator, os +import operator, os, sys +from urllib import quote +from binascii import hexlify import cherrypy from calibre.constants import filesystem_encoding from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml +from calibre.library.server.utils import Offsets + +def paginate(offsets, content, base_url, up_url=None): + 'Create markup for pagination' + + if '?' not in base_url: + base_url += '?' + + if base_url[-1] != '?': + base_url += '&' + + def navlink(decoration, name, cls, offset): + label = xml(name) + if cls in ('next', 'last'): + label += ' ' + decoration + else: + label = decoration + ' ' + label + return (u'' + u'{label}').format(cls=cls, decoration=decoration, + name=xml(name, True), offset=offset, + base_url=xml(base_url, True), label=label) + left = '' + if offsets.offset > 0 and offsets.previous_offset > 0: + left += navlink(u'\u219e', _('First'), 'first', 0) + if offsets.offset > 0: + left += ' ' + navlink('←', _('Previous'), 'previous', + offsets.previous_offset) + + middle = '' + if up_url: + middle = '[{1} ↑]'.format(xml(up_url, True), + xml(_('Up'))) + + right = '' + if offsets.next_offset > -1: + right += navlink('&rarr', _('Next'), 'next', offsets.next_offset) + if offsets.last_offset > offsets.next_offset and offsets.last_offset > 0: + right += ' ' + navlink(u'\u21A0', _('Last'), 'last', offsets.last_offset) + + navbar = u''' + + + + + + + + '''.format(left=left, right=right, middle=middle) + + templ = u''' +
+ {navbar} +
+ {content} +
+ {navbar} +
+ ''' + return templ.format(navbar=navbar, content=content) + +def utf8(x): + if isinstance(x, unicode): + x = x.encode('utf-8') + return x + +def render_rating(rating, container='span'): + if rating < 0.1: + return '', '' + added = 0 + rstring = xml(_('Average rating: %.1f stars')% (rating if rating else 0.0), + True) + ans = ['<%s class="rating">' % (container)] + for i in range(5): + n = rating - added + x = 'half' + if n <= 0.1: + x = 'off' + elif n >= 0.9: + x = 'on' + ans.append( + u'{0}'.format( + rstring, x)) + added += 1 + ans.append(''%container) + return u''.join(ans), rstring + +def get_category_items(category, items, offsets, db): + + def item(i): + templ = (u'
  • ' + '

    {0}  {1}

      {2}' + '{3}
  • ') + rating, rstring = render_rating(i.avg_rating) + name = xml(i.name) + id_ = i.id + if id_ is None: + id_ = hexlify(force_unicode(name).encode('utf-8')) + id_ = xml(str(id_)) + desc = '' + if i.count > 0: + desc += '[' + _('%d items')%i.count + ']' + href = '/browse/matches/%s/%s'%(category, id_) + return templ.format(xml(name), rating, + xml(desc), xml(quote(href)), rstring) + + items = list(map(item, items[offsets.offset:offsets.slice_upper_bound])) + return '\n'.join(['
      '] + items + ['
    ']) + class BrowseServer(object): @@ -23,7 +133,6 @@ class BrowseServer(object): connect('browse_search', base_href+'/search/{query}', self.browse_search) connect('browse_book', base_href+'/book/{uuid}', self.browse_book) - connect('browse_json', base_href+'/json/{query}', self.browse_json) def browse_template(self, category=True): @@ -36,7 +145,8 @@ class BrowseServer(object): sort_opts = [(x, fm[x]['name']) for x in fm.sortable_field_keys() if fm[x]['name']] prefix = 'category' if category else 'book' - ans = P('content_server/browse/browse.html', data=True) + ans = P('content_server/browse/browse.html', + data=True).decode('utf-8') ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':')) opts = ['' % (prefix, xml(k), xml(n)) for k, n in @@ -61,50 +171,102 @@ class BrowseServer(object): # Catalogs {{{ - def browse_catalog(self, category=None): - if category == None: - categories = self.categories_cache() - category_meta = self.db.field_metadata - cats = [ - (_('Newest'), 'newest'), - ] - def getter(x): - return category_meta[x]['name'].lower() - for category in sorted(categories, - cmp=lambda x,y: cmp(getter(x), getter(y))): - if len(categories[category]) == 0: - continue - if category == 'formats': - continue - meta = category_meta.get(category, None) - if meta is None: - continue - cats.append((meta['name'], category)) - cats = ['
  • {0}/browse/category/{1}
  • '.format(xml(x, True), - xml(y), xml(_('Browse books by'))) for x, y in cats] + def browse_toplevel(self): + categories = self.categories_cache() + category_meta = self.db.field_metadata + cats = [ + (_('Newest'), 'newest'), + ] - main = '

    {0}

      {1}
    '\ - .format(_('Choose a category to browse by:'), '\n\n'.join(cats)) - ans = self.browse_template().format(title='', - script='toplevel();', main=main) - else: + def getter(x): + return category_meta[x]['name'].lower() + + for category in sorted(categories, + cmp=lambda x,y: cmp(getter(x), getter(y))): + if len(categories[category]) == 0: + continue + if category == 'formats': + continue + meta = category_meta.get(category, None) + if meta is None: + continue + cats.append((meta['name'], category)) + cats = ['
  • {0}/browse/category/{1}
  • '\ + .format(xml(x, True), xml(quote(y)), xml(_('Browse books by'))) + for x, y in cats] + + main = '

    {0}

      {1}
    '\ + .format(_('Choose a category to browse by:'), '\n\n'.join(cats)) + return self.browse_template().format(title='', + script='toplevel();', main=main) + + def browse_category(self, category, offset, sort, subcategory=None): + categories = self.categories_cache() + category_meta = self.db.field_metadata + category_name = category_meta[category]['name'] + + if category not in categories: + raise cherrypy.HTTPError(404, 'category not found') + + items = categories[category] + + base_url='/browse/category/'+category+'?' + if subcategory is not None: + base_url += 'subcategory='+quote(subcategory) + if sort is not None: + base_url += 'sort='+quote(sort) + + script = 'category();' + + max_items = sys.maxint + offsets = Offsets(offset, max_items, len(items)) + items = list(items)[offsets.offset:offsets.offset+max_items] + items = get_category_items(category, items, offsets, self.db) + main = u''' +
    +

    {0}

    +

    [{2} ↑] +

    + {1} +
    + '''.format( + xml(_('Browsing by')+': ' + category_name), items, + xml(_('Up'), True)) + + return self.browse_template().format(title=category_name, + script=script, main=main) + + + + def browse_catalog(self, category=None, offset=0, sort=None, + subcategory=None): + 'Entry point for top-level, categories and sub-categories' + try: + offset = int(offset) + except: raise cherrypy.HTTPError(404, 'Not found') + if category == None: + ans = self.browse_toplevel() + else: + ans = self.browse_category(category, offset, sort) + cherrypy.response.headers['Content-Type'] = 'text/html' updated = self.db.last_modified() cherrypy.response.headers['Last-Modified'] = \ self.last_modified(max(updated, self.build_time)) - return ans + return utf8(ans) # }}} # Book Lists {{{ - def browse_list(self, query=None): + def browse_list(self, query=None, offset=0, sort=None): raise NotImplementedError() # }}} # Search {{{ - def browse_search(self, query=None): + def browse_search(self, query=None, offset=0, sort=None): raise NotImplementedError() # }}} @@ -113,8 +275,4 @@ class BrowseServer(object): raise NotImplementedError() # }}} - # JSON {{{ - def browse_json(self, query=None): - raise NotImplementedError() - # }}} diff --git a/src/calibre/library/server/utils.py b/src/calibre/library/server/utils.py index 4c286b555b..35c92f7ae2 100644 --- a/src/calibre/library/server/utils.py +++ b/src/calibre/library/server/utils.py @@ -23,6 +23,7 @@ class Offsets(object): raise cherrypy.HTTPError(404, 'Invalid offset: %r'%offset) last_allowed_index = total - 1 last_current_index = offset + delta - 1 + self.slice_upper_bound = offset+delta self.offset = offset self.next_offset = last_current_index + 1 if self.next_offset > last_allowed_index: From a5f2c1fbb1f429acbd11ca56eb1d3025914e10da Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 Oct 2010 15:11:06 -0600 Subject: [PATCH 066/140] Fix #7168 (unhandled exception when changing cover picture) --- src/calibre/gui2/convert/metadata.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/convert/metadata.py b/src/calibre/gui2/convert/metadata.py index b206bf68a6..7a02cf4429 100644 --- a/src/calibre/gui2/convert/metadata.py +++ b/src/calibre/gui2/convert/metadata.py @@ -172,7 +172,7 @@ class MetadataWidget(Widget, Ui_Form): if _file: _file = os.path.abspath(_file) if not os.access(_file, os.R_OK): - d = error_dialog(self.window, _('Cannot read'), + d = error_dialog(self.parent(), _('Cannot read'), _('You do not have permission to read the file: ') + _file) d.exec_() return @@ -181,14 +181,14 @@ class MetadataWidget(Widget, Ui_Form): cf = open(_file, "rb") cover = cf.read() except IOError, e: - d = error_dialog(self.window, _('Error reading file'), + d = error_dialog(self.parent(), _('Error reading file'), _("

    There was an error reading from file:
    ") + _file + "


    "+str(e)) d.exec_() if cover: pix = QPixmap() pix.loadFromData(cover) if pix.isNull(): - d = error_dialog(self.window, _('Error reading file'), + d = error_dialog(self.parent(), _('Error reading file'), _file + _(" is not a valid picture")) d.exec_() else: From 4f031e05dd23223a9f7bbfead2a4dd7cac20e4d2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 Oct 2010 18:47:46 -0600 Subject: [PATCH 067/140] Sorting for CS category views --- resources/content_server/browse/browse.html | 7 +- resources/content_server/browse/browse.js | 146 +++++++----------- .../content_server/jquery.multiselect.css | 21 +++ .../content_server/jquery.multiselect.min.js | 16 ++ src/calibre/library/server/browse.py | 118 +++++++++----- 5 files changed, 173 insertions(+), 135 deletions(-) create mode 100644 resources/content_server/jquery.multiselect.css create mode 100644 resources/content_server/jquery.multiselect.min.js diff --git a/resources/content_server/browse/browse.html b/resources/content_server/browse/browse.html index 1258088741..b5c208fd46 100644 --- a/resources/content_server/browse/browse.html +++ b/resources/content_server/browse/browse.html @@ -12,17 +12,22 @@ + + diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 905b600322..c2d942efab 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -95,7 +95,7 @@ function category() { window.location = href; }); - $(".category a.navlink").button({ primary: 'ui-icon-arrow-1-n'}); + $(".category a.navlink").button(); } // }}} diff --git a/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_flat_75_aaaaaa_40x100.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_flat_75_aaaaaa_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5dab2ab7b1c50dea9cfe73dc5a269a92d2d4b4 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FscKIb$B>N1x91EQ4=4yQ7#`R^ z$vje}bP0l+XkK DSH>_4 literal 0 HcmV?d00001 diff --git a/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_100_f5f0e5_1x400.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_100_f5f0e5_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..688c2f3e8806f3afcc0b659d397d142ee4a0bb1c GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAGVZ#9R3S1yVd+978O6-(EE2I&2`|a&dor zn8LpQws$p8OzfO3V%e~f#q0BpwPkHfr@d8n;J+1T@Kk-4oVon+?9YosV?K4Y1e?D1 zs@@-9Jjqz`Hmt(*75f0D)F-JJVxa8Kd#rie_?PKtC^>bP0 Hl+XkK#L_u` literal 0 HcmV?d00001 diff --git a/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_25_cb842e_1x400.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_25_cb842e_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..cabc6a964db6f3249e44f544e7970b295b492183 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouq&z)c978O6-=0_GYj6;7zIfl> z{Bes+Z?i(7w%Aq;8&1j3la`r2y|G=>;j+p8)EIRq15P2qqe~3LCLIb%eAIQmB}H`p e{LER;dZjns;J6a8{m*5fp$wj`elF{r5}E*dLM)pA literal 0 HcmV?d00001 diff --git a/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_70_ede4d4_1x400.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_70_ede4d4_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..779ef0d0e24ce761cdbf952b5f1db4c80face3fe GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouq+C2*978O6-=0_GI$$8u{Lp^q zf7$AWm7QHq4}Ei%X4Un~=od&jRJp~-f#c!!GZza*BQ7zQe>!w*!){Z@V@~lu1f43L ZGQIlG{q(*7ZyC@$22WQ%mvv4FO#pkXEp7k+ literal 0 HcmV?d00001 diff --git a/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_highlight-hard_100_f4f0ec_1x100.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_highlight-hard_100_f4f0ec_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..8766f00183abc559eeaa61766dc0401ba19de22c GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^j6j^i!3HGVb)pi0l%A)HV~E7m5FH&+JoXP|@hd!f^Go?6iA_FS-MDGI+ZBxvXMZJAC$8GnU0<<-ano)i%lSMBf^9780g&YfV$*X+Qi{M=B2PZvC@% qQ`Wu?mFM1dqC1Ot*w3`iJS6V%TOd*5`~fGR`3#<}elF{r5}E)^Gc&~i literal 0 HcmV?d00001 diff --git a/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-icons_8c291d_256x240.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_c47a23_256x240.png similarity index 94% rename from resources/content_server/jquery_ui/css/pepper-grinder/images/ui-icons_8c291d_256x240.png rename to resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_c47a23_256x240.png index 961c8fea3bd431af0918d53a6ff55f8f8bfd014e..2e77966d429388240068a60115c22c288b4336cc 100644 GIT binary patch delta 267 tcmaE@`C4;=X8n;Wm4QU{^tJx6UEwnZ1_r;9AirP+2>~OP&FYLtgaND-cUu4e delta 267 tcmaE@`C4;=W_^#A%s`@g`dYsx?r9kV1A|{lkY6x^gn$vtW_89R!T<>SO4tAZ diff --git a/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-icons_3572ac_256x240.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_cb672b_256x240.png similarity index 92% rename from resources/content_server/jquery_ui/css/pepper-grinder/images/ui-icons_3572ac_256x240.png rename to resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_cb672b_256x240.png index 6127bb0c84991c92d688df5e5d65e4fd9f67ee64..b75ecdabd116ba5934db42fc78595081d146fe84 100644 GIT binary patch delta 267 ucmbQJG*M}SX8oBooq8`fx)jN$S;^dLcoY+vpVAuVE`p{RCfRX diff --git a/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-icons_222222_256x240.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_f35f07_256x240.png similarity index 92% rename from resources/content_server/jquery_ui/css/pepper-grinder/images/ui-icons_222222_256x240.png rename to resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_f35f07_256x240.png index b273ff111d219c9b9a8b96d57683d0075fb7871a..3ecc7d27eca0698af2545242426498322f5b1bd8 100644 GIT binary patch delta 267 tcmbQJG*M}SX8o5qmVrd|^tJwWoM`|91A|{lkY6x^gn$vtW_89_`~cyMa!mjL delta 267 tcmbQJG*M}SX1$7%%0Qxe`da_taEAy31A|{lkY6x^gn$vtW_89_`~Y0{DhdDq diff --git a/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-icons_fbdb93_256x240.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_ff7519_256x240.png similarity index 92% rename from resources/content_server/jquery_ui/css/pepper-grinder/images/ui-icons_fbdb93_256x240.png rename to resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_ff7519_256x240.png index 95c4a66f6679511ccf8a96c5af8521308cd276f7..544517142205f1adfc22b44eda10028e10abd42c 100644 GIT binary patch delta 267 ucmbQJG*M}SR(+YwpMga6bhVyYbLmzF1_r;9AirP+2>~M(j?Eg3FZluEhJtDU delta 267 vcmbQJG*M}SR{gChe+ClO)7AQ|C-ZX|7#RFYg8YIRBm|6DI5ulAzT^i0pf4C3?CzhDc0JKJ$6{&+`X>Kwab5 zhnIh~*(4mEedg>kG!R_;;_~ls9v=g?`3ods6n81H0fCwXbFAU6l{!2g43UOyR}J{| z7`98zK74U*<0H3{`O4bIq-pk)l6u6{1-oD!Mf4d3(AzhDc29op6x%fB^@yf9%QM z{Re#BDR+hhPC9UU0W&iLo6bq^=QXdyQl_0K2;A-SD=K|QRt1B~#_kS@M@irBe=PC& gIq&W2D1jfWR=PQ7Y?df{1C3?yboFyt=akR{0BZLyAOHXW diff --git a/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-bg_fine-grain_10_eceadf_60x60.png b/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-bg_fine-grain_10_eceadf_60x60.png deleted file mode 100644 index 46567420785a91503f30de00eec3a331cd4a969e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4429 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 ncXnGA{OfOX&WZaj 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 b8Sj4sJ%r;G6~R8400000NkvXXu0mjfnlArE diff --git a/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-bg_fine-grain_15_ffffff_60x60.png b/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-bg_fine-grain_15_ffffff_60x60.png deleted file mode 100644 index 19ab46c3f875f352230abdd8a2a651003b8b83b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3716 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 Qe{7QJJu=d+)_fWIKRK1dSpWb4 diff --git a/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-bg_fine-grain_68_b83400_60x60.png b/resources/content_server/jquery_ui/css/pepper-grinder/images/ui-bg_fine-grain_68_b83400_60x60.png deleted file mode 100644 index 3f3639b490c47377b4a7bf6f57d52883ddda9cce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8306 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

    ! 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 R#}izLrLL?4d#h+0@gK7EGZp{< diff --git a/resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js b/resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js index 827b5f05ba..b63fe5cf60 100644 --- a/resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js +++ b/resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js @@ -63,210 +63,6 @@ a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_ b.left=d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0]; b.left+=a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d= c(b),g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); -;/* - * jQuery UI Draggable 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Draggables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== -"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= -this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;return true},_mouseStart:function(a){var b=this.options;this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top- -this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions(); -d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);return true},_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis|| -this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if(!this.element[0]||!this.element[0].parentNode)return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element, -b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this== -a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone():this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]|| -0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], -this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top- -(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment== -"parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&& -a.containment.constructor!=Array){var b=d(a.containment)[0];if(b){a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"), -10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], -this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft(): -f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.leftthis.containment[2])e=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?e:!(e-this.offset.click.left').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")})},stop:function(){d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options; -if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!= -"HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY=0;h--){var i=c.snapElements[h].left,k=i+c.snapElements[h].width,j=c.snapElements[h].top,l=j+c.snapElements[h].height;if(i-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>= -i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), -top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= -this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", -nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== -String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),k=0;k=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,k);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection(); -this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){e(this).removeClass("ui-resizable-autohide");b._handles.show()},function(){if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()}; -if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(), -d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset= -this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff={width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio: -this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize", -b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height; -f={width:c.size.width-(f?0:c.sizeDiff.width),height:c.size.height-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop", -b);this._helper&&this.helper.remove();return false},_updateCache:function(b){this.offset=this.helper.offset();if(l(b.left))this.position.left=b.left;if(l(b.top))this.position.top=b.top;if(l(b.height))this.size.height=b.height;if(l(b.width))this.size.width=b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(b.height)b.width=c.height*this.aspectRatio;else if(b.width)b.height=c.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top= -a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this.options,c=this.axis,d=l(b.width)&&a.maxWidth&&a.maxWidthb.width,h=l(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height, -k=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&k)b.left=i-a.minWidth;if(d&&k)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+ -a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this, -arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable, -{version:"1.8.5"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize, -function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var k=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:k.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n= -(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(k.css("position"))){c._revertToRelativePosition=true;k.css({position:"absolute",top:"auto",left:"auto"})}k.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition= -false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left- -a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize", -b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top", -"Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset, -f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left= -a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+ -a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&& -e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative", -height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width= -d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},l=function(b){return!isNaN(parseInt(b,10))}})(jQuery); -;/* - * jQuery UI Selectable 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Selectables - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"), -selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("

    ")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX, -c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting", -c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d= -this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.righti||a.bottomb&&a.rightg&&a.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable"); -this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this, -arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem= -c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset, -{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment(); -if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start", -a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute"); -if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a, -c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]== -document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp();this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate", -null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem): -d(this.domPosition.parent).prepend(this.currentItem);return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute|| -"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")}, -_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!= -this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a= -this.currentItem.find(":data(sortable-item)"),b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable"); -if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h= -0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width= -this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f}, -update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b= -null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this)); -this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])? -g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive", -g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over= -0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e").addClass("ui-autocomplete").appendTo(e(this.options.appendTo||"body",b)[0]).mousedown(function(c){var d=a.menu.element[0]; -c.target===d&&setTimeout(function(){e(document).one("mousedown",function(f){f.target!==a.element[0]&&f.target!==d&&!e.ui.contains(d,f.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,d){d=d.item.data("item.autocomplete");false!==a._trigger("focus",null,{item:d})&&/^key/.test(c.originalEvent.type)&&a.element.val(d.value)},selected:function(c,d){d=d.item.data("item.autocomplete");var f=a.previous;if(a.element[0]!==b.activeElement){a.element.focus(); -a.previous=f}if(false!==a._trigger("select",c,{item:d})){a.term=d.value;a.element.val(d.value)}a.close(c);a.selectedItem=d},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");e.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"); -this.menu.element.remove();e.Widget.prototype.destroy.call(this)},_setOption:function(a,b){e.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(e(b||"body",this.element[0].ownerDocument)[0])},_initSource:function(){var a=this,b,c;if(e.isArray(this.options.source)){b=this.options.source;this.source=function(d,f){f(e.ui.autocomplete.filter(b,d.term))}}else if(typeof this.options.source==="string"){c=this.options.source;this.source= -function(d,f){a.xhr&&a.xhr.abort();a.xhr=e.getJSON(c,d,function(g,i,h){h===a.xhr&&f(g);a.xhr=null})}}else this.source=this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length").data("item.autocomplete",b).append(e("
    ").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});e.extend(e.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")}, -filter:function(a,b){var c=new RegExp(e.ui.autocomplete.escapeRegex(b),"i");return e.grep(a,function(d){return c.test(d.label||d.value||d)})}})})(jQuery); -(function(e){e.widget("ui.menu",{_create:function(){var a=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(b){if(e(b.target).closest(".ui-menu-item a").length){b.preventDefault();a.select(b)}});this.refresh()},refresh:function(){var a=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", --1).mouseenter(function(b){a.activate(b,e(this).parent())}).mouseleave(function(){a.deactivate()})},activate:function(a,b){this.deactivate();if(this.hasScroll()){var c=b.offset().top-this.element.offset().top,d=this.element.attr("scrollTop"),f=this.element.height();if(c<0)this.element.attr("scrollTop",d+c);else c>=f&&this.element.attr("scrollTop",d+c-f+b.height())}this.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",a,{item:b})}, -deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");this._trigger("blur");this.active=null}},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(this.active){a=this.active[a+"All"](".ui-menu-item").eq(0); -a.length?this.activate(c,a):this.activate(c,this.element.children(b))}else this.activate(c,this.element.children(b))},nextPage:function(a){if(this.hasScroll())if(!this.active||this.last())this.activate(a,this.element.children(":first"));else{var b=this.active.offset().top,c=this.element.height(),d=this.element.children("li").filter(function(){var f=e(this).offset().top-b-c+e(this).height();return f<10&&f>-10});d.length||(d=this.element.children(":last"));this.activate(a,d)}else this.activate(a,this.element.children(!this.active|| -this.last()?":first":":last"))},previousPage:function(a){if(this.hasScroll())if(!this.active||this.first())this.activate(a,this.element.children(":last"));else{var b=this.active.offset().top,c=this.element.height();result=this.element.children("li").filter(function(){var d=e(this).offset().top-b+c-e(this).height();return d<10&&d>-10});result.length||(result=this.element.children(":first"));this.activate(a,result)}else this.activate(a,this.element.children(!this.active||this.first()?":last":":first"))}, -hasScroll:function(){return this.element.height()").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary;if(d.primary||d.secondary){b.addClass("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary"));d.primary&&b.prepend("");d.secondary&&b.append("");if(!this.options.text){b.addClass(e?"ui-button-icons-only":"ui-button-icon-only").removeClass("ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary"); this.hasTitle||b.attr("title",c)}}else b.addClass("ui-button-text-only")}}});a.widget("ui.buttonset",{_create:function(){this.element.addClass("ui-buttonset");this._init()},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(":button, :submit, :reset, :checkbox, :radio, a, :data(button)").filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":visible").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end().end()}, destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");a.Widget.prototype.destroy.call(this)}})})(jQuery); -;/* - * jQuery UI Dialog 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Dialog - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - * jquery.ui.button.js - * jquery.ui.draggable.js - * jquery.ui.mouse.js - * jquery.ui.position.js - * jquery.ui.resizable.js - */ -(function(c,j){c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,position:{my:"center",at:"center",of:window,collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title"); -if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||" ",f=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog", -"aria-labelledby":f}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var e=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i); -return false}).appendTo(e);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id",f).html(d).prependTo(e);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;e.find("*").add(e).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&& -g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog"); -b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==b.uiDialog[0])d=Math.max(d,c(this).css("z-index"))});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,f=d.options;if(f.modal&&!a||!f.stack&&!f.modal)return d._trigger("focus",b);if(f.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ= -f.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+=1;d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;d.next().length&&d.appendTo("body");a._size();a._position(b.position);d.show(b.show); -a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(f){if(f.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),e=g.filter(":first");g=g.filter(":last");if(f.target===g[0]&&!f.shiftKey){e.focus(1);return false}else if(f.target===e[0]&&f.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false, -f=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("
    ").addClass("ui-dialog-buttonset").appendTo(f);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,function(){return!(d=true)});if(d){c.each(a,function(e,h){h=c.isFunction(h)?{click:h,text:e}:h;e=c("",h).unbind("click").click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.fn.button&&e.button()});f.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(e){return{position:e.position, -offset:e.offset}}var b=this,d=b.options,f=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(e,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",e,a(h))},drag:function(e,h){b._trigger("drag",e,a(h))},stop:function(e,h){d.position=[h.position.left-f.scrollLeft(),h.position.top-f.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g); -b._trigger("dragStop",e,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}a=a===j?this.options.resizable:a;var d=this,f=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:f.maxWidth,maxHeight:f.maxHeight,minWidth:f.minWidth,minHeight:d._minHeight(), -handles:a,start:function(e,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",e,b(h))},resize:function(e,h){d._trigger("resize",e,b(h))},stop:function(e,h){c(this).removeClass("ui-dialog-resizing");f.height=c(this).height();f.width=c(this).width();d._trigger("resizeStop",e,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight, -a.height)},_position:function(a){var b=[],d=[0,0],f;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,e){if(+b[g]===b[g]){d[g]=b[g];b[g]=e}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(f=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(a); -f||this.uiDialog.hide()},_setOption:function(a,b){var d=this,f=d.uiDialog,g=f.is(":data(resizable)"),e=false;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);e=true;break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":f.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case "draggable":b? -d._makeDraggable():f.draggable("destroy");break;case "height":e=true;break;case "maxHeight":g&&f.resizable("option","maxHeight",b);e=true;break;case "maxWidth":g&&f.resizable("option","maxWidth",b);e=true;break;case "minHeight":g&&f.resizable("option","minHeight",b);e=true;break;case "minWidth":g&&f.resizable("option","minWidth",b);e=true;break;case "position":d._position(b);break;case "resizable":g&&!b&&f.resizable("destroy");g&&typeof b==="string"&&f.resizable("option","handles",b);!g&&b!==false&& -d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||" "));break;case "width":e=true;break}c.Widget.prototype._setOption.apply(d,arguments);e&&d._size()},_size:function(){var a=this.options,b;this.element.css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();this.element.css(a.height==="auto"?{minHeight:Math.max(a.minHeight-b,0),height:c.support.minHeight?"auto":Math.max(a.minHeight- -b,0)}:{minHeight:0,height:Math.max(a.height-b,0)}).show();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.5",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","), -function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){this.oldInstances.push(this.instances.splice(c.inArray(a,this.instances),1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var b=0;c.each(this.instances,function(){b=Math.max(b,this.css("z-index"))});this.maxZ=b},height:function(){var a, -b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a");if(!b.values)b.values=[this._valueMin(),this._valueMin()];if(b.values.length&&b.values.length!==2)b.values=[b.values[0],b.values[0]]}else this.range=d("
    ");this.range.appendTo(this.element).addClass("ui-slider-range");if(b.range==="min"||b.range==="max")this.range.addClass("ui-slider-range-"+b.range);this.range.addClass("ui-widget-header")}d(".ui-slider-handle",this.element).length===0&&d("").appendTo(this.element).addClass("ui-slider-handle"); -if(b.values&&b.values.length)for(;d(".ui-slider-handle",this.element).length").appendTo(this.element).addClass("ui-slider-handle");this.handles=d(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(c){c.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur(); -else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(c){d(this).data("index.ui-slider-handle",c)});this.handles.keydown(function(c){var e=true,f=d(this).data("index.ui-slider-handle"),h,g,i;if(!a.options.disabled){switch(c.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:e= -false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");h=a._start(c,f);if(h===false)return}break}i=a.options.step;h=a.options.values&&a.options.values.length?(g=a.values(f)):(g=a.value());switch(c.keyCode){case d.ui.keyCode.HOME:g=a._valueMin();break;case d.ui.keyCode.END:g=a._valueMax();break;case d.ui.keyCode.PAGE_UP:g=a._trimAlignValue(h+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:g=a._trimAlignValue(h-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(h=== -a._valueMax())return;g=a._trimAlignValue(h+i);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(h===a._valueMin())return;g=a._trimAlignValue(h-i);break}a._slide(c,f,g);return e}}).keyup(function(c){var e=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(c,e);a._change(c,e);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"); -this._mouseDestroy();return this},_mouseCapture:function(a){var b=this.options,c,e,f,h,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});e=this._valueMax()-this._valueMin()+1;h=this;this.handles.each(function(i){var j=Math.abs(c-h.values(i));if(e>j){e=j;f=d(this);g=i}});if(b.range===true&&this.values(1)===b.min){g+=1;f=d(this.handles[g])}if(this._start(a, -g)===false)return false;this._mouseSliding=true;h._handleIndex=g;f.addClass("ui-state-active").focus();b=f.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-f.width()/2,top:a.pageY-b.top-f.height()/2-(parseInt(f.css("borderTopWidth"),10)||0)-(parseInt(f.css("borderBottomWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0)};this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b= -this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b= -this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b); -c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var e;if(this.options.values&&this.options.values.length){e=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>e||b===1&&c1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;e=arguments[0];for(f=0;fthis._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=a%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a= -this.options.range,b=this.options,c=this,e=!this._animateOff?b.animate:false,f,h={},g,i,j,l;if(this.options.values&&this.options.values.length)this.handles.each(function(k){f=(c.values(k)-c._valueMin())/(c._valueMax()-c._valueMin())*100;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";d(this).stop(1,1)[e?"animate":"css"](h,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(k===0)c.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},b.animate);if(k===1)c.range[e?"animate":"css"]({width:f- -g+"%"},{queue:false,duration:b.animate})}else{if(k===0)c.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},b.animate);if(k===1)c.range[e?"animate":"css"]({height:f-g+"%"},{queue:false,duration:b.animate})}g=f});else{i=this.value();j=this._valueMin();l=this._valueMax();f=l!==j?(i-j)/(l-j)*100:0;h[c.orientation==="horizontal"?"left":"bottom"]=f+"%";this.handle.stop(1,1)[e?"animate":"css"](h,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"}, -b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[e?"animate":"css"]({width:100-f+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[e?"animate":"css"]({height:100-f+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.5"})})(jQuery); -;/* - * jQuery UI Tabs 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Tabs - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this._tabify(true)},_setOption:function(a,e){if(a=="selected")this.options.collapsible&& -e==this.options.selected||this.select(e);else{this.options[a]=e;this._tabify()}},_tabId:function(a){return a.title&&a.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:function(){var a=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[a].concat(d.makeArray(arguments)))},_ui:function(a,e){return{tab:a,panel:e,index:this.anchors.index(a)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var a= -d(this);a.html(a.data("label.tabs")).removeData("label.tabs")})},_tabify:function(a){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var b=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]|| -(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))b.panels=b.panels.add(b._sanitizeSelector(i));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=b._tabId(f);f.href="#"+i;f=d("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(b.panels[g-1]||b.list);f.data("destroy.tabs",true)}b.panels=b.panels.add(f)}else c.disabled.push(g)});if(a){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); -this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(b._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected= -this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return b.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); -if(c.selected>=0&&this.anchors.length){this.panels.eq(c.selected).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");b.element.queue("tabs",function(){b._trigger("show",null,b._ui(b.anchors[c.selected],b.panels[c.selected]))});this.load(c.selected)}d(window).bind("unload",function(){b.lis.add(b.anchors).unbind(".tabs");b.lis=b.anchors=b.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));this.element[c.collapsible?"addClass": -"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);a=0;for(var j;j=this.lis[a];a++)d(j)[d.inArray(a,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+g)};this.lis.bind("mouseover.tabs", -function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",function(){e(f,o);b._trigger("show", -null,b._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");b._trigger("show",null,b._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){b.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);b.element.dequeue("tabs")})}:function(g,f){b.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");b.element.dequeue("tabs")};this.anchors.bind(c.event+".tabs", -function(){var g=this,f=d(g).closest("li"),i=b.panels.filter(":not(.ui-tabs-hide)"),l=d(b._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||b.panels.filter(":animated").length||b._trigger("select",null,b._ui(this,l[0]))===false){this.blur();return false}c.selected=b.anchors.index(this);b.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=-1;c.cookie&&b._cookie(c.selected,c.cookie);b.element.queue("tabs", -function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&b._cookie(c.selected,c.cookie);b.element.queue("tabs",function(){r(g,l)});b.load(b.anchors.index(this));this.blur();return false}c.cookie&&b._cookie(c.selected,c.cookie);if(l.length){i.length&&b.element.queue("tabs",function(){s(g,i)});b.element.queue("tabs",function(){r(g,l)});b.load(b.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";d.browser.msie&&this.blur()});this.anchors.bind("click.tabs", -function(){return false})},_getIndex:function(a){if(typeof a=="string")a=this.anchors.index(this.anchors.filter("[href$="+a+"]"));return a},destroy:function(){var a=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=d.data(this,"href.tabs");if(e)this.href= -e;var b=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){b.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});a.cookie&&this._cookie(null,a.cookie);return this},add:function(a,e,b){if(b===p)b=this.anchors.length; -var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,a).replace(/#\{label\}/g,e));a=!a.indexOf("#")?a.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=d("#"+a);j.length||(j=d(h.panelTemplate).attr("id",a).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(b>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[b]); -j.insertBefore(this.panels[b])}h.disabled=d.map(h.disabled,function(k){return k>=b?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[b],this.panels[b]));return this},remove:function(a){a=this._getIndex(a);var e=this.options,b=this.lis.eq(a).remove(),c=this.panels.eq(a).remove(); -if(b.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(a+(a+1=a?--h:h});this._tabify();this._trigger("remove",null,this._ui(b.find("a")[0],c[0]));return this},enable:function(a){a=this._getIndex(a);var e=this.options;if(d.inArray(a,e.disabled)!=-1){this.lis.eq(a).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(b){return b!=a});this._trigger("enable",null, -this._ui(this.anchors[a],this.panels[a]));return this}},disable:function(a){a=this._getIndex(a);var e=this.options;if(a!=e.selected){this.lis.eq(a).addClass("ui-state-disabled");e.disabled.push(a);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[a],this.panels[a]))}return this},select:function(a){a=this._getIndex(a);if(a==-1)if(this.options.collapsible&&this.options.selected!=-1)a=this.options.selected;else return this;this.anchors.eq(a).trigger(this.options.event+".tabs");return this}, -load:function(a){a=this._getIndex(a);var e=this,b=this.options,c=this.anchors.eq(a)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(a).addClass("ui-state-processing");if(b.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(b.spinner)}this.xhr=d.ajax(d.extend({},b.ajaxOptions,{url:h,success:function(k,n){d(e._sanitizeSelector(c.hash)).html(k);e._cleanup();b.cache&&d.data(c,"cache.tabs", -true);e._trigger("load",null,e._ui(e.anchors[a],e.panels[a]));try{b.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[a],e.panels[a]));try{b.ajaxOptions.error(k,n,a,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},url:function(a, -e){this.anchors.eq(a).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.5"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(a,e){var b=this,c=this.options,h=b._rotate||(b._rotate=function(j){clearTimeout(b.rotation);b.rotation=setTimeout(function(){var k=c.selected;b.select(++k')}function E(a,b){d.extend(a, -b);for(var c in b)if(b[c]==null||b[c]==G)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.5"}});var y=(new Date).getTime();d.extend(L.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]= -f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('
    ')}}, -_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&& -b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f== -""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a, -c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b), -true);this._updateDatepicker(b);this._updateAlternate(b)}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{});b=b&&b.constructor== -Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]); -d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}}, -_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b= -d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false; -for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target|| -a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a); -d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&& -d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=d.datepicker._getBorders(b.dpDiv);b.dpDiv.find("iframe.ui-datepicker-cover").css({left:-i[0],top:-i[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f, -h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a)).find("iframe.ui-datepicker-cover").css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}).end().find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){d(this).removeClass("ui-state-hover"); -this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).addClass("ui-datepicker-prev-hover"); -this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);var e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"); -a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input.focus()},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(), -k=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>k&&k>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1);)a=a[b?"previousSibling":"nextSibling"]; -a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val(): -"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&& -!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth; -b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b= -this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a= -d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a, -"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b== -"object"?b.toString():b+"";if(b=="")return null;for(var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff,f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,k=c=-1,l=-1,u=-1,j=false,o=function(p){(p=z+1 --1){k=1;l=u;do{e=this._getDaysInMonth(c,k-1);if(l<=e)break;k++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,k-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=k||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24* -60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=j+112?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e? -"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),k= -this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),j=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=j&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a, -"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-k,1)),this._getFormatConfig(a));n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+ -n+"";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m,g+k,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+r+"":f?"":''+r+"";k=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&&a.currentDay?u:b;k=!h?k:this.formatDate(k,r,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,r)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;k=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),w=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var M=this._getDefaultDate(a),I="",C=0;C1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='
    '+(/all|left/.test(t)&&C==0?c? -f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,j,o,C>0||D>0,z,v)+'
    ';var A=k?'":"";for(t=0;t<7;t++){var q=(t+h)%7;A+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+""}x+=A+"";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, -A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var O=0;O";var P=!k?"":'";for(t=0;t<7;t++){var F=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,K=B&&!H||!F[0]||j&&qo;P+='";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+=P+""}g++;if(g>11){g=0;m++}x+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(q)+""+(B&&!w?" ":K?''+q.getDate()+ -"":''+q.getDate()+"")+"
    "+(l?""+(i[0]>0&&D==i[1]-1?'
    ':""):"");N+=x}I+=N}I+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': -"");a._keyEvent=false;return I},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var k=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),j='
    ',o="";if(h||!k)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(j+=o+(h||!(k&&l)?" ":""));if(h||!l)j+=''+c+"";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b, -i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(j+='"}j+=this._get(a,"yearSuffix");if(u)j+=(h||!(k&&l)?" ":"")+o;j+="
    ";return j},_adjustInstDate:function(a,b,c){var e= -a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a, -"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a); -c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, -"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= -function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b)); -return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new L;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.5";window["DP_jQuery_"+y]=d})(jQuery); -;/* - * jQuery UI Progressbar 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(b,c){b.widget("ui.progressbar",{options:{value:0},min:0,max:100,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.max,"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); -this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===c)return this._value();this._setOption("value",a);return this},_setOption:function(a,d){if(a==="value"){this.options.value=d;this._refreshValue();this._trigger("change")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.max,Math.max(this.min,a))},_refreshValue:function(){var a=this.value();this.valueDiv.toggleClass("ui-corner-right", -a===this.max).width(a+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.5"})})(jQuery); ;/* * jQuery UI Effects 1.8.5 * diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index cf848b4ced..a292db309c 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -129,7 +129,7 @@ def get_category_items(category, items, db): # {{{ class Endpoint(object): # {{{ 'Manage encoding, mime-type, last modified, cookies, etc.' - def __init__(self, mimetype='text/html', sort_type='category'): + def __init__(self, mimetype='text/html; charset=utf-8', sort_type='category'): self.mimetype = mimetype self.sort_type = sort_type self.sort_kwarg = sort_type + '_sort' @@ -272,7 +272,8 @@ class BrowseServer(object): main = u'''

    {0}

    - {2} + {2} ↑ {1}
    '''.format( From 29b91f532e89f32ec18a88cdf275533d4515d605 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 Oct 2010 21:59:32 -0600 Subject: [PATCH 070/140] Fix average rating calculation for rating datatype in Tag Browser incorrect --- src/calibre/library/database2.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 5bec43ab28..e9b90f37e3 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -10,6 +10,7 @@ import os, sys, shutil, cStringIO, glob, time, functools, traceback, re from itertools import repeat from math import floor from Queue import Queue +from operator import itemgetter from PyQt4.QtGui import QImage @@ -1102,20 +1103,21 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): tooltip = self.custom_column_label_map[label]['name'] datatype = cat['datatype'] + avgr = itemgetter(3) + item_not_zero_func = lambda x: x[2] > 0 if datatype == 'rating': # eliminate the zero ratings line as well as count == 0 item_not_zero_func = (lambda x: x[1] > 0 and x[2] > 0) formatter = (lambda x:u'\u2605'*int(x/2)) + avgr = itemgetter(1) elif category == 'authors': - item_not_zero_func = (lambda x: x[2] > 0) # Clean up the authors strings to human-readable form formatter = (lambda x: x.replace('|', ',')) else: - item_not_zero_func = (lambda x: x[2] > 0) formatter = (lambda x:unicode(x)) categories[category] = [Tag(formatter(r[1]), count=r[2], id=r[0], - avg=r[3], sort=r[4], + avg=avgr(r), sort=r[4], icon=icon, tooltip=tooltip) for r in data if item_not_zero_func(r)] From 31cde51c11a4fcc47657068b7009f5ae9d1da912 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 Oct 2010 23:54:47 -0600 Subject: [PATCH 071/140] Grouping for category views --- resources/content_server/browse/browse.css | 33 +++----- resources/content_server/browse/browse.js | 37 +++++++++ src/calibre/library/server/browse.py | 97 ++++++++++++++++++---- 3 files changed, 130 insertions(+), 37 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 438e9ebb8e..aa3d3be0ef 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -148,19 +148,6 @@ h2.library_name { padding-top: 40px; } -/* }}} */ - -/* Widgets {{{ */ - -#content .ui-widget { font-size: medium; font-family: monospace; } - -#content .ui-button, #content .ui-button-text { - padding: 0.2em 0.5em; - font-family: monospace; - line-height: 1 -} - - /* }}} */ /* Sort select {{{ */ @@ -179,7 +166,6 @@ h2.library_name { /* }}} */ - /* Search bar {{{ */ #search_box { @@ -192,7 +178,6 @@ h2.library_name { /* }}} */ - /* Top level {{{ */ .toplevel ul { list-style-type: none; @@ -220,7 +205,6 @@ h2.library_name { /* }}} */ - /* Category {{{ */ .category ul { list-style-type: none; @@ -247,16 +231,19 @@ h2.library_name { .category li.category-item h4 { display: inline } .category li.category-item span.href { display: none } -/* -.category a.navlink { - text-decoration: none; - color: inherit; +#groups span.load_href { display: none } + +#groups h3 { + font-weight: bold; + margin-top: 1ex; + padding-left: 2em; + padding-top: 0.5ex; + padding-bottom: 0.5ex; } -.category a.navlink:hover { - color: red; +#groups h3 span { + font-weight: normal } -*/ /* }}} */ diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index c2d942efab..692dd5e630 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -86,6 +86,10 @@ function toplevel() { } // }}} +function render_error(msg) { + return '

     Error: '+msg+"

    " +} + // Category feed {{{ function category() { $(".category li").corner("15px"); @@ -96,6 +100,39 @@ function category() { }); $(".category a.navlink").button(); + + $("#groups").accordion({ + collapsible: true, + active: false, + autoHeight: false, + clearStyle: true, + header: "h3", + + change: function(event, ui) { + if (ui.newContent) { + var href = ui.newContent.children("span.load_href").html(); + ui.newContent.children(".loading").show(); + if (href) { + $.ajax({ + url:href, + success: function(data) { + this.children(".loaded").html(data); + this.children(".loaded").show(); + this.children(".loading").hide(); + }, + context: ui.newContent, + dataType: "json", + timeout: 600000, //milliseconds (10 minutes) + error: function(xhr, stat, err) { + this.children(".loaded").html(render_error(stat)); + this.children(".loaded").show(); + this.children(".loading").hide(); + } + }); + } + } + } + }); } // }}} diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index a292db309c..87a6f6a9cf 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -5,7 +5,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import operator, os +import operator, os, json from urllib import quote from binascii import hexlify @@ -13,6 +13,7 @@ import cherrypy from calibre.constants import filesystem_encoding from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml +from calibre.utils.ordered_dict import OrderedDict def paginate(offsets, content, base_url, up_url=None): # {{{ 'Create markup for pagination' @@ -102,7 +103,7 @@ def render_rating(rating, container='span'): # {{{ # }}} -def get_category_items(category, items, db): # {{{ +def get_category_items(category, items, db, datatype): # {{{ def item(i): templ = (u'
  • ' @@ -110,6 +111,8 @@ def get_category_items(category, items, db): # {{{ '{3}
  • ') rating, rstring = render_rating(i.avg_rating) name = xml(i.name) + if datatype == 'rating': + name = xml(_('%d stars')%int(i.avg_rating)) id_ = i.id if id_ is None: id_ = hexlify(force_unicode(name).encode('utf-8')) @@ -164,6 +167,9 @@ class BrowseServer(object): connect('browse', base_href, self.browse_catalog) connect('browse_catalog', base_href+'/category/{category}', self.browse_catalog) + connect('browse_category_group', + base_href+'/category_group/{category}/{group}', + self.browse_category_group) connect('browse_list', base_href+'/list/{query}', self.browse_list) connect('browse_search', base_href+'/search/{query}', self.browse_search) @@ -243,32 +249,66 @@ class BrowseServer(object): return self.browse_template('name').format(title='', script='toplevel();', main=main) - def browse_category(self, category, sort): + def browse_sort_categories(self, items, sort): if sort not in ('rating', 'name', 'popularity'): sort = 'name' + def sorter(x): + ans = getattr(x, 'sort', x.name) + if hasattr(ans, 'upper'): + ans = ans.upper() + return ans + items.sort(key=sorter) + if sort == 'popularity': + items.sort(key=operator.attrgetter('count'), reverse=True) + elif sort == 'rating': + items.sort(key=operator.attrgetter('avg_rating'), reverse=True) + return sort + + def browse_category(self, category, sort): categories = self.categories_cache() category_meta = self.db.field_metadata category_name = category_meta[category]['name'] + datatype = category_meta[category]['datatype'] if category not in categories: raise cherrypy.HTTPError(404, 'category not found') items = categories[category] + sort = self.browse_sort_categories(items, sort) - name_keyg = lambda x: getattr(x, 'sort', x.name).lower() - items.sort(key=name_keyg) - if sort == 'popularity': - items.sort(key=operator.attrgetter('count'), reverse=True) - elif sort == 'rating': - items.sort(key=operator.attrgetter('avg_rating'), reverse=True) + script = 'true' - base_url='/browse/category/'+category - if sort is not None: - base_url += '?sort='+quote(sort) + if len(items) <= self.opts.max_opds_ungrouped_items: + script = 'false' + items = get_category_items(category, items, self.db, datatype) + else: + getter = lambda x: unicode(getattr(x, 'sort', x.name)) + starts = set([]) + for x in items: + val = getter(x) + if not val: + val = u'A' + starts.add(val[0].upper()) + category_groups = OrderedDict() + for x in sorted(starts): + category_groups[x] = len([y for y in items if + getter(y).upper().startswith(x)]) + items = [(u'

    {0} [{2}]

    ' + u'' + u'
    {1}
    ' + u'{3}
    ').format( + xml(s, True), + xml(_('Loading, please wait'))+'…', + unicode(c), + xml(u'/browse/category_group/%s/%s'%(category, s))) + for s, c in category_groups.items()] + items = '\n\n'.join(items) + items = u'
    \n{0}
    '.format(items) - script = 'category();' - items = get_category_items(category, items, self.db) + + script = 'category(%s);'%script + main = u'''

    {0}

    @@ -283,6 +323,35 @@ class BrowseServer(object): return self.browse_template(sort).format(title=category_name, script=script, main=main) + @Endpoint(mimetype='application/json; charset=utf-8') + def browse_category_group(self, category=None, group=None, + category_sort=None): + sort = category_sort + if sort not in ('rating', 'name', 'popularity'): + sort = 'name' + categories = self.categories_cache() + category_meta = self.db.field_metadata + datatype = category_meta[category]['datatype'] + + if category not in categories: + raise cherrypy.HTTPError(404, 'category not found') + if not group: + raise cherrypy.HTTPError(404, 'invalid group') + + items = categories[category] + entries = [] + getter = lambda x: unicode(getattr(x, 'sort', x.name)) + for x in items: + val = getter(x) + if not val: + val = u'A' + if val.upper().startswith(group): + entries.append(x) + + sort = self.browse_sort_categories(entries, sort) + entries = get_category_items(category, entries, self.db, datatype) + return json.dumps(entries, ensure_ascii=False) + @Endpoint() From e216d698783b52da452b15f52929f0bb15ec30b4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 00:14:39 -0600 Subject: [PATCH 072/140] ... --- resources/content_server/browse/browse.css | 2 +- src/calibre/library/server/browse.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index aa3d3be0ef..824c1c5e09 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -236,7 +236,7 @@ h2.library_name { #groups h3 { font-weight: bold; margin-top: 1ex; - padding-left: 2em; + padding-left: 30px; padding-top: 0.5ex; padding-bottom: 0.5ex; } diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 87a6f6a9cf..28fa7b4772 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -295,7 +295,7 @@ class BrowseServer(object): getter(y).upper().startswith(x)]) items = [(u'

    {0} [{2}]

    ' u'' - u'
    {1}
    ' + u'
    {1}{1}
    ' u'{3}
    ').format( xml(s, True), xml(_('Loading, please wait'))+'…', From 6c9541723f2ac9677c4822bd61f2e75a043771bd Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Thu, 14 Oct 2010 16:53:39 +0800 Subject: [PATCH 073/140] [SNBOutput][Bug] Fixed a bug when using multiple SNBFile object, error will happen. [Feature] Get ready for SNB input plugin --- src/calibre/ebooks/snb/snbfile.py | 44 +++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/calibre/ebooks/snb/snbfile.py b/src/calibre/ebooks/snb/snbfile.py index ca10f800c7..34830fa808 100644 --- a/src/calibre/ebooks/snb/snbfile.py +++ b/src/calibre/ebooks/snb/snbfile.py @@ -5,6 +5,7 @@ __copyright__ = '2010, Li Fanxi ' __docformat__ = 'restructuredtext en' import sys, struct, zlib, bz2, os, math +from mimetypes import types_map class FileStream: def IsBinary(self): @@ -18,9 +19,6 @@ class BlockData: class SNBFile: - files = [] - blocks = [] - MAGIC = 'SNBP000B' REV80 = 0x00008000 REVA3 = 0x00A3A3A3 @@ -28,15 +26,21 @@ class SNBFile: REVZ2 = 0x00000000 def __init__(self, inputFile = None): + self.files = [] + self.blocks = [] + if inputFile != None: - self.Parse(inputFile); - - def Parse(self, inputFile): + self.Open(inputFile) + + def Open(self, inputFile): self.fileName = inputFile snbFile = open(self.fileName, "rb") snbFile.seek(0) + self.Parse(snbFile) + snbFile.close() + def Parse(self, snbFile, metaOnly = False): # Read header vmbr = snbFile.read(44) (self.magic, self.rev80, self.revA3, self.revZ1, @@ -47,7 +51,7 @@ class SNBFile: # Read FAT self.vfat = zlib.decompress(snbFile.read(self.vfatCompressed)) self.ParseFile(self.vfat, self.fileCount) - + # Read tail snbFile.seek(-16, os.SEEK_END) #plainStreamEnd = snbFile.tell() @@ -57,7 +61,7 @@ class SNBFile: self.vTailUncompressed = zlib.decompress(snbFile.read(self.tailSize)) self.tailSizeUncompressed = len(self.vTailUncompressed) self.ParseTail(self.vTailUncompressed, self.fileCount) - + # Uncompress file data # Read files binPos = 0 @@ -78,7 +82,7 @@ class SNBFile: try: data = snbFile.read(bSize) uncompressedData += bzdc.decompress(data) - except EOFError, e: + except Exception, e: print e f.fileBody = uncompressedData[plainPos:plainPos+f.fileSize] plainPos += f.fileSize @@ -90,7 +94,6 @@ class SNBFile: else: print f.attr, f.fileName raise Exception("Invalid file") - snbFile.close() def ParseFile(self, vfat, fileCount): fileNames = vfat[fileCount*12:].split('\0'); @@ -156,6 +159,24 @@ class SNBFile: f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() f.fileName = fileName.replace(os.sep, '/') self.files.append(f) + + def GetFileStream(self, fileName): + for file in self.files: + if file.fileName == fileName: + return file.fileBody + return None + + def OutputImageFiles(self, path): + fileNames = [] + for f in self.files: + fname = os.path.basename(f.fileName) + root, ext = os.path.splitext(fname) + if ext in [ '.jpeg', '.jpg', '.gif', '.svg', '.png' ]: + file = open(os.path.join(path, fname), 'wb') + file.write(f.fileBody) + file.close() + fileNames.append((fname, types_map[ext])) + return fileNames def Output(self, outputFile): @@ -247,7 +268,8 @@ class SNBFile: return def Dump(self): - print "File Name:\t", self.fileName + if self.fileName: + print "File Name:\t", self.fileName print "File Count:\t", self.fileCount print "VFAT Size(Compressed):\t%d(%d)" % (self.vfatSize, self.vfatCompressed) print "Binary Stream Size:\t", self.binStreamSize From 83cf5d5f2820b985d72556b835578f2bf25ddac5 Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Thu, 14 Oct 2010 16:59:22 +0800 Subject: [PATCH 074/140] [SNBInput][SNBMetadataReader] Add SNB input plugin and SNB Metadata Reader plugin. --- src/calibre/customize/builtins.py | 13 ++++ src/calibre/ebooks/__init__.py | 2 +- src/calibre/ebooks/metadata/meta.py | 2 +- src/calibre/ebooks/metadata/snb.py | 47 +++++++++++++ src/calibre/ebooks/snb/input.py | 104 ++++++++++++++++++++++++++++ src/calibre/gui2/actions/add.py | 1 + 6 files changed, 167 insertions(+), 2 deletions(-) create mode 100755 src/calibre/ebooks/metadata/snb.py create mode 100755 src/calibre/ebooks/snb/input.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 8550f57ee6..fe187a1400 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -292,6 +292,17 @@ class RTFMetadataReader(MetadataReaderPlugin): def get_metadata(self, stream, ftype): from calibre.ebooks.metadata.rtf import get_metadata return get_metadata(stream) + +class SNBMetadataReader(MetadataReaderPlugin): + + name = 'Read SNB metadata' + file_types = set(['snb']) + description = _('Read metadata from %s files') % 'SNB' + author = 'Li Fanxi' + + def get_metadata(self, stream, ftype): + from calibre.ebooks.metadata.snb import get_metadata + return get_metadata(stream) class TOPAZMetadataReader(MetadataReaderPlugin): @@ -420,6 +431,7 @@ from calibre.ebooks.tcr.input import TCRInput from calibre.ebooks.txt.input import TXTInput from calibre.ebooks.lrf.input import LRFInput from calibre.ebooks.chm.input import CHMInput +from calibre.ebooks.snb.input import SNBInput from calibre.ebooks.epub.output import EPUBOutput from calibre.ebooks.fb2.output import FB2Output @@ -496,6 +508,7 @@ plugins += [ TXTInput, LRFInput, CHMInput, + SNBInput, ] plugins += [ EPUBOutput, diff --git a/src/calibre/ebooks/__init__.py b/src/calibre/ebooks/__init__.py index 624b277e61..9bdf937dd1 100644 --- a/src/calibre/ebooks/__init__.py +++ b/src/calibre/ebooks/__init__.py @@ -25,7 +25,7 @@ class DRMError(ValueError): BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm', 'html', 'xhtml', 'pdf', 'pdb', 'pdr', 'prc', 'mobi', 'azw', 'doc', 'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip', - 'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml', 'mbp', 'tan'] + 'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml', 'mbp', 'tan', 'snb'] class HTMLRenderer(object): diff --git a/src/calibre/ebooks/metadata/meta.py b/src/calibre/ebooks/metadata/meta.py index 87b8d3b535..cbd9db3f04 100644 --- a/src/calibre/ebooks/metadata/meta.py +++ b/src/calibre/ebooks/metadata/meta.py @@ -15,7 +15,7 @@ _METADATA_PRIORITIES = [ 'html', 'htm', 'xhtml', 'xhtm', 'rtf', 'fb2', 'pdf', 'prc', 'odt', 'epub', 'lit', 'lrx', 'lrf', 'mobi', - 'rb', 'imp', 'azw' + 'rb', 'imp', 'azw', 'snb' ] # The priorities for loading metadata from different file types diff --git a/src/calibre/ebooks/metadata/snb.py b/src/calibre/ebooks/metadata/snb.py new file mode 100755 index 0000000000..67bbc89a32 --- /dev/null +++ b/src/calibre/ebooks/metadata/snb.py @@ -0,0 +1,47 @@ +'''Read meta information from SNB files''' + +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2010, Li Fanxi ' + +import re, os +from StringIO import StringIO +from calibre.ebooks.metadata import MetaInformation +from calibre.ebooks.snb.snbfile import SNBFile +from lxml import etree + +def get_metadata(stream, extract_cover=True): + """ Return metadata as a L{MetaInfo} object """ + mi = MetaInformation(_('Unknown'), [_('Unknown')]) + snbFile = SNBFile() + + try: + if not hasattr(stream, 'write'): + snbFile.Parse(StringIO(stream), True) + else: + stream.seek(0) + snbFile.Parse(stream, True) + + meta = snbFile.GetFileStream('snbf/book.snbf') + + if meta != None: + meta = etree.fromstring(meta) + mi.title = meta.find('.//head/name').text + mi.authors = [meta.find('.//head/author').text] + mi.language = meta.find('.//head/language').text.lower().replace('_', '-') + mi.publisher = meta.find('.//head/publisher').text + + if extract_cover: + cover = meta.find('.//head/cover') + if cover != None and cover.text != None: + root, ext = os.path.splitext(cover.text) + if ext == '.jpeg': + ext = '.jpg' + mi.cover_data = (ext[-3:], snbFile.GetFileStream('snbc/images/' + cover.text)) + + except Exception, e: + print e + pass + + return mi diff --git a/src/calibre/ebooks/snb/input.py b/src/calibre/ebooks/snb/input.py new file mode 100755 index 0000000000..a85feddbb2 --- /dev/null +++ b/src/calibre/ebooks/snb/input.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- + +__license__ = 'GPL 3' +__copyright__ = '2010, Li Fanxi ' +__docformat__ = 'restructuredtext en' + +import os, uuid + +from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation +from calibre.ebooks.oeb.base import DirContainer +from calibre.ebooks.snb.snbfile import SNBFile +from calibre.ptempfile import TemporaryDirectory +from calibre.utils.filenames import ascii_filename +from calibre import prepare_string_for_xml +from lxml import etree + +HTML_TEMPLATE = u'%s\n%s\n' + +def html_encode(s): + return s.replace(u'&', u'&').replace(u'<', u'<').replace(u'>', u'>').replace(u'"', u'"').replace(u"'", u''').replace(u'\n', u'
    ').replace(u' ', u' ') + +class SNBInput(InputFormatPlugin): + + name = 'SNB Input' + author = 'Li Fanxi' + description = 'Convert SNB files to OEB' + file_types = set(['snb']) + + options = set([ + ]) + + def convert(self, stream, options, file_ext, log, + accelerators): + log.debug("Parsing SNB file...") + snbFile = SNBFile() + try: + snbFile.Parse(stream) + except: + raise ValueError("Invalid SNB file") + if not snbFile.IsValid(): + log.debug("Invaild SNB file") + raise ValueError("Invalid SNB file") + log.debug("Handle meta data ...") + from calibre.ebooks.conversion.plumber import create_oebbook + oeb = create_oebbook(log, None, options, self, + encoding=options.input_encoding, populate=False) + meta = snbFile.GetFileStream('snbf/book.snbf') + if meta != None: + meta = etree.fromstring(meta) + oeb.metadata.add('title', meta.find('.//head/name').text) + oeb.metadata.add('creator', meta.find('.//head/author').text, attrib={'role':'aut'}) + oeb.metadata.add('language', meta.find('.//head/language').text.lower().replace('_', '-')) + oeb.metadata.add('creator', meta.find('.//head/generator').text) + oeb.metadata.add('publisher', meta.find('.//head/publisher').text) + cover = meta.find('.//head/cover') + if cover != None and cover.text != None: + oeb.guide.add('cover', 'Cover', cover.text) + + bookid = str(uuid.uuid4()) + oeb.metadata.add('identifier', bookid, id='uuid_id', scheme='uuid') + for ident in oeb.metadata.identifier: + if 'id' in ident.attrib: + oeb.uid = oeb.metadata.identifier[0] + break + + with TemporaryDirectory('_chm2oeb', keep=True) as tdir: + log.debug('Process TOC ...') + toc = snbFile.GetFileStream('snbf/toc.snbf') + oeb.container = DirContainer(tdir, log) + if toc != None: + toc = etree.fromstring(toc) + i = 1 + for ch in toc.find('.//body'): + chapterName = ch.text + chapterSrc = ch.get('src') + fname = 'ch_%d.htm' % i + data = snbFile.GetFileStream('snbc/' + chapterSrc) + if data != None: + snbc = etree.fromstring(data) + outputFile = open(os.path.join(tdir, fname), 'wb') + lines = [] + for line in snbc.find('.//body'): + if line.tag == 'text': + lines.append(u'

    %s

    ' % html_encode(line.text)) + elif line.tag == 'img': + lines.append(u'

    ' % html_encode(line.text)) + outputFile.write((HTML_TEMPLATE % (chapterName, u'\n'.join(lines))).encode('utf-8', 'replace')) + outputFile.close() + oeb.toc.add(ch.text, fname) + id, href = oeb.manifest.generate(id='html', + href=ascii_filename(fname)) + item = oeb.manifest.add(id, href, 'text/html') + item.html_input_href = fname + oeb.spine.add(item, True) + i = i + 1 + imageFiles = snbFile.OutputImageFiles(tdir) + for f, m in imageFiles: + id, href = oeb.manifest.generate(id='image', + href=ascii_filename(f)) + item = oeb.manifest.add(id, href, m) + item.html_input_href = f + + return oeb + diff --git a/src/calibre/gui2/actions/add.py b/src/calibre/gui2/actions/add.py index be1f8f4eaf..5bcdf2254e 100644 --- a/src/calibre/gui2/actions/add.py +++ b/src/calibre/gui2/actions/add.py @@ -166,6 +166,7 @@ class AddAction(InterfaceAction): (_('Topaz books'), ['tpz','azw1']), (_('Text books'), ['txt', 'rtf']), (_('PDF Books'), ['pdf']), + (_('SNB Books'), ['snb']), (_('Comics'), ['cbz', 'cbr', 'cbc']), (_('Archives'), ['zip', 'rar']), ] From 8ecad1b0d750673d9b1fe65ac10f56a1067a022f Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 14 Oct 2010 10:21:38 +0100 Subject: [PATCH 075/140] Enhancement #7172: drop on items in user categories. Also fixed a bug where the interface indicated that one could drop on a format, when you can't. --- src/calibre/gui2/tag_view.py | 46 +++++++++++++++++--------------- src/calibre/library/database2.py | 16 ++++++----- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 8732aec63d..a71230c984 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -434,25 +434,24 @@ class TagsModel(QAbstractItemModel): # {{{ action != Qt.CopyAction: return False idx = parent - p = self.parent(idx) - if idx.isValid() and p.isValid(): - item = self.data(p, Qt.UserRole) - fm = self.db.metadata_for_field(item.category_key) - if item.category_key in \ - ('tags', 'series', 'authors', 'rating', 'publisher') or \ - (fm['is_custom'] and \ - fm['datatype'] in ['text', 'rating', 'series']): - child = self.data(idx, Qt.UserRole) - mime = 'application/calibre+from_library' - ids = list(map(int, str(md.data(mime)).split())) - self.handle_drop(item, child, ids) - return True + if idx.isValid(): + node = self.data(idx, Qt.UserRole) + if node.type == TagTreeItem.TAG: + fm = self.db.metadata_for_field(node.tag.category) + if node.tag.category in \ + ('tags', 'series', 'authors', 'rating', 'publisher') or \ + (fm['is_custom'] and \ + fm['datatype'] in ['text', 'rating', 'series']): + mime = 'application/calibre+from_library' + ids = list(map(int, str(md.data(mime)).split())) + self.handle_drop(node, ids) + return True return False - def handle_drop(self, parent, child, ids): - # print 'Dropped ids:', ids, parent.category_key, child.tag.name - key = parent.category_key + def handle_drop(self, on_node, ids): + #print 'Dropped ids:', ids, on_node.tag + key = on_node.tag.category if (key == 'authors' and len(ids) >= 5): if not confirm('

    '+_('Changing the authors for several books can ' 'take a while. Are you sure?') @@ -466,7 +465,7 @@ class TagsModel(QAbstractItemModel): # {{{ fm = self.db.metadata_for_field(key) is_multiple = fm['is_multiple'] - val = child.tag.name + val = on_node.tag.name for id in ids: mi = self.db.get_metadata(id, index_is_id=True) @@ -500,8 +499,6 @@ class TagsModel(QAbstractItemModel): # {{{ self.db.commit() self.drag_drop_finished.emit(ids) - - def set_search_restriction(self, s): self.search_restriction = s @@ -633,8 +630,15 @@ class TagsModel(QAbstractItemModel): # {{{ def flags(self, index, *args): ans = Qt.ItemIsEnabled|Qt.ItemIsSelectable|Qt.ItemIsEditable - if index.isValid() and self.parent(index).isValid(): - ans |= Qt.ItemIsDropEnabled + if index.isValid(): + node = self.data(index, Qt.UserRole) + if node.type == TagTreeItem.TAG: + fm = self.db.metadata_for_field(node.tag.category) + if node.tag.category in \ + ('tags', 'series', 'authors', 'rating', 'publisher') or \ + (fm['is_custom'] and \ + fm['datatype'] in ['text', 'rating', 'series']): + ans |= Qt.ItemIsDropEnabled return ans def supportedDropActions(self): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 5bec43ab28..3c949fcf78 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -68,7 +68,7 @@ copyfile = os.link if hasattr(os, 'link') else shutil.copyfile class Tag(object): def __init__(self, name, id=None, count=0, state=0, avg=0, sort=None, - tooltip=None, icon=None): + tooltip=None, icon=None, category=None): self.name = name self.id = id self.count = count @@ -81,9 +81,11 @@ class Tag(object): tooltip = _('%sAverage rating is %3.1f')%(tooltip, self.avg_rating) self.tooltip = tooltip self.icon = icon + self.category = category def __unicode__(self): - return u'%s:%s:%s:%s:%s'%(self.name, self.count, self.id, self.state, self.tooltip) + return u'%s:%s:%s:%s:%s:%s'%(self.name, self.count, self.id, self.state, + self.category, self.tooltip) def __str__(self): return unicode(self).encode('utf-8') @@ -1115,8 +1117,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): formatter = (lambda x:unicode(x)) categories[category] = [Tag(formatter(r[1]), count=r[2], id=r[0], - avg=r[3], sort=r[4], - icon=icon, tooltip=tooltip) + avg=r[3], sort=r[4], icon=icon, + tooltip=tooltip, category=category) for r in data if item_not_zero_func(r)] # Needed for legacy databases that have multiple ratings that @@ -1148,7 +1150,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): WHERE format="%s"'''%fmt, all=False) if count > 0: - categories['formats'].append(Tag(fmt, count=count, icon=icon)) + categories['formats'].append(Tag(fmt, count=count, icon=icon, + category='formats')) if sort == 'popularity': categories['formats'].sort(key=lambda x: x.count, reverse=True) @@ -1194,7 +1197,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if icon_map and 'search' in icon_map: icon = icon_map['search'] for srch in saved_searches().names(): - items.append(Tag(srch, tooltip=saved_searches().lookup(srch), icon=icon)) + items.append(Tag(srch, tooltip=saved_searches().lookup(srch), + icon=icon, category='search')) if len(items): if icon_map is not None: icon_map['search'] = icon_map['search'] From 082ffba6cf575a16279e90c6fb242beae073d468 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 14 Oct 2010 10:37:24 +0100 Subject: [PATCH 076/140] Restore average rating display in the icon. --- src/calibre/gui2/tag_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index a71230c984..36d78838d3 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -29,7 +29,7 @@ class TagDelegate(QItemDelegate): # {{{ def paint(self, painter, option, index): item = index.internalPointer() - if True or item.type != TagTreeItem.TAG: + if item.type != TagTreeItem.TAG: QItemDelegate.paint(self, painter, option, index) return r = option.rect From 8d26343231ae58962b8b80756f427827b58f8b4d Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Thu, 14 Oct 2010 20:57:47 +0800 Subject: [PATCH 077/140] [SNBOutput] Also convert png image to jpg. --- src/calibre/ebooks/snb/snbml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/snb/snbml.py b/src/calibre/ebooks/snb/snbml.py index af0106aaa0..1847e05a4b 100644 --- a/src/calibre/ebooks/snb/snbml.py +++ b/src/calibre/ebooks/snb/snbml.py @@ -25,7 +25,7 @@ def ProcessFileName(fileName): fileName = fileName.lower() # Change all images to jpg root, ext = os.path.splitext(fileName) - if ext in [ '.jpeg', '.jpg', '.gif', '.svg' ]: + if ext in [ '.jpeg', '.jpg', '.gif', '.svg', '.png' ]: fileName = root + '.jpg' return fileName From 565295b3531b9e6f1251c9ad9d352b6f2812003b Mon Sep 17 00:00:00 2001 From: Li Fanxi Date: Thu, 14 Oct 2010 21:19:08 +0800 Subject: [PATCH 078/140] [SNBOutput] Improve handling of comics, read screen size from profile. --- src/calibre/customize/profiles.py | 1 + src/calibre/ebooks/snb/output.py | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/calibre/customize/profiles.py b/src/calibre/customize/profiles.py index bda2103484..4fa53b1cdb 100644 --- a/src/calibre/customize/profiles.py +++ b/src/calibre/customize/profiles.py @@ -655,6 +655,7 @@ class BambookOutput(OutputProfile): # Screen size is a best guess screen_size = (800, 600) + comic_screen_size = (700, 540) dpi = 168.451 fbase = 12 fsizes = [10, 12, 14, 16] diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index cbe785d384..3aadb79185 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -50,6 +50,7 @@ class SNBOutput(OutputFormatPlugin): ]) def convert(self, oeb_book, output_path, input_plugin, opts, log): + self.opts = opts # Create temp dir with TemporaryDirectory('_snb_output') as tdir: # Create stub directories @@ -224,9 +225,12 @@ class SNBOutput(OutputFormatPlugin): img = Image() img.load(imageData) (x,y) = img.size - # TODO use the data from device profile - SCREEN_X = 540 - SCREEN_Y = 700 + if self.opts: + SCREEN_Y, SCREEN_X = self.opts.output_profile.comic_screen_size + print SCREEN_Y, SCREEN_X + else: + SCREEN_X = 540 + SCREEN_Y = 700 # Handle big image only if x > SCREEN_X or y > SCREEN_Y: SCREEN_RATIO = float(SCREEN_Y) / SCREEN_X From 05cda6b7f95dcecd832827f8c6c661f8db23e8de Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 08:24:47 -0600 Subject: [PATCH 079/140] ... --- src/calibre/gui2/viewer/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index a1d3b9bea4..f0f29a67e6 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -731,8 +731,8 @@ def main(args=sys.argv): main.raise_() if opts.full_screen: main.action_full_screen.trigger() - with main: - return app.exec_() + with main: + return app.exec_() return 0 if __name__ == '__main__': From 5523da3dde513d82e6a1dbd1fd791d8671f6febe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 08:36:10 -0600 Subject: [PATCH 080/140] Auto expand when dropping onto Tag Browser. Only seems to work for Searches node and I dont have the time to figure out why --- src/calibre/gui2/tag_view.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 36d78838d3..3505cc7344 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -84,6 +84,7 @@ class TagsView(QTreeView): # {{{ self.setAcceptDrops(True) self.setDragDropMode(self.DropOnly) self.setDropIndicatorShown(True) + self.setAutoExpandDelay(500) def set_database(self, db, tag_match, sort_by): self.hidden_categories = config['tag_browser_hidden_categories'] From f468fd5cf3a7feffbbb86f5363761d700591e0b4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 09:08:42 -0600 Subject: [PATCH 081/140] Try using a table to layout category view --- resources/content_server/browse/browse.css | 15 +++++++-------- resources/content_server/browse/browse.js | 6 ++---- src/calibre/library/server/browse.py | 9 +++++---- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 824c1c5e09..7af4aeb5f4 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -206,30 +206,29 @@ h2.library_name { /* }}} */ /* Category {{{ */ -.category ul { - list-style-type: none; +.category table { margin: 0; padding: 0; + width: 100%; + border-collapse: collapse; } -.category li.category-item { - margin: 0.75em; +.category tr.category-item td { padding: 0.75em; text-align: center; cursor: pointer; } -.category li.category-item:hover { +.category tr.category-item:hover { background-color: #d6d3c9; - font-weight: bold; -moz-box-shadow: 5px 5px 5px #ccc; -webkit-box-shadow: 5px 5px 5px #ccc; box-shadow: 5px 5px 5px #ccc; } -.category li.category-item h4 { display: inline } -.category li.category-item span.href { display: none } +.category tr.category-item h4 { display: inline } +.category tr.category-item span.href { display: none } #groups span.load_href { display: none } diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 692dd5e630..a841fb5e5d 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -92,10 +92,8 @@ function render_error(msg) { // Category feed {{{ function category() { - $(".category li").corner("15px"); - - $(".category li").click(function() { - var href = $(this).children("span.href").html(); + $(".category tr").click(function() { + var href = $(this).find("span.href").html(); window.location = href; }); diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 28fa7b4772..4d860b2d0d 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -106,9 +106,10 @@ def render_rating(rating, container='span'): # {{{ def get_category_items(category, items, db, datatype): # {{{ def item(i): - templ = (u'

  • ' - '

    {0}  {1}

      {2}' - '{3}
  • ') + templ = (u'' + '

    {0}

    {1}' + '{2}' + '{3}') rating, rstring = render_rating(i.avg_rating) name = xml(i.name) if datatype == 'rating': @@ -125,7 +126,7 @@ def get_category_items(category, items, db, datatype): # {{{ xml(desc), xml(quote(href)), rstring) items = list(map(item, items)) - return '\n'.join(['
      '] + items + ['
    ']) + return '\n'.join([''] + items + ['
    ']) # }}} From dbcf47468bd88bed0938596f90ed95bfca4ab5ac Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 09:38:52 -0600 Subject: [PATCH 082/140] More semantic table markup. --- resources/content_server/browse/browse.css | 25 +++++++++++++++------- resources/content_server/browse/browse.js | 4 +--- src/calibre/library/server/browse.py | 10 ++++----- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 7af4aeb5f4..0222aefdb4 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -206,29 +206,38 @@ h2.library_name { /* }}} */ /* Category {{{ */ -.category table { - margin: 0; - padding: 0; +.category > div.category-container { width: 100%; - border-collapse: collapse; + margin-top: 1ex; + margin-bottom: 1ex; + display: table; } -.category tr.category-item td { +.category div.category-item { + display: table-row; +} + +.category div.category-item > div { padding: 0.75em; text-align: center; cursor: pointer; + display: table-cell; } -.category tr.category-item:hover { +.category div.category-name { font-weight: bold } + +.category div.category-item:hover > div { background-color: #d6d3c9; -moz-box-shadow: 5px 5px 5px #ccc; -webkit-box-shadow: 5px 5px 5px #ccc; box-shadow: 5px 5px 5px #ccc; + border-radius: 15px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; } -.category tr.category-item h4 { display: inline } -.category tr.category-item span.href { display: none } +.category div.category-item span.href { display: none } #groups span.load_href { display: none } diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index a841fb5e5d..f3f278fc48 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -77,8 +77,6 @@ function init() { function toplevel() { $(".sort_select").hide(); - $(".toplevel li").corner("15px"); - $(".toplevel li").click(function() { var href = $(this).children("span").html(); window.location = href; @@ -92,7 +90,7 @@ function render_error(msg) { // Category feed {{{ function category() { - $(".category tr").click(function() { + $(".category .category-item").click(function() { var href = $(this).find("span.href").html(); window.location = href; }); diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 4d860b2d0d..6a557e423a 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -106,10 +106,10 @@ def render_rating(rating, container='span'): # {{{ def get_category_items(category, items, db, datatype): # {{{ def item(i): - templ = (u'' - '

    {0}

    {1}' - '{2}' - '{3}') + templ = (u'
    ' + '
    {0}
    {1}
    ' + '
    {2}' + '{3}
    ') rating, rstring = render_rating(i.avg_rating) name = xml(i.name) if datatype == 'rating': @@ -126,7 +126,7 @@ def get_category_items(category, items, db, datatype): # {{{ xml(desc), xml(quote(href)), rstring) items = list(map(item, items)) - return '\n'.join([''] + items + ['
    ']) + return '\n'.join(['
    '] + items + ['
    ']) # }}} From 6f2742500d22445132dcdecb2f8663d538fd9029 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 09:42:59 -0600 Subject: [PATCH 083/140] ... --- resources/content_server/browse/browse.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 0222aefdb4..07cf22f1cc 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -190,6 +190,10 @@ h2.library_name { padding: 0.75em; text-align: center; cursor: pointer; + border-radius: 15px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + } .toplevel li:hover { From 8f82dc37138053327bb3244c362328e49d0c6cf0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 15:56:38 -0600 Subject: [PATCH 084/140] Orsai by DM. Fixes #7176 (New recipe for famous blog in Spanish - Orsai) --- resources/images/news/orsai.png | Bin 0 -> 684 bytes resources/recipes/orsai.recipe | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 resources/images/news/orsai.png create mode 100644 resources/recipes/orsai.recipe diff --git a/resources/images/news/orsai.png b/resources/images/news/orsai.png new file mode 100644 index 0000000000000000000000000000000000000000..e65f02206c19fed25f1632ff43531bd8609df8e6 GIT binary patch literal 684 zcmV;d0#p5oP)=e}8EFYAp^0E59e7=nZX1&9IxDN=+c zX{hKBk?4|$f|8DObkr27k}8UnKY%U>2^tIW(NKVGA+XrUVEncF-kcQkO!A6(Q`|Ya z_nhCAvyCX90cA{pLCQb?8FE16wk|+5kO-7%0NbDp4n%ou0+X=?LHQF!!xb13Kn0+N zxH*rzePTEP-2uOTKgX4?`u{bj1_+Fhgo|9ONW#5o4xBv7zT;Em-E&<1eSyCgmrG~c zH5QTq5RfQ$B%o1ubG z>WtCx7?W;zI2f|OT12Vwf*L3@rOKFgHnj+EhZTlwL|x!> zA|!(nI9QlB|J>VFFGox5C+8gn;BA&=+n-u;F+~4gYd~(w%DHSZ)F_K4D*gqvYY`M! Sit~H`0000 Date: Thu, 14 Oct 2010 15:59:54 -0600 Subject: [PATCH 085/140] ... --- resources/content_server/browse/browse.css | 11 ++ src/calibre/library/server/browse.py | 164 ++++++++++++--------- 2 files changed, 109 insertions(+), 66 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 07cf22f1cc..d66ae744cf 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -259,4 +259,15 @@ h2.library_name { /* }}} */ +/* Booklist {{{ */ + +#booklist .page { + display: none; +} + +.loading img { + vertical-align: middle; +} + +/* }}} */ diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 6a557e423a..ffbd958688 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en' import operator, os, json from urllib import quote -from binascii import hexlify +from binascii import hexlify, unhexlify import cherrypy @@ -15,64 +15,33 @@ from calibre.constants import filesystem_encoding from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml from calibre.utils.ordered_dict import OrderedDict -def paginate(offsets, content, base_url, up_url=None): # {{{ - 'Create markup for pagination' - - if '?' not in base_url: - base_url += '?' - - if base_url[-1] != '?': - base_url += '&' - - def navlink(decoration, name, cls, offset): - label = xml(name) - if cls in ('next', 'last'): - label += ' ' + decoration - else: - label = decoration + ' ' + label - return (u'' - u'{label}').format(cls=cls, decoration=decoration, - name=xml(name, True), offset=offset, - base_url=xml(base_url, True), label=label) - left = '' - if offsets.offset > 0 and offsets.previous_offset > 0: - left += navlink(u'\u219e', _('First'), 'first', 0) - if offsets.offset > 0: - left += ' ' + navlink('←', _('Previous'), 'previous', - offsets.previous_offset) - - middle = '' - if up_url: - middle = '[{1} ↑]'.format(xml(up_url, True), - xml(_('Up'))) - - right = '' - if offsets.next_offset > -1: - right += navlink('&rarr', _('Next'), 'next', offsets.next_offset) - if offsets.last_offset > offsets.next_offset and offsets.last_offset > 0: - right += ' ' + navlink(u'\u21A0', _('Last'), 'last', offsets.last_offset) - - navbar = u''' - - - - - - - - '''.format(left=left, right=right, middle=middle) - - templ = u''' -
    - {navbar} -
    - {content} +def render_book_list(ids): + pages = [] + while ids: + page = list(ids[:25]) + pages.append(page) + ids = ids[25:] + page_template = u'''\ +
    +
    +
    {2}
    +
    - {navbar} -
    - ''' - return templ.format(navbar=navbar, content=content) -# }}} + ''' + rpages = [] + for i, pg in enumerate(pages): + ld = xml(json.dumps(pg), True) + rpages.append(page_template.format(i, ld, + xml(_('Loading, please wait')) + '…')) + rpages = u'\n\n'.join(rpages) + + templ = u'''\ +

    {0}

    +
    + {pages} +
    + ''' + return templ.format(_('Browsing %d books')%len(ids), pages=rpages) def utf8(x): # {{{ if isinstance(x, unicode): @@ -171,10 +140,15 @@ class BrowseServer(object): connect('browse_category_group', base_href+'/category_group/{category}/{group}', self.browse_category_group) - connect('browse_list', base_href+'/list/{query}', self.browse_list) + connect('browse_matches', + base_href+'/matches/{category}/{cid}', + self.browse_matches) + connect('browse_booklist_page', + base_href+'/booklist_page', + self.browse_booklist_page) + connect('browse_search', base_href+'/search/{query}', self.browse_search) - connect('browse_book', base_href+'/book/{uuid}', self.browse_book) def browse_template(self, sort, category=True): @@ -267,12 +241,12 @@ class BrowseServer(object): def browse_category(self, category, sort): categories = self.categories_cache() + if category not in categories: + raise cherrypy.HTTPError(404, 'category not found') category_meta = self.db.field_metadata category_name = category_meta[category]['name'] datatype = category_meta[category]['datatype'] - if category not in categories: - raise cherrypy.HTTPError(404, 'category not found') items = categories[category] sort = self.browse_sort_categories(items, sort) @@ -331,11 +305,12 @@ class BrowseServer(object): if sort not in ('rating', 'name', 'popularity'): sort = 'name' categories = self.categories_cache() + if category not in categories: + raise cherrypy.HTTPError(404, 'category not found') + category_meta = self.db.field_metadata datatype = category_meta[category]['datatype'] - if category not in categories: - raise cherrypy.HTTPError(404, 'category not found') if not group: raise cherrypy.HTTPError(404, 'invalid group') @@ -360,6 +335,8 @@ class BrowseServer(object): 'Entry point for top-level, categories and sub-categories' if category == None: ans = self.browse_toplevel() + elif category == 'newest': + raise cherrypy.InternalRedirect('/browse/matches/newest/dummy') else: ans = self.browse_category(category, category_sort) @@ -368,8 +345,63 @@ class BrowseServer(object): # }}} # Book Lists {{{ - def browse_list(self, query=None, offset=0, sort=None): - raise NotImplementedError() + + def browse_sort_book_list(self, items, sort): + fm = self.db.field_metadata + keys = frozenset(fm.sortable_field_keys()) + if sort not in keys: + sort = 'title' + self.sort(items, 'title', True) + if sort != 'title': + ascending = fm[sort]['datatype'] not in ('rating', 'datetime') + self.sort(items, sort, ascending) + return sort + + @Endpoint(sort_type='list') + def browse_matches(self, category=None, cid=None, list_sort=None): + if not cid: + raise cherrypy.HTTPError(404, 'invalid category id: %r'%cid) + categories = self.categories_cache() + + if category not in categories and category != 'newest': + raise cherrypy.HTTPError(404, 'category not found') + try: + category_name = self.db.field_metadata[category]['name'] + except: + if category != 'newest': + raise + category_name = _('Newest') + + if category == 'search': + which = unhexlify(cid) + try: + ids = self.search_cache('search:"%s"'%which) + except: + raise cherrypy.HTTPError(404, 'Search: %r not understood'%which) + elif category == 'newest': + ids = list(self.db.data.iterallids()) + else: + ids = self.db.get_books_for_category(category, cid) + + items = [self.db.data._data[x] for x in ids] + if category == 'newest': + list_sort = 'timestamp' + sort = self.browse_sort_book_list(items, list_sort) + ids = [x[0] for x in items] + html = render_book_list(ids) + return self.browse_template(sort).format( + title=_('Books in') + " " +category_name, + script='booklist();', main=html) + + @Endpoint(mimetype='application/json; charset=utf-8', sort_type='list') + def browse_booklist_page(self, ids=None, list_sort=None): + if ids is None: + ids = json.dumps('[]') + try: + ids = json.loads(ids) + except: + raise cherrypy.HTTPError(404, 'invalid ids') + # }}} # Search {{{ From d5462c8d0063c844f5e8370a17e8da382d80dd37 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 17:39:00 -0600 Subject: [PATCH 086/140] Update Globe and Mail. Fix #405 (New news feed) --- resources/recipes/globe_and_mail.recipe | 31 ++++---------------- resources/recipes/volksrant.recipe | 39 ++++++++++++++----------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/resources/recipes/globe_and_mail.recipe b/resources/recipes/globe_and_mail.recipe index b2a9915250..b6e6b5c25b 100644 --- a/resources/recipes/globe_and_mail.recipe +++ b/resources/recipes/globe_and_mail.recipe @@ -26,31 +26,12 @@ class GlobeAndMail(BasicNewsRecipe): #credit {margin-top:0px;} .tag {font-size: 22pt;}''' description = 'Canada\'s national newspaper' - remove_tags_before = dict(id="article-top") - remove_tags = [ - {'id':['util', 'article-tabs', 'comments', 'article-relations', - 'gallery-controls', 'video', 'galleryLoading','deck','header', - 'toolsBottom'] }, - {'class':['credit','inline-img-caption','tab-pointer'] }, - dict(name='div', attrs={'id':['lead-photo', 'most-popular-story']}), - dict(name='div', attrs={'class':'right'}), - dict(name='div', attrs={'id':'footer'}), - dict(name='div', attrs={'id':'beta-msg'}), - dict(name='img', attrs={'class':'headshot'}), - dict(name='div', attrs={'class':'brand'}), - dict(name='div', attrs={'id':'nav-wrap'}), - dict(name='div', attrs={'id':'featureTopics'}), - dict(name='div', attrs={'id':'videoNav'}), - dict(name='div', attrs={'id':'blog-header'}), - dict(name='div', attrs={'id':'right-rail'}), - dict(name='div', attrs={'id':'group-footer-container'}), - dict(name=['iframe', 'style']) - ] - remove_attributes = ['style'] - remove_tags_after = [{'id':['article-content']}, - {'class':['pull','inline-img'] }, - dict(name='img', attrs={'class':'inline-media-embed'}), - ] + keep_only_tags = [dict(name='article')] + remove_tags = [dict(name='aside'), + dict(name='footer'), + dict(name='div', attrs={'class':(lambda x: isinstance(x, (str,unicode)) and 'articlecommentcountholder' in x.split(' '))}), + dict(name='ul', attrs={'class':(lambda x: isinstance(x, (str,unicode)) and 'articletoolbar' in x.split(' '))}), + ] feeds = [ (u'Latest headlines', u'http://www.theglobeandmail.com/?service=rss'), (u'Top stories', u'http://www.theglobeandmail.com/?service=rss&feed=topstories'), diff --git a/resources/recipes/volksrant.recipe b/resources/recipes/volksrant.recipe index dcc8c042ee..6f3ec4ce0d 100644 --- a/resources/recipes/volksrant.recipe +++ b/resources/recipes/volksrant.recipe @@ -11,6 +11,7 @@ __docformat__ = 'restructuredtext en' on 10/10/10 to include function to grab print version of articles ''' +from datetime import date from calibre.web.feeds.news import BasicNewsRecipe ''' added by Tony Stegall @@ -27,7 +28,6 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe): no_stylesheets = True language = 'nl' - extra_css = ''' body{font-family:Arial,Helvetica,sans-serif; font-size:small;} h1{font-size:large;} @@ -43,14 +43,16 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe): def get_obfuscated_article(self, url): br = self.get_browser() + print 'THE CURRENT URL IS: ', url br.open(url) + year = date.today().year try: - response = br.follow_link(url_regex='.*?(2010)(\\/)(article)(\\/)(print)(\\/)', nr = 0) - html = response.read() + response = br.follow_link(url_regex='.*?(%d)(\\/)(article)(\\/)(print)(\\/)'%year, nr = 0) + html = response.read() except: - response = br.open(url) - html = response.read() + response = br.open(url) + html = response.read() self.temp_files.append(PersistentTemporaryFile('_fa.html')) self.temp_files[-1].write(html) @@ -59,19 +61,22 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe): ############################################################################################################### - feeds = [ - (u'Laatste Nieuws', u'http://volkskrant.nl/rss/laatstenieuws.rss'), - (u'Binnenlands nieuws', u'http://volkskrant.nl/rss/nederland.rss'), - (u'Buitenlands nieuws', u'http://volkskrant.nl/rss/internationaal.rss'), - (u'Economisch nieuws', u'http://volkskrant.nl/rss/economie.rss'), - (u'Sportnieuws', u'http://volkskrant.nl/rss/sport.rss'), - (u'Kunstnieuws', u'http://volkskrant.nl/rss/kunst.rss'), + ''' + Change Log: + Date: 10/15/2010 + Feeds updated by Martin Tarenskeen + ''' + + feeds = [ + (u'Laatste Nieuws', u'http://www.volkskrant.nl/rss/laatstenieuws.rss'), + (u'Binnenland', u'http://www.volkskrant.nl/rss/nederland.rss'), + (u'Buitenland', u'http://www.volkskrant.nl/rss/internationaal.rss'), + (u'Economie', u'http://www.volkskrant.nl/rss/economie.rss'), + (u'Sport', u'http://www.volkskrant.nl/rss/sport.rss'), + (u'Cultuur', u'http://www.volkskrant.nl/rss/kunst.rss'), + (u'Gezondheid & Wetenschap', u'http://www.volkskrant.nl/rss/wetenschap.rss'), + (u'Internet & Media', u'http://www.volkskrant.nl/rss/media.rss') ] - #both of these rss feeds link back to the main volksrant.nl url a.k.a Broken - #If someone happens to know the correct paths then they can put them in here - #(u'Wetenschapsnieuws', u'http://feeds.feedburner.com/DeVolkskrantWetenschap'), - #(u'Technologienieuws', u'http://feeds.feedburner.com/vkmedia') - ] ''' example for formating From 4fc74bdcd0e6403b8d46884a42d6767b0488be3d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 19:41:55 -0600 Subject: [PATCH 087/140] Content server: Allow variable sized thumbnails and replace use of PIL with ImageMagick --- src/calibre/library/server/content.py | 66 ++++++++++++++------------- src/calibre/utils/magick/draw.py | 8 +++- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py index 59fed03fbd..8c5fef4ee1 100644 --- a/src/calibre/library/server/content.py +++ b/src/calibre/library/server/content.py @@ -5,18 +5,15 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import re, os, cStringIO +import re, os import cherrypy -try: - from PIL import Image as PILImage - PILImage -except ImportError: - import Image as PILImage from calibre import fit_image, guess_type from calibre.utils.date import fromtimestamp from calibre.library.caches import SortKeyGenerator +from calibre.utils.magick.draw import save_cover_data_to, Image, \ + thumbnail as generate_thumbnail class CSSortKeyGenerator(SortKeyGenerator): @@ -77,8 +74,13 @@ class ContentServer(object): id = int(match.group()) if not self.db.has_id(id): raise cherrypy.HTTPError(400, 'id:%d does not exist in database'%id) - if what == 'thumb': - return self.get_cover(id, thumbnail=True) + if what == 'thumb' or what.startswith('thumb_'): + try: + width, height = map(int, what.split('_')[1:]) + except: + width, height = 60, 80 + return self.get_cover(id, thumbnail=True, thumb_width=width, + thumb_height=height) if what == 'cover': return self.get_cover(id) return self.get_format(id, what) @@ -128,37 +130,39 @@ class ContentServer(object): return self.static('index.html') # Actually get content from the database {{{ - def get_cover(self, id, thumbnail=False): - cover = self.db.cover(id, index_is_id=True, as_file=False) - if cover is None: - cover = self.default_cover - cherrypy.response.headers['Content-Type'] = 'image/jpeg' - cherrypy.response.timeout = 3600 - path = getattr(cover, 'name', False) - updated = fromtimestamp(os.stat(path).st_mtime) if path and \ - os.access(path, os.R_OK) else self.build_time - cherrypy.response.headers['Last-Modified'] = self.last_modified(updated) + def get_cover(self, id, thumbnail=False, thumb_width=60, thumb_height=80): try: - f = cStringIO.StringIO(cover) - try: - im = PILImage.open(f) - except IOError: - raise cherrypy.HTTPError(404, 'No valid cover found') - width, height = im.size + cherrypy.response.headers['Content-Type'] = 'image/jpeg' + cherrypy.response.timeout = 3600 + cover = self.db.cover(id, index_is_id=True, as_file=True) + if cover is None: + cover = self.default_cover + updated = self.build_time + else: + with cover as f: + updated = fromtimestamp(os.stat(f.name).st_mtime) + cover = f.read() + cherrypy.response.headers['Last-Modified'] = self.last_modified(updated) + + if thumbnail: + return generate_thumbnail(cover, + width=thumb_width, height=thumb_height)[-1] + + img = Image() + img.load(cover) + width, height = img.size scaled, width, height = fit_image(width, height, - 60 if thumbnail else self.max_cover_width, - 80 if thumbnail else self.max_cover_height) + thumb_width if thumbnail else self.max_cover_width, + thumb_height if thumbnail else self.max_cover_height) if not scaled: return cover - im = im.resize((int(width), int(height)), PILImage.ANTIALIAS) - of = cStringIO.StringIO() - im.convert('RGB').save(of, 'JPEG') - return of.getvalue() + return save_cover_data_to(img, 'img.jpg', return_data=True, + resize_to=(width, height)) except Exception, err: import traceback cherrypy.log.error('Failed to generate cover:') cherrypy.log.error(traceback.print_exc()) - raise cherrypy.HTTPError(404, 'Failed to generate cover: %s'%err) + raise cherrypy.HTTPError(404, 'Failed to generate cover: %r'%err) def get_format(self, id, format): format = format.upper() diff --git a/src/calibre/utils/magick/draw.py b/src/calibre/utils/magick/draw.py index 6808215554..5c978a27e0 100644 --- a/src/calibre/utils/magick/draw.py +++ b/src/calibre/utils/magick/draw.py @@ -25,6 +25,7 @@ def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None, resize and the input and output image formats are the same, no changes are made. + :param data: Image data as bytestring or Image object :param compression_quality: The quality of the image after compression. Number between 1 and 100. 1 means highest compression, 100 means no compression (lossless). @@ -33,8 +34,11 @@ def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None, ''' changed = False - img = Image() - img.load(data) + if isinstance(data, Image): + img = data + else: + img = Image() + img.load(data) orig_fmt = normalize_format_name(img.format) fmt = os.path.splitext(path)[1] fmt = normalize_format_name(fmt[1:]) From 217eedf8feaa443819b3467fc90acedc6566ca04 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2010 19:42:12 -0600 Subject: [PATCH 088/140] ... --- src/calibre/manual/faq.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 51660d2620..359cc4755f 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -387,6 +387,12 @@ solve it, look for a corrupted font file on your system, in ~/Library/Fonts or t check for corrupted fonts in OS X is to start the "Font Book" application, select all fonts and then in the File menu, choose "Validate fonts". + +I downloaded the installer, but it is not working? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Downloading from the internet can sometimes result in a corrupted download. If the |app| installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from `an alternate location `_. If the installer still doesn't work, then something on your computer is preventing it from running. Best place to ask for more help is in the `forums `_. + My antivirus program claims |app| is a virus/trojan? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 0a4da453beca720a47f5d93da7a0e47b8f1e8dad Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 09:46:42 -0600 Subject: [PATCH 089/140] ... --- src/calibre/manual/news.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/manual/news.rst b/src/calibre/manual/news.rst index de50fd1c19..88b6dd47bc 100644 --- a/src/calibre/manual/news.rst +++ b/src/calibre/manual/news.rst @@ -295,6 +295,9 @@ To learn more about writing advanced recipes using some of the facilities, avail `Built-in recipes `_ The source code for the built-in recipes that come with |app| + `The calibre recipes forum `_ + Lots of knowledgeable |app| recipe writers hang out here. + API documentation -------------------- From 1f0d61c630af6888841e83cfc3439dc816d69b94 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 11:41:50 -0600 Subject: [PATCH 090/140] Basic book list in the new content server --- resources/content_server/browse/browse.css | 57 ++++++++++++++ resources/content_server/browse/browse.js | 53 ++++++++++++- resources/content_server/browse/summary.html | 14 ++++ src/calibre/library/comments.py | 2 + src/calibre/library/server/browse.py | 78 +++++++++++++++++--- 5 files changed, 188 insertions(+), 16 deletions(-) create mode 100644 resources/content_server/browse/summary.html diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index d66ae744cf..d2e8106ba0 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -265,9 +265,66 @@ h2.library_name { display: none; } +#booklist .load_data { display: none } + .loading img { vertical-align: middle; } +#booklist .summary { + margin-bottom: 2ex; + border-bottom: solid 1px black; +} + +#booklist div.left { + float: left; + height: 190px; + vertical-align: middle; + text-align: center; + margin-left: 1em; + margin-right: 2em; +} + +#booklist div.left img { + display: block; + margin-bottom: 1ex; +} + +#booklist div.right { + height: 190px; + overflow: auto; + margin-left: 1em; + margin-right: 1em; +} + +#booklist div.right .stars { + float:right; + margin-right: 1em; +} + +#booklist div.right .stars .rating_container { + display: block; +} + +#booklist .title { + font-size: larger; +} + +#booklist .formats a { + text-decoration: none; +} + +#booklist .formats a:hover { + color: red; +} + +#booklist .left .ui-button-text { + font-weight: bold; + padding-left: 0.25em; + padding-right: 0.25em; + padding-top: 0.25em; + padding-bottom: 0.25em; +} + /* }}} */ diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index f3f278fc48..5f2af3299e 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -89,11 +89,14 @@ function render_error(msg) { } // Category feed {{{ + +function category_clicked() { + var href = $(this).find("span.href").html(); + window.location = href; +} + function category() { - $(".category .category-item").click(function() { - var href = $(this).find("span.href").html(); - window.location = href; - }); + $(".category .category-item").click(category_clicked); $(".category a.navlink").button(); @@ -115,6 +118,7 @@ function category() { this.children(".loaded").html(data); this.children(".loaded").show(); this.children(".loading").hide(); + this.find('.category-item').click(category_clicked); }, context: ui.newContent, dataType: "json", @@ -132,4 +136,45 @@ function category() { } // }}} +// Booklist {{{ +function load_page(elem) { + var ids = elem.find(".load_data").attr('title'); + var href = elem.find(".load_data").html(); + $.ajax({ + url: href, + context: elem, + dataType: "json", + type: 'POST', + timeout: 600000, //milliseconds (10 minutes) + data: {'ids': ids}, + error: function(xhr, stat, err) { + this.children(".loaded").html(render_error(stat)); + this.children(".loaded").show(); + this.children(".loading").hide(); + }, + success: function(data) { + this.children(".loaded").html(data); + this.children(".loaded").show(); + this.children(".loading").hide(); + this.find(".left a.read").button(); + } + }); + $("#booklist .page").hide(); + elem.children(".loaded").hide(); + elem.children(".loading").show(); + elem.show(); +} + +function booklist() { + var test = $("#booklist #page0").html(); + if (!test) { + $("#booklist").html(render_error("No books found")); + return; + } + + load_page($("#booklist #page0")); + +} + +// }}} diff --git a/resources/content_server/browse/summary.html b/resources/content_server/browse/summary.html new file mode 100644 index 0000000000..0caf710a43 --- /dev/null +++ b/resources/content_server/browse/summary.html @@ -0,0 +1,14 @@ +
    + +
    +
    {stars}{series}
    +
    {title}
    +
    {authors}
    +
    {comments}
    +
    {tags}
    +
    {other_formats}
    +
    +
    diff --git a/src/calibre/library/comments.py b/src/calibre/library/comments.py index 32ae65b31e..670d9f2564 100644 --- a/src/calibre/library/comments.py +++ b/src/calibre/library/comments.py @@ -42,6 +42,8 @@ def comments_to_html(comments): Deprecated HTML returns as HTML via BeautifulSoup() ''' + if not comments: + return u'

    ' if not isinstance(comments, unicode): comments = comments.decode(preferred_encoding, 'replace') diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index ffbd958688..e1415decdb 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -14,16 +14,20 @@ import cherrypy from calibre.constants import filesystem_encoding from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml from calibre.utils.ordered_dict import OrderedDict +from calibre.utils.filenames import ascii_filename +from calibre.utils.config import prefs +from calibre.library.comments import comments_to_html def render_book_list(ids): pages = [] + num = len(ids) while ids: page = list(ids[:25]) pages.append(page) ids = ids[25:] page_template = u'''\
    -
    +
    /browse/booklist_page
    {2}
    @@ -41,7 +45,7 @@ def render_book_list(ids): {pages}
    ''' - return templ.format(_('Browsing %d books')%len(ids), pages=rpages) + return templ.format(_('Browsing %d books')%num, pages=rpages) def utf8(x): # {{{ if isinstance(x, unicode): @@ -49,11 +53,13 @@ def utf8(x): # {{{ return x # }}} -def render_rating(rating, container='span'): # {{{ +def render_rating(rating, container='span', prefix=None): # {{{ if rating < 0.1: return '', '' added = 0 - rstring = xml(_('Average rating: %.1f stars')% (rating if rating else 0.0), + if prefix is None: + prefix = _('Average rating') + rstring = xml(_('%s: %.1f stars')% (prefix, rating if rating else 0.0), True) ans = ['<%s class="rating">' % (container)] for i in range(5): @@ -89,7 +95,7 @@ def get_category_items(category, items, db, datatype): # {{{ id_ = xml(str(id_)) desc = '' if i.count > 0: - desc += '[' + _('%d items')%i.count + ']' + desc += '[' + _('%d books')%i.count + ']' href = '/browse/matches/%s/%s'%(category, id_) return templ.format(xml(name), rating, xml(desc), xml(quote(href)), rstring) @@ -193,6 +199,14 @@ class BrowseServer(object): self.__browse_template__ = generate() return self.__browse_template__ + @property + def browse_summary_template(self): + if not hasattr(self, '__browse_summary_template__') or \ + self.opts.develop: + self.__browse_summary_template__ = \ + P('content_server/browse/summary.html', data=True).decode('utf-8') + return self.__browse_summary_template__ + # Catalogs {{{ def browse_toplevel(self): @@ -329,7 +343,6 @@ class BrowseServer(object): return json.dumps(entries, ensure_ascii=False) - @Endpoint() def browse_catalog(self, category=None, category_sort=None): 'Entry point for top-level, categories and sub-categories' @@ -401,16 +414,57 @@ class BrowseServer(object): ids = json.loads(ids) except: raise cherrypy.HTTPError(404, 'invalid ids') + summs = [] + for id_ in ids: + try: + id_ = int(id_) + mi = self.db.get_metadata(id_, index_is_id=True) + except: + continue + fmts = self.db.formats(id_, index_is_id=True) + if not fmts: + fmts = '' + fmts = [x.lower() for x in fmts.split(',') if x] + pf = prefs['output_format'].lower() + fmt = pf if pf in fmts else fmts[0] + args = {'id':id_, 'mi':mi, 'read_string':_('Read'),} + for key in mi.all_field_keys(): + val = mi.format_field(key)[1] + if not val: + val = '' + args[key] = xml(val, True) + fname = ascii_filename(args['title']) + ' - ' + ascii_filename(args['authors']) + args['href'] = '/get/%s/%s_%d.%s'%( + fmt, fname, id_, fmt) + args['comments'] = comments_to_html(mi.comments) + args['read_tooltip'] = \ + _('Read %s in the %s format')%(args['title'], fmt.upper()) + args['stars'] = '' + if mi.rating: + args['stars'] = render_rating(mi.rating/2.0, prefix=_('Rating'))[0] + if args['tags']: + args['tags'] = u'%s: '%_('Tags') + args['tags'] + args['other_formats'] = '' + other_fmts = [x for x in fmts if x.lower() != fmt.lower()] + + if other_fmts: + ofmts = [u'{3}'\ + .format(fmt, fname, id_, fmt.upper()) for fmt in + other_fmts] + ofmts = ', '.join(ofmts) + args['other_formats'] = u'%s: ' % \ + _('Other formats') + ofmts + + + summs.append(self.browse_summary_template.format(**args)) + + + return json.dumps('\n'.join(summs), ensure_ascii=False) # }}} # Search {{{ - def browse_search(self, query=None, offset=0, sort=None): - raise NotImplementedError() - # }}} - - # Book {{{ - def browse_book(self, uuid=None): + def browse_search(self, query=None): raise NotImplementedError() # }}} From 06c36eac92f848aa3312664ad56bab4bff5d0d93 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 16:58:39 -0600 Subject: [PATCH 091/140] Pagination of book lists --- resources/content_server/browse/browse.css | 46 ++++++++++++++++++ resources/content_server/browse/browse.js | 39 +++++++++++++--- resources/content_server/browse/summary.html | 2 +- src/calibre/library/server/browse.py | 49 ++++++++++++++++---- 4 files changed, 119 insertions(+), 17 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index d2e8106ba0..f3dfc89caf 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -279,6 +279,7 @@ h2.library_name { #booklist div.left { float: left; height: 190px; + width: 100px; vertical-align: middle; text-align: center; margin-left: 1em; @@ -288,6 +289,8 @@ h2.library_name { #booklist div.left img { display: block; margin-bottom: 1ex; + margin-left: auto; + margin-right: auto; } #booklist div.right { @@ -312,6 +315,7 @@ h2.library_name { #booklist .formats a { text-decoration: none; + color: blue; } #booklist .formats a:hover { @@ -326,5 +330,47 @@ h2.library_name { padding-bottom: 0.25em; } +#booklist .listnav { + display: table; + width: 100%; +} + +#booklist .listnav a { + color: blue; + text-decoration: none; +} + +#booklist .listnav a:hover { + color: red; +} + +#booklist .topnav { + border-bottom: solid black 1px; + margin-bottom: 1ex; +} + +#booklist .navleft { + display: table-cell; + text-align: left; +} + +#booklist .navleft a { + margin-right: 1em; +} + +#booklist .navmiddle { + display: table-cell; + text-align: center; +} + +#booklist .navright { + display: table-cell; + text-align: right; +} + +#booklist .navright a { + margin-left: 1em; +} + /* }}} */ diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 5f2af3299e..d6383d3646 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -138,9 +138,33 @@ function category() { // Booklist {{{ +function first_page() { + load_page($("#booklist #page0")); +} + +function last_page() { + load_page($("#booklist .page").last()); +} + +function next_page() { + var elem = $("#booklist .page:visible").next('.page'); + if (elem.length > 0) load_page(elem); + else first_page(); +} + +function previous_page() { + var elem = $("#booklist .page:visible").prev('.page'); + if (elem.length > 0) load_page(elem); + else last_page(); +} + function load_page(elem) { - var ids = elem.find(".load_data").attr('title'); - var href = elem.find(".load_data").html(); + if (elem.is(":visible")) return; + var ld = elem.find('.load_data'); + var ids = ld.attr('title'); + var href = ld.find(".url").attr('title'); + elem.children(".loaded").hide(); + $.ajax({ url: href, context: elem, @@ -155,12 +179,14 @@ function load_page(elem) { }, success: function(data) { this.children(".loaded").html(data); - this.children(".loaded").show(); - this.children(".loading").hide(); this.find(".left a.read").button(); + this.children(".loading").hide(); + this.parent().find('.navmiddle .start').html(this.find('.load_data .start').attr('title')); + this.parent().find('.navmiddle .end').html(this.find('.load_data .end').attr('title')); + this.children(".loaded").fadeIn(1000); } }); - $("#booklist .page").hide(); + $("#booklist .page:visible").hide(); elem.children(".loaded").hide(); elem.children(".loading").show(); elem.show(); @@ -173,8 +199,7 @@ function booklist() { return; } - load_page($("#booklist #page0")); - + first_page(); } // }}} diff --git a/resources/content_server/browse/summary.html b/resources/content_server/browse/summary.html index 0caf710a43..01f2b333f2 100644 --- a/resources/content_server/browse/summary.html +++ b/resources/content_server/browse/summary.html @@ -1,6 +1,6 @@
    diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index e1415decdb..bd63325ecc 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -21,31 +21,62 @@ from calibre.library.comments import comments_to_html def render_book_list(ids): pages = [] num = len(ids) + pos = 0 + delta = 25 while ids: - page = list(ids[:25]) - pages.append(page) - ids = ids[25:] + page = list(ids[:delta]) + pages.append((page, pos)) + ids = ids[delta:] + pos += len(page) page_template = u'''\
    -
    /browse/booklist_page
    +
    + + + +
    {2}
    ''' rpages = [] - for i, pg in enumerate(pages): + for i, x in enumerate(pages): + pg, pos = x ld = xml(json.dumps(pg), True) rpages.append(page_template.format(i, ld, - xml(_('Loading, please wait')) + '…')) + xml(_('Loading, please wait')) + '…', + start=pos+1, end=pos+len(pg))) rpages = u'\n\n'.join(rpages) templ = u'''\

    {0}

    +
    + {navbar} +
    {pages} +
    + {navbar} +
    ''' - return templ.format(_('Browsing %d books')%num, pages=rpages) + + navbar = u'''\ + + + + '''.format(first=_('First'), last=_('Last'), previous=_('Previous'), + next=_('Next'), num=num) + + return templ.format(_('Browsing %d books')%num, pages=rpages, navbar=navbar) def utf8(x): # {{{ if isinstance(x, unicode): @@ -182,7 +213,7 @@ class BrowseServer(object): opts = ['' % ( 'selected="selected" ' if k==sort else '', xml(k), xml(n), ) for k, n in - sorted(sort_opts, key=operator.itemgetter(1))] + sorted(sort_opts, key=operator.itemgetter(1)) if k and n] ans = ans.replace('{sort_select_options}', ('\n'+' '*20).join(opts)) lp = self.db.library_path if isbytestring(lp): @@ -402,7 +433,7 @@ class BrowseServer(object): sort = self.browse_sort_book_list(items, list_sort) ids = [x[0] for x in items] html = render_book_list(ids) - return self.browse_template(sort).format( + return self.browse_template(sort, category=False).format( title=_('Books in') + " " +category_name, script='booklist();', main=html) From f7b3f6a442666358f78026f6bc23433affaa78bf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 17:26:49 -0600 Subject: [PATCH 092/140] Fix #5249 (Error parsing accented characters in content server) --- resources/content_server/browse/browse.html | 2 +- resources/content_server/index.html | 2 +- src/calibre/library/server/mobile.py | 6 +++++- src/calibre/library/server/xml.py | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/resources/content_server/browse/browse.html b/resources/content_server/browse/browse.html index e1e4cd47f5..cd556b87a4 100644 --- a/resources/content_server/browse/browse.html +++ b/resources/content_server/browse/browse.html @@ -76,7 +76,7 @@
    ');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== +String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),k=0;k=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,k);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection(); +this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){e(this).removeClass("ui-resizable-autohide");b._handles.show()},function(){if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()}; +if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(), +d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset= +this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff={width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio: +this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize", +b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height; +f={width:c.size.width-(f?0:c.sizeDiff.width),height:c.size.height-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop", +b);this._helper&&this.helper.remove();return false},_updateCache:function(b){this.offset=this.helper.offset();if(l(b.left))this.position.left=b.left;if(l(b.top))this.position.top=b.top;if(l(b.height))this.size.height=b.height;if(l(b.width))this.size.width=b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(b.height)b.width=c.height*this.aspectRatio;else if(b.width)b.height=c.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top= +a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this.options,c=this.axis,d=l(b.width)&&a.maxWidth&&a.maxWidthb.width,h=l(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height, +k=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&k)b.left=i-a.minWidth;if(d&&k)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a
    ');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+ +a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this, +arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable, +{version:"1.8.5"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize, +function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var k=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:k.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n= +(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(k.css("position"))){c._revertToRelativePosition=true;k.css({position:"absolute",top:"auto",left:"auto"})}k.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition= +false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left- +a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize", +b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top", +"Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset, +f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left= +a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+ +a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&& +e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative", +height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width= +d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},l=function(b){return!isNaN(parseInt(b,10))}})(jQuery); ;/* * jQuery UI Accordion 1.8.5 * @@ -119,222 +166,42 @@ c=a("").addClass("ui-button-text").html(this.options.label).appendT this.hasTitle||b.attr("title",c)}}else b.addClass("ui-button-text-only")}}});a.widget("ui.buttonset",{_create:function(){this.element.addClass("ui-buttonset");this._init()},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(":button, :submit, :reset, :checkbox, :radio, a, :data(button)").filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":visible").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end().end()}, destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");a.Widget.prototype.destroy.call(this)}})})(jQuery); ;/* - * jQuery UI Effects 1.8.5 + * jQuery UI Dialog 1.8.5 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * - * http://docs.jquery.com/UI/Effects/ - */ -jQuery.effects||function(f,j){function l(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], -16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return m.transparent;return m[f.trim(c).toLowerCase()]}function r(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return l(b)}function n(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, -a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function o(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in s||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function t(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d= -a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:f.fx.speeds[b]||f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=r(b.elem,a);b.end=l(b.end);b.colorInit= -true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var m={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189, -183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255, -165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},p=["add","remove","toggle"],s={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,d){if(f.isFunction(b)){d=b;b=null}return this.each(function(){var e=f(this),g=e.attr("style")||" ",h=o(n.call(this)),q,u=e.attr("className");f.each(p,function(v, -i){c[i]&&e[i+"Class"](c[i])});q=o(n.call(this));e.attr("className",u);e.animate(t(h,q),a,b,function(){f.each(p,function(v,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments)})})};f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a? -f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.5",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"}); -c.css({position:"relative",top:0,left:0})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments);a={options:a[1],duration:a[2],callback:a[3]};var b=f.effects[c];return b&&!f.fx.off?b.call(this,a):this},_show:f.fn.show,show:function(c){if(!c|| -typeof c=="number"||f.fx.speeds[c]||!f.effects[c])return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||!f.effects[c])return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||!f.effects[c]||typeof c== -"boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c, -a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/= -e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+ -b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/ -2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ -e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); -;/* - * jQuery UI Effects Fade 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Fade - * - * Depends: - * jquery.effects.core.js - */ -(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Fold 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Fold - * - * Depends: - * jquery.effects.core.js - */ -(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","left"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],10)/100* -f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); -;/* - * jQuery UI Effects Highlight 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Highlight - * - * Depends: - * jquery.effects.core.js - */ -(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& -this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Pulsate 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Pulsate - * - * Depends: - * jquery.effects.core.js - */ -(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); -b.dequeue()})})}})(jQuery); +(function(c,j){c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,position:{my:"center",at:"center",of:window,collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title"); +if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||" ",f=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog", +"aria-labelledby":f}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var e=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i); +return false}).appendTo(e);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id",f).html(d).prependTo(e);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;e.find("*").add(e).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&& +g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog"); +b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==b.uiDialog[0])d=Math.max(d,c(this).css("z-index"))});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,f=d.options;if(f.modal&&!a||!f.stack&&!f.modal)return d._trigger("focus",b);if(f.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ= +f.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+=1;d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;d.next().length&&d.appendTo("body");a._size();a._position(b.position);d.show(b.show); +a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(f){if(f.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),e=g.filter(":first");g=g.filter(":last");if(f.target===g[0]&&!f.shiftKey){e.focus(1);return false}else if(f.target===e[0]&&f.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false, +f=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("
    ").addClass("ui-dialog-buttonset").appendTo(f);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,function(){return!(d=true)});if(d){c.each(a,function(e,h){h=c.isFunction(h)?{click:h,text:e}:h;e=c("",h).unbind("click").click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.fn.button&&e.button()});f.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(e){return{position:e.position, +offset:e.offset}}var b=this,d=b.options,f=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(e,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",e,a(h))},drag:function(e,h){b._trigger("drag",e,a(h))},stop:function(e,h){d.position=[h.position.left-f.scrollLeft(),h.position.top-f.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g); +b._trigger("dragStop",e,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}a=a===j?this.options.resizable:a;var d=this,f=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:f.maxWidth,maxHeight:f.maxHeight,minWidth:f.minWidth,minHeight:d._minHeight(), +handles:a,start:function(e,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",e,b(h))},resize:function(e,h){d._trigger("resize",e,b(h))},stop:function(e,h){c(this).removeClass("ui-dialog-resizing");f.height=c(this).height();f.width=c(this).width();d._trigger("resizeStop",e,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight, +a.height)},_position:function(a){var b=[],d=[0,0],f;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,e){if(+b[g]===b[g]){d[g]=b[g];b[g]=e}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(f=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(a); +f||this.uiDialog.hide()},_setOption:function(a,b){var d=this,f=d.uiDialog,g=f.is(":data(resizable)"),e=false;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);e=true;break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":f.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case "draggable":b? +d._makeDraggable():f.draggable("destroy");break;case "height":e=true;break;case "maxHeight":g&&f.resizable("option","maxHeight",b);e=true;break;case "maxWidth":g&&f.resizable("option","maxWidth",b);e=true;break;case "minHeight":g&&f.resizable("option","minHeight",b);e=true;break;case "minWidth":g&&f.resizable("option","minWidth",b);e=true;break;case "position":d._position(b);break;case "resizable":g&&!b&&f.resizable("destroy");g&&typeof b==="string"&&f.resizable("option","handles",b);!g&&b!==false&& +d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||" "));break;case "width":e=true;break}c.Widget.prototype._setOption.apply(d,arguments);e&&d._size()},_size:function(){var a=this.options,b;this.element.css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();this.element.css(a.height==="auto"?{minHeight:Math.max(a.minHeight-b,0),height:c.support.minHeight?"auto":Math.max(a.minHeight- +b,0)}:{minHeight:0,height:Math.max(a.height-b,0)}).show();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.5",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","), +function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){this.oldInstances.push(this.instances.splice(c.inArray(a,this.instances),1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var b=0;c.each(this.instances,function(){b=Math.max(b,this.css("z-index"))});this.maxZ=b},height:function(){var a, +b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a Date: Fri, 15 Oct 2010 19:57:41 -0600 Subject: [PATCH 095/140] /browse: Show category being currently listed and hide sort combobox in newest book list --- resources/content_server/browse/browse.js | 3 ++- src/calibre/library/server/browse.py | 16 ++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 397db397bf..5e0d9f8ee4 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -193,7 +193,8 @@ function load_page(elem) { elem.show(); } -function booklist() { +function booklist(hide_sort) { + if (hide_sort) $("#content > .sort_select").hide(); var test = $("#booklist #page0").html(); if (!test) { $("#booklist").html(render_error("No books found")); diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 69a931b1e2..c8c816d617 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -18,7 +18,7 @@ from calibre.utils.filenames import ascii_filename from calibre.utils.config import prefs from calibre.library.comments import comments_to_html -def render_book_list(ids): # {{{ +def render_book_list(ids, suffix=''): # {{{ pages = [] num = len(ids) pos = 0 @@ -49,7 +49,7 @@ def render_book_list(ids): # {{{ rpages = u'\n\n'.join(rpages) templ = u'''\ -

    {0}

    +

    {0} {suffix}

    {navbar} @@ -76,7 +76,8 @@ def render_book_list(ids): # {{{ '''.format(first=_('First'), last=_('Last'), previous=_('Previous'), next=_('Next'), num=num) - return templ.format(_('Browsing %d books')%num, pages=rpages, navbar=navbar) + return templ.format(_('Browsing %d books')%num, suffix=suffix, + pages=rpages, navbar=navbar) # }}} @@ -420,6 +421,7 @@ class BrowseServer(object): raise category_name = _('Newest') + hide_sort = 'false' if category == 'search': which = unhexlify(cid) try: @@ -428,6 +430,7 @@ class BrowseServer(object): raise cherrypy.HTTPError(404, 'Search: %r not understood'%which) elif category == 'newest': ids = list(self.db.data.iterallids()) + hide_sort = 'true' else: ids = self.db.get_books_for_category(category, cid) @@ -436,10 +439,11 @@ class BrowseServer(object): list_sort = 'timestamp' sort = self.browse_sort_book_list(items, list_sort) ids = [x[0] for x in items] - html = render_book_list(ids) + html = render_book_list(ids, suffix=_('in') + ' ' + category_name) + return self.browse_template(sort, category=False).format( title=_('Books in') + " " +category_name, - script='booklist();', main=html) + script='booklist(%s);'%hide_sort, main=html) @Endpoint(mimetype='application/json; charset=utf-8') def browse_booklist_page(self, ids=None, sort=None): @@ -509,7 +513,7 @@ class BrowseServer(object): items = [self.db.data._data[x] for x in ids] sort = self.browse_sort_book_list(items, list_sort) ids = [x[0] for x in items] - html = render_book_list(ids) + html = render_book_list(ids, suffix=_('in search')+': '+query) return self.browse_template(sort, category=False, initial_search=query).format( title=_('Matching books'), script='booklist();', main=html) From 5c02ca217343ee71004dfdcb1e8d19f653134b97 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 19:59:29 -0600 Subject: [PATCH 096/140] ... --- src/calibre/library/server/browse.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index c8c816d617..439c0f4cf2 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -191,6 +191,7 @@ class BrowseServer(object): connect('browse_search', base_href+'/search', self.browse_search) + # Templates {{{ def browse_template(self, sort, category=True, initial_search=''): def generate(): @@ -243,6 +244,7 @@ class BrowseServer(object): P('content_server/browse/summary.html', data=True).decode('utf-8') return self.__browse_summary_template__ + # }}} # Catalogs {{{ def browse_toplevel(self): From 5b1cfa68650b742fabf781b5cc011433e1581ea7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 20:49:57 -0600 Subject: [PATCH 097/140] ... --- resources/content_server/browse/browse.css | 12 +++-- resources/content_server/browse/browse.html | 1 + resources/content_server/browse/browse.js | 15 +++++- resources/content_server/browse/summary.html | 6 ++- .../images/ui-bg_glass_100_f5f0e5_1x400.png | Bin 157 -> 123 bytes .../images/ui-bg_glass_70_ede4d4_1x400.png | Bin 125 -> 161 bytes .../ui-bg_inset-soft_100_f4f0ec_1x100.png | Bin 141 -> 113 bytes .../images/ui-icons_f08000_256x240.png | Bin 5355 -> 4369 bytes .../images/ui-icons_f35f07_256x240.png | Bin 4369 -> 5355 bytes .../images/ui-icons_ff7519_256x240.png | Bin 4369 -> 5355 bytes .../jquery-ui-1.8.5.custom.css | 44 +++++++++++++++++- src/calibre/library/server/browse.py | 6 ++- 12 files changed, 76 insertions(+), 8 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index f3dfc89caf..aceb595383 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -309,21 +309,27 @@ h2.library_name { display: block; } +#booklist div.right .stars .series { + display: block; +} + #booklist .title { font-size: larger; } -#booklist .formats a { +#booklist a { text-decoration: none; color: blue; } -#booklist .formats a:hover { +#booklist a:hover { color: red; } + #booklist .left .ui-button-text { - font-weight: bold; + font-size: medium; + color: black; padding-left: 0.25em; padding-right: 0.25em; padding-top: 0.25em; diff --git a/resources/content_server/browse/browse.html b/resources/content_server/browse/browse.html index 8c6d310015..b3039f8c4e 100644 --- a/resources/content_server/browse/browse.html +++ b/resources/content_server/browse/browse.html @@ -94,5 +94,6 @@
    + diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 5e0d9f8ee4..9d3c01fa15 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -200,8 +200,21 @@ function booklist(hide_sort) { $("#booklist").html(render_error("No books found")); return; } - + $("#book_details_dialog").dialog({ + autoOpen: false, + modal: true + }); first_page(); } +function show_details(a_dom) { + var book = $(a_dom).closest('div.summary'); + var id = book.attr('id').split('_')[1]; + var bd = $('#book_details_dialog'); + bd.attr('title', 'test'); + bd.html('test'); + bd.dialog('option', 'width', $('#container').width() - 50); + bd.dialog('open'); +} + // }}} diff --git a/resources/content_server/browse/summary.html b/resources/content_server/browse/summary.html index 01f2b333f2..8df42de366 100644 --- a/resources/content_server/browse/summary.html +++ b/resources/content_server/browse/summary.html @@ -4,7 +4,11 @@ {read_string}
    -
    {stars}{series}
    +
    + {stars} + {series} + {details} +
    {title}
    {authors}
    {comments}
    diff --git a/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_100_f5f0e5_1x400.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_100_f5f0e5_1x400.png index 688c2f3e8806f3afcc0b659d397d142ee4a0bb1c..4fb71be1b40892d5b6ef1e4091104a876a9892f6 100644 GIT binary patch delta 101 zcmV-r0Gj`u0eg@fUjT1d3flkx076MbK~y-6?awg^fKUts(V4yfUF|+7f)?_%u@_{U z$0Vc)>sXEelu~}nzKuC55oZ|!5|ve*qX@I!yoo0Axu-K~y-6?a?s`!!Qs;(Z63-Ain=S-6_bCiJKHT zfUyKh^VqF+hNYJ6BR~(@UNGe+njSYFr)%@0Rb29lgi|%|N_)RhGnz7n%Lu7%VB(+8 qOWO-Zh++Dj2)`@D2uc53#0gdk9<2slO@jac002ovPDHLk0$_qBz&J<% diff --git a/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_70_ede4d4_1x400.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-bg_glass_70_ede4d4_1x400.png index 779ef0d0e24ce761cdbf952b5f1db4c80face3fe..0b4ae4776b3627a480c7fa2a04a7198c9a8f6584 100644 GIT binary patch delta 140 zcmV;70CWF+p#hK_e*qX@I!yoo0BA`>K~y-6?a@IB!axv2(ciz@D1!Gto(FK{A|W9W z-Q)<85ys7A%C=0HHSC1-3#|^I uCuV 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 delta 5037 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 diff --git a/resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_ff7519_256x240.png b/resources/content_server/jquery_ui/css/humanity-custom/images/ui-icons_ff7519_256x240.png index 544517142205f1adfc22b44eda10028e10abd42c..5a4070af92983caef197fffc854029b8047b31e7 100644 GIT binary patch delta 5037 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 diff --git a/resources/content_server/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css b/resources/content_server/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css index f1625b027f..6b221bca77 100644 --- a/resources/content_server/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css +++ b/resources/content_server/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css @@ -50,13 +50,13 @@ * * http://docs.jquery.com/UI/Theming/API * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1em&cornerRadius=6px&bgColorHeader=cb842e&bgTextureHeader=02_glass.png&bgImgOpacityHeader=25&borderColorHeader=d49768&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=f4f0ec&bgTextureContent=05_inset_soft.png&bgImgOpacityContent=100&borderColorContent=e0cfc2&fcContent=1e1b1d&iconColorContent=c47a23&bgColorDefault=ede4d4&bgTextureDefault=02_glass.png&bgImgOpacityDefault=70&borderColorDefault=cdc3b7&fcDefault=3f3731&iconColorDefault=f08000&bgColorHover=f5f0e5&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=f5ad66&fcHover=a46313&iconColorHover=f08000&bgColorActive=f4f0ec&bgTextureActive=04_highlight_hard.png&bgImgOpacityActive=100&borderColorActive=e0cfc2&fcActive=b85700&iconColorActive=f35f07&bgColorHighlight=f5f5b5&bgTextureHighlight=04_highlight_hard.png&bgImgOpacityHighlight=75&borderColorHighlight=d9bb73&fcHighlight=060200&iconColorHighlight=cb672b&bgColorError=fee4bd&bgTextureError=04_highlight_hard.png&bgImgOpacityError=65&borderColorError=f8893f&fcError=592003&iconColorError=ff7519&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=75&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=75&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px + * To view and modify this theme, visit http://jqueryui.com/themeroller/?tr=ffDefault=Helvetica,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=6px&bgColorHeader=cb842e&bgTextureHeader=02_glass.png&bgImgOpacityHeader=25&borderColorHeader=d49768&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=f4f0ec&bgTextureContent=05_inset_soft.png&bgImgOpacityContent=100&borderColorContent=e0cfc2&fcContent=1e1b1d&iconColorContent=c47a23&bgColorDefault=ede4d4&bgTextureDefault=02_glass.png&bgImgOpacityDefault=70&borderColorDefault=cdc3b7&fcDefault=3f3731&iconColorDefault=f08000&bgColorHover=f5f0e5&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=f5ad66&fcHover=a46313&iconColorHover=f08000&bgColorActive=f4f0ec&bgTextureActive=04_highlight_hard.png&bgImgOpacityActive=100&borderColorActive=e0cfc2&fcActive=b85700&iconColorActive=f35f07&bgColorHighlight=f5f5b5&bgTextureHighlight=04_highlight_hard.png&bgImgOpacityHighlight=75&borderColorHighlight=d9bb73&fcHighlight=060200&iconColorHighlight=cb672b&bgColorError=fee4bd&bgTextureError=04_highlight_hard.png&bgImgOpacityError=65&borderColorError=f8893f&fcError=592003&iconColorError=ff7519&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=75&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=75&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px */ /* Component containers ----------------------------------*/ -.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1em; } +.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; } .ui-widget .ui-widget { font-size: 1em; } .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } .ui-widget-content { border: 1px solid #e0cfc2; background: #f4f0ec url(images/ui-bg_inset-soft_100_f4f0ec_1x100.png) 50% bottom repeat-x; color: #1e1b1d; } @@ -293,6 +293,25 @@ /* Overlays */ .ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_75_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } .ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_75_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* + * jQuery UI Resizable @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizable#theming + */ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* * jQuery UI Accordion @VERSION * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) @@ -348,3 +367,24 @@ input.ui-button { padding: .4em 1em; } /* workarounds */ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ +/* + * jQuery UI Dialog @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog#theming + */ +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } +.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } +.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 439c0f4cf2..7d476ade3b 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -470,7 +470,11 @@ class BrowseServer(object): fmts = [x.lower() for x in fmts.split(',') if x] pf = prefs['output_format'].lower() fmt = pf if pf in fmts else fmts[0] - args = {'id':id_, 'mi':mi, 'read_string':_('Read'),} + args = {'id':id_, 'mi':mi, + 'read_string':xml(_('Read'), True), + 'details': xml(_('Details'), True), + 'details_tt': xml(_('Show book details'), True) + } for key in mi.all_field_keys(): val = mi.format_field(key)[1] if not val: From a657a3fb67f0593f89003d3b49fc8f4ae45cefd8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 21:02:18 -0600 Subject: [PATCH 098/140] ... --- resources/content_server/browse/browse.js | 9 +- .../js/jquery-ui-1.8.5.custom.min.js | 219 ++++++++++++++++++ 2 files changed, 225 insertions(+), 3 deletions(-) diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 9d3c01fa15..f5b04db5f8 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -202,7 +202,8 @@ function booklist(hide_sort) { } $("#book_details_dialog").dialog({ autoOpen: false, - modal: true + modal: true, + show: 'slide' }); first_page(); } @@ -211,9 +212,11 @@ function show_details(a_dom) { var book = $(a_dom).closest('div.summary'); var id = book.attr('id').split('_')[1]; var bd = $('#book_details_dialog'); - bd.attr('title', 'test'); - bd.html('test'); + bd.html('LoadingLoading, please wait…'); bd.dialog('option', 'width', $('#container').width() - 50); + bd.dialog('option', 'height', $(window).height() - 100); + + bd.dialog('option', 'title', book.find('.title').text()); bd.dialog('open'); } diff --git a/resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js b/resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js index 034d13a25a..d858ce51de 100644 --- a/resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js +++ b/resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js @@ -204,4 +204,223 @@ function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.in (this.oldInstances.pop()||c("
    ").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){this.oldInstances.push(this.instances.splice(c.inArray(a,this.instances),1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var b=0;c.each(this.instances,function(){b=Math.max(b,this.css("z-index"))});this.maxZ=b},height:function(){var a, b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a
    ").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"}); +c.css({position:"relative",top:0,left:0})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments);a={options:a[1],duration:a[2],callback:a[3]};var b=f.effects[c];return b&&!f.fx.off?b.call(this,a):this},_show:f.fn.show,show:function(c){if(!c|| +typeof c=="number"||f.fx.speeds[c]||!f.effects[c])return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||!f.effects[c])return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||!f.effects[c]||typeof c== +"boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c, +a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/= +e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+ +b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/ +2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ +e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); +;/* + * jQuery UI Effects Fade 1.8.5 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fade + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Fold 1.8.5 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fold + * + * Depends: + * jquery.effects.core.js + */ +(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","left"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],10)/100* +f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); +;/* + * jQuery UI Effects Highlight 1.8.5 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Highlight + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& +this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Pulsate 1.8.5 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Pulsate + * + * Depends: + * jquery.effects.core.js + */ +(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); +b.dequeue()})})}})(jQuery); ; \ No newline at end of file From 953da12b44fcf09cb34d4fe3473fc88b4813b08b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 21:32:05 -0600 Subject: [PATCH 099/140] ... --- resources/content_server/browse/browse.css | 4 ++++ resources/content_server/browse/browse.html | 2 +- resources/content_server/browse/browse.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index aceb595383..406de09f53 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -176,6 +176,10 @@ h2.library_name { overflow: hidden; } +#search_box .ui-button { + padding: 0.25em; +} + /* }}} */ /* Top level {{{ */ diff --git a/resources/content_server/browse/browse.html b/resources/content_server/browse/browse.html index b3039f8c4e..4acc15f3ea 100644 --- a/resources/content_server/browse/browse.html +++ b/resources/content_server/browse/browse.html @@ -94,6 +94,6 @@ - +
    diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index f5b04db5f8..16753a38b9 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -213,7 +213,7 @@ function show_details(a_dom) { var id = book.attr('id').split('_')[1]; var bd = $('#book_details_dialog'); bd.html('LoadingLoading, please wait…'); - bd.dialog('option', 'width', $('#container').width() - 50); + bd.dialog('option', 'width', $(window).width() - 100); bd.dialog('option', 'height', $(window).height() - 100); bd.dialog('option', 'title', book.find('.title').text()); From cf644ac63ecfd70e3d12614b82b6fff551ba3ee4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 22:05:51 -0600 Subject: [PATCH 100/140] ... --- resources/content_server/browse/browse.js | 16 +++- resources/content_server/browse/summary.html | 1 + src/calibre/library/server/browse.py | 93 ++++++++++++++------ 3 files changed, 79 insertions(+), 31 deletions(-) diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 16753a38b9..367b8341d9 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -210,13 +210,25 @@ function booklist(hide_sort) { function show_details(a_dom) { var book = $(a_dom).closest('div.summary'); - var id = book.attr('id').split('_')[1]; var bd = $('#book_details_dialog'); bd.html('LoadingLoading, please wait…'); bd.dialog('option', 'width', $(window).width() - 100); bd.dialog('option', 'height', $(window).height() - 100); - bd.dialog('option', 'title', book.find('.title').text()); + + $.ajax({ + url: book.find('.details-href').attr('title'), + context: bd, + dataType: "json", + timeout: 600000, //milliseconds (10 minutes) + error: function(xhr, stat, err) { + this.html(render_error(stat)); + }, + success: function(data) { + this.html(data); + } + }); + bd.dialog('open'); } diff --git a/resources/content_server/browse/summary.html b/resources/content_server/browse/summary.html index 8df42de366..ba23ed854c 100644 --- a/resources/content_server/browse/summary.html +++ b/resources/content_server/browse/summary.html @@ -15,4 +15,5 @@
    {tags}
    {other_formats}
    + diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 7d476ade3b..805a8f6424 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -187,9 +187,10 @@ class BrowseServer(object): connect('browse_booklist_page', base_href+'/booklist_page', self.browse_booklist_page) - connect('browse_search', base_href+'/search', self.browse_search) + connect('browse_details', base_href+'/details/{id}', + self.browse_details) # Templates {{{ def browse_template(self, sort, category=True, initial_search=''): @@ -447,6 +448,23 @@ class BrowseServer(object): title=_('Books in') + " " +category_name, script='booklist(%s);'%hide_sort, main=html) + def browse_get_book_args(self, mi, id_): + fmts = self.db.formats(id_, index_is_id=True) + if not fmts: + fmts = '' + fmts = [x.lower() for x in fmts.split(',') if x] + pf = prefs['output_format'].lower() + fmt = pf if pf in fmts else fmts[0] + args = {'id':id_, 'mi':mi, + } + for key in mi.all_field_keys(): + val = mi.format_field(key)[1] + if not val: + val = '' + args[key] = xml(val, True) + fname = ascii_filename(args['title']) + ' - ' + ascii_filename(args['authors']) + return args, fmt, fmts, fname + @Endpoint(mimetype='application/json; charset=utf-8') def browse_booklist_page(self, ids=None, sort=None): if sort == 'null': @@ -464,36 +482,9 @@ class BrowseServer(object): mi = self.db.get_metadata(id_, index_is_id=True) except: continue - fmts = self.db.formats(id_, index_is_id=True) - if not fmts: - fmts = '' - fmts = [x.lower() for x in fmts.split(',') if x] - pf = prefs['output_format'].lower() - fmt = pf if pf in fmts else fmts[0] - args = {'id':id_, 'mi':mi, - 'read_string':xml(_('Read'), True), - 'details': xml(_('Details'), True), - 'details_tt': xml(_('Show book details'), True) - } - for key in mi.all_field_keys(): - val = mi.format_field(key)[1] - if not val: - val = '' - args[key] = xml(val, True) - fname = ascii_filename(args['title']) + ' - ' + ascii_filename(args['authors']) - args['href'] = '/get/%s/%s_%d.%s'%( - fmt, fname, id_, fmt) - args['comments'] = comments_to_html(mi.comments) - args['read_tooltip'] = \ - _('Read %s in the %s format')%(args['title'], fmt.upper()) - args['stars'] = '' - if mi.rating: - args['stars'] = render_rating(mi.rating/2.0, prefix=_('Rating'))[0] - if args['tags']: - args['tags'] = u'%s: '%_('Tags') + args['tags'] + args, fmt, fmts, fname = self.browse_get_book_args(mi, id_) args['other_formats'] = '' other_fmts = [x for x in fmts if x.lower() != fmt.lower()] - if other_fmts: ofmts = [u'{3}'\ .format(fmt, fname, id_, fmt.upper()) for fmt in @@ -502,12 +493,56 @@ class BrowseServer(object): args['other_formats'] = u'%s: ' % \ _('Other formats') + ofmts + args['details_href'] = '/browse/details/'+str(id_) + args['read_tooltip'] = \ + _('Read %s in the %s format')%(args['title'], fmt.upper()) + args['href'] = '/get/%s/%s_%d.%s'%( + fmt, fname, id_, fmt) + args['comments'] = comments_to_html(mi.comments) + args['stars'] = '' + if mi.rating: + args['stars'] = render_rating(mi.rating/2.0, prefix=_('Rating'))[0] + if args['tags']: + args['tags'] = u'%s: '%_('Tags') + \ + xml(args['tags']) + if args['series']: + args['series'] = xml(args['series']) + args['read_string'] = xml(_('Read'), True) + args['details'] = xml(_('Details'), True) + args['details_tt'] = xml(_('Show book details'), True) summs.append(self.browse_summary_template.format(**args)) return json.dumps('\n'.join(summs), ensure_ascii=False) + @Endpoint(mimetype='application/json; charset=utf-8') + def browse_details(self, id=None): + try: + id_ = int(id) + except: + raise cherrypy.HTTPError(404, 'invalid id: %r'%id) + + try: + mi = self.db.get_metadata(id_, index_is_id=True) + except: + ans = _('This book has been deleted') + else: + args, fmt, fmts, fname = self.browse_get_book_args(mi, id_) + args['formats'] = '' + if fmts: + ofmts = [u'{3}'\ + .format(fmt, fname, id_, fmt.upper()) for fmt in + fmts] + ofmts = ', '.join(ofmts) + args['formats'] = u'%s: ' % \ + _('Formats') + ofmts + + + return json.dumps(ans, ensure_ascii=False) + + + # }}} # Search {{{ From ff8f949f97a267be14552acd728177dc1e742ab9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 23:05:35 -0600 Subject: [PATCH 101/140] /browse: Implement Book details --- resources/content_server/browse/browse.css | 38 ++++++++++++++++ resources/content_server/browse/details.html | 10 +++++ src/calibre/library/field_metadata.py | 2 +- src/calibre/library/server/browse.py | 47 +++++++++++++++++--- 4 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 resources/content_server/browse/details.html diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 406de09f53..d50b6936ff 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -384,3 +384,41 @@ h2.library_name { /* }}} */ +/* Details {{{ */ + +.details .left { + float: left; + max-width: 50%; + margin-right: 2em; + overflow: auto; +} + +.details .right { + overflow: auto; +} + +.details .formats { + margin-bottom: 2ex; +} + +#book_details_dialog .details a { + color: blue; + text-decoration: none; +} + +#book_details_dialog .details a:hover { + color: red; +} + +.details .field { + margin-bottom: 0.5em; +} + +.details .comment { + margin-left: 1em; + overflow: auto; + max-height: 50%; +} + +/* }}} */ + diff --git a/resources/content_server/browse/details.html b/resources/content_server/browse/details.html new file mode 100644 index 0000000000..59af5c535e --- /dev/null +++ b/resources/content_server/browse/details.html @@ -0,0 +1,10 @@ +
    +
    + Cover of {title} +
    +
    +
    {formats}
    + {fields} + {comments} +
    +
    diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index b43a6620d0..69dd7ae636 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -161,7 +161,7 @@ class FieldMetadata(dict): 'datatype':'text', 'is_multiple':None, 'kind':'field', - 'name':None, + 'name':_('Comments'), 'search_terms':['comments', 'comment'], 'is_custom':False, 'is_category':False}), ('cover', {'table':None, diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 805a8f6424..5e7de43d45 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -245,6 +245,14 @@ class BrowseServer(object): P('content_server/browse/summary.html', data=True).decode('utf-8') return self.__browse_summary_template__ + @property + def browse_details_template(self): + if not hasattr(self, '__browse_details_template__') or \ + self.opts.develop: + self.__browse_details_template__ = \ + P('content_server/browse/details.html', data=True).decode('utf-8') + return self.__browse_details_template__ + # }}} # Catalogs {{{ @@ -503,10 +511,10 @@ class BrowseServer(object): if mi.rating: args['stars'] = render_rating(mi.rating/2.0, prefix=_('Rating'))[0] if args['tags']: - args['tags'] = u'%s: '%_('Tags') + \ - xml(args['tags']) + args['tags'] = u'%s: '%xml(_('Tags')) + \ + args['tags'] if args['series']: - args['series'] = xml(args['series']) + args['series'] = args['series'] args['read_string'] = xml(_('Read'), True) args['details'] = xml(_('Details'), True) args['details_tt'] = xml(_('Show book details'), True) @@ -535,9 +543,38 @@ class BrowseServer(object): .format(fmt, fname, id_, fmt.upper()) for fmt in fmts] ofmts = ', '.join(ofmts) - args['formats'] = u'%s: ' % \ - _('Formats') + ofmts + args['formats'] = ofmts + fields, comments = [], [] + for field, m in list(mi.get_all_standard_metadata(False).items()) + \ + list(mi.get_all_user_metadata(False).items()): + if m['datatype'] == 'comments' or field == 'comments': + comments.append((m['name'], comments_to_html(mi.get(field, + '')))) + continue + if field in ('title', 'formats') or not args.get(field, False) \ + or not m['name']: + continue + if m['datatype'] == 'rating': + r = u'%s: '%xml(m['name']) + \ + render_rating(mi.rating/2.0, prefix=m['name'])[0] + else: + r = u'%s: '%xml(m['name']) + \ + args[field] + fields.append((m['name'], r)) + fields.sort(key=lambda x: x[0].lower()) + fields = [u'
    {0}
    '.format(f[1]) for f in + fields] + fields = u'
    %s
    '%('\n\n'.join(fields)) + + comments.sort(key=lambda x: x[0].lower()) + comments = [(u'
    %s: ' + u'
    %s
    ') % (xml(c[0]), + c[1]) for c in comments] + comments = u'
    %s
    '%('\n\n'.join(comments)) + ans = self.browse_details_template.format(id=id_, + title=xml(mi.title, True), fields=fields, + formats=args['formats'], comments=comments) return json.dumps(ans, ensure_ascii=False) From 9bb060124ed756196658f9485709261b8096b553 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2010 23:07:37 -0600 Subject: [PATCH 102/140] ... --- src/calibre/gui2/dialogs/metadata_single.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 64ac4a3ccb..8043016f9f 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -25,7 +25,7 @@ from calibre.ebooks.metadata.covers import download_cover from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata import MetaInformation from calibre.utils.config import prefs, tweaks -from calibre.utils.date import qt_to_dt, local_tz, utcfromtimestamp, utc_tz +from calibre.utils.date import qt_to_dt, local_tz, utcfromtimestamp from calibre.customize.ui import run_plugins_on_import, get_isbndb_key from calibre.gui2.preferences.social import SocialMetadata from calibre.gui2.custom_column_widgets import populate_metadata_page From 848fe8096dd28fa6cf89661eb904b1f1ae7c5793 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 06:57:16 -0600 Subject: [PATCH 103/140] ... --- src/calibre/library/server/content.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py index 8c5fef4ee1..d95cd1818c 100644 --- a/src/calibre/library/server/content.py +++ b/src/calibre/library/server/content.py @@ -140,7 +140,7 @@ class ContentServer(object): updated = self.build_time else: with cover as f: - updated = fromtimestamp(os.stat(f.name).st_mtime) + updated = fromtimestamp(os.fstat(f.fileno()).st_mtime) cover = f.read() cherrypy.response.headers['Last-Modified'] = self.last_modified(updated) From e46fed78ccf4d8c714c94317a960afe121d954f0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 07:04:38 -0600 Subject: [PATCH 104/140] Fix regression in formats listing in /browse summaries --- src/calibre/library/server/browse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 5e7de43d45..f28762dddf 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -495,7 +495,7 @@ class BrowseServer(object): other_fmts = [x for x in fmts if x.lower() != fmt.lower()] if other_fmts: ofmts = [u'{3}'\ - .format(fmt, fname, id_, fmt.upper()) for fmt in + .format(f, fname, id_, f.upper()) for f in other_fmts] ofmts = ', '.join(ofmts) args['other_formats'] = u'%s: ' % \ From ed3909b1a781d3ee1acfebba41d68c24b88f6edb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 07:29:06 -0600 Subject: [PATCH 105/140] Fix bugs in frwrapper returned by lopen on windows --- src/calibre/startup.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/calibre/startup.py b/src/calibre/startup.py index 1046cd93b3..b5741c1991 100644 --- a/src/calibre/startup.py +++ b/src/calibre/startup.py @@ -120,7 +120,8 @@ if not _run_once: object.__setattr__(self, 'name', name) def __getattribute__(self, attr): - if attr == 'name': + if attr in ('name', '__enter__', '__str__', '__unicode__', + '__repr__'): return object.__getattribute__(self, attr) fobject = object.__getattribute__(self, 'fobject') return getattr(fobject, attr) @@ -141,6 +142,10 @@ if not _run_once: def __unicode__(self): return repr(self).decode('utf-8') + def __enter__(self): + fobject = object.__getattribute__(self, 'fobject') + fobject.__enter__() + return self m = mode[0] random = len(mode) > 1 and mode[1] == '+' From 16368ec54b639ccf3d9fe86092aca8432a899d5e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 07:43:10 -0600 Subject: [PATCH 106/140] /browse: Fix handling of books with no formats --- resources/content_server/browse/summary.html | 2 +- src/calibre/library/server/browse.py | 39 +++++++++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/resources/content_server/browse/summary.html b/resources/content_server/browse/summary.html index ba23ed854c..4e9c9d2a77 100644 --- a/resources/content_server/browse/summary.html +++ b/resources/content_server/browse/summary.html @@ -1,7 +1,7 @@
    Cover of {title} - {read_string} + {get_button}
    diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index f28762dddf..89b1fe9afc 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -462,7 +462,10 @@ class BrowseServer(object): fmts = '' fmts = [x.lower() for x in fmts.split(',') if x] pf = prefs['output_format'].lower() - fmt = pf if pf in fmts else fmts[0] + try: + fmt = pf if pf in fmts else fmts[0] + except: + fmt = None args = {'id':id_, 'mi':mi, } for key in mi.all_field_keys(): @@ -492,20 +495,29 @@ class BrowseServer(object): continue args, fmt, fmts, fname = self.browse_get_book_args(mi, id_) args['other_formats'] = '' - other_fmts = [x for x in fmts if x.lower() != fmt.lower()] - if other_fmts: - ofmts = [u'{3}'\ - .format(f, fname, id_, f.upper()) for f in - other_fmts] - ofmts = ', '.join(ofmts) - args['other_formats'] = u'%s: ' % \ - _('Other formats') + ofmts + if fmts and fmt: + other_fmts = [x for x in fmts if x.lower() != fmt.lower()] + if other_fmts: + ofmts = [u'{3}'\ + .format(f, fname, id_, f.upper()) for f in + other_fmts] + ofmts = ', '.join(ofmts) + args['other_formats'] = u'%s: ' % \ + _('Other formats') + ofmts args['details_href'] = '/browse/details/'+str(id_) - args['read_tooltip'] = \ - _('Read %s in the %s format')%(args['title'], fmt.upper()) - args['href'] = '/get/%s/%s_%d.%s'%( - fmt, fname, id_, fmt) + + if fmt: + href = '/get/%s/%s_%d.%s'%( + fmt, fname, id_, fmt) + rt = xml(_('Read %s in the %s format')%(args['title'], + fmt.upper()), True) + + args['get_button'] = \ + '%s' % \ + (xml(href, True), rt, xml(_('Get'))) + else: + args['get_button'] = '' args['comments'] = comments_to_html(mi.comments) args['stars'] = '' if mi.rating: @@ -515,7 +527,6 @@ class BrowseServer(object): args['tags'] if args['series']: args['series'] = args['series'] - args['read_string'] = xml(_('Read'), True) args['details'] = xml(_('Details'), True) args['details_tt'] = xml(_('Show book details'), True) From 4c207d9b6f071f36e0507b1a5e60d2cee5e14f3f Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 16 Oct 2010 14:54:03 +0100 Subject: [PATCH 107/140] Remove non-displayable categories from the home page --- src/calibre/library/server/browse.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index a122f539c6..be8b55e648 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -267,6 +267,7 @@ class BrowseServer(object): def getter(x): return category_meta[x]['name'].lower() + displayed_custom_fields = custom_fields_to_display(self.db) for category in sorted(categories, cmp=lambda x,y: cmp(getter(x), getter(y))): if len(categories[category]) == 0: @@ -276,6 +277,8 @@ class BrowseServer(object): meta = category_meta.get(category, None) if meta is None: continue + if meta['is_custom'] and category not in displayed_custom_fields: + continue cats.append((meta['name'], category)) cats = ['
  • {0}/browse/category/{1}
  • '\ .format(xml(x, True), xml(quote(y)), xml(_('Browse books by'))) From 2e10fd115b7181d0616dd168184b986f25e578bf Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 16 Oct 2010 15:21:36 +0100 Subject: [PATCH 108/140] Put the icon file map into field metadata, then change tag_view to use it. --- src/calibre/gui2/tag_view.py | 18 ++++++------------ src/calibre/library/field_metadata.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 3505cc7344..6e0aef1b99 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -17,7 +17,7 @@ from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, \ from calibre.ebooks.metadata import title_sort from calibre.gui2 import config, NONE -from calibre.library.field_metadata import TagsIcons +from calibre.library.field_metadata import TagsIcons, category_icon_map from calibre.utils.search_query_parser import saved_searches from calibre.gui2 import error_dialog from calibre.gui2.dialogs.confirm_delete import confirm @@ -382,17 +382,11 @@ class TagsModel(QAbstractItemModel): # {{{ # must do this here because 'QPixmap: Must construct a QApplication # before a QPaintDevice'. The ':' in front avoids polluting either the # user-defined categories (':' at end) or columns namespaces (no ':'). - self.category_icon_map = TagsIcons({ - 'authors' : QIcon(I('user_profile.png')), - 'series' : QIcon(I('series.png')), - 'formats' : QIcon(I('book.png')), - 'publisher' : QIcon(I('publisher.png')), - 'rating' : QIcon(I('rating.png')), - 'news' : QIcon(I('news.png')), - 'tags' : QIcon(I('tags.png')), - ':custom' : QIcon(I('column.png')), - ':user' : QIcon(I('drawer.png')), - 'search' : QIcon(I('search.png'))}) + iconmap = {} + for key in category_icon_map: + iconmap[key] = QIcon(I(category_icon_map[key])) + self.category_icon_map = TagsIcons(iconmap) + self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags'] self.drag_drop_finished = drag_drop_finished diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 69dd7ae636..dbc871026e 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -22,6 +22,20 @@ class TagsIcons(dict): raise ValueError('Missing category icon [%s]'%a) self[a] = icon_dict[a] +category_icon_map = { + 'authors' : 'user_profile.png', + 'series' : 'series.png', + 'formats' : 'book.png', + 'publisher' : 'publisher.png', + 'rating' : 'rating.png', + 'news' : 'news.png', + 'tags' : 'tags.png', + ':custom' : 'column.png', + ':user' : 'drawer.png', + 'search' : 'search.png' + } + + class FieldMetadata(dict): ''' key: the key to the dictionary is: From 8a6e6c07e7b1d9edee0cc1aa9c775849d9e544aa Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 16 Oct 2010 15:28:25 +0100 Subject: [PATCH 109/140] Get the icons --- src/calibre/library/server/browse.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index dc8494dc13..e4d001c959 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -18,6 +18,7 @@ from calibre.utils.filenames import ascii_filename from calibre.utils.config import prefs from calibre.library.comments import comments_to_html from calibre.library.server import custom_fields_to_display +from calibre.library.field_metadata import category_icon_map def render_book_list(ids, suffix=''): # {{{ pages = [] @@ -279,6 +280,16 @@ class BrowseServer(object): continue if meta['is_custom'] and category not in displayed_custom_fields: continue + # get the icon files + if category in category_icon_map: + icon = I(category_icon_map[category]) + elif meta['is_custom']: + icon = I(category_icon_map[':custom']) + elif meta['kind'] == 'user': + icon = I(category_icon_map[':user']) + else: + icon = None # shouldn't get here + cats.append((meta['name'], category)) cats = ['
  • {0}/browse/category/{1}
  • '\ .format(xml(x, True), xml(quote(y)), xml(_('Browse books by'))) From a06618d4b1229a5b0636d06cc036df2bd10bfdf2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 08:52:28 -0600 Subject: [PATCH 110/140] ... --- resources/content_server/browse/browse.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index a22386b7a0..bd713625b4 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -193,6 +193,7 @@ h2.library_name { margin: 0.75em; padding: 0.75em; cursor: pointer; + font-size: larger; border-radius: 15px; -moz-border-radius: 15px; -webkit-border-radius: 15px; @@ -201,6 +202,7 @@ h2.library_name { .toplevel li img { vertical-align: middle; + margin-right: 2em; } .toplevel li:hover { From 36c0079061683ef8b781c723dce32df7a6882d83 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 08:58:30 -0600 Subject: [PATCH 111/140] /browse: Fix sorting in book list views when server is run without --develop --- src/calibre/library/server/browse.py | 73 ++++++++++++++-------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index d1476b34e1..d3cfc0e84a 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -200,46 +200,45 @@ class BrowseServer(object): # Templates {{{ def browse_template(self, sort, category=True, initial_search=''): - def generate(): - scn = 'calibre_browse_server_sort_' + if not hasattr(self, '__browse_template__') or \ + self.opts.develop: + self.__browse_template__ = \ + P('content_server/browse/browse.html', data=True).decode('utf-8') - if category: - sort_opts = [('rating', _('Average rating')), ('name', - _('Name')), ('popularity', _('Popularity'))] - scn += 'category' - else: - scn += 'list' - fm = self.db.field_metadata - sort_opts, added = [], set([]) - for x in fm.sortable_field_keys(): - n = fm[x]['name'] - if n not in added: - added.add(n) - sort_opts.append((x, n)) + ans = self.__browse_template__ + scn = 'calibre_browse_server_sort_' - ans = P('content_server/browse/browse.html', - data=True).decode('utf-8') - ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':')) - ans = ans.replace('{sort_cookie_name}', scn) - opts = ['' % ( - 'selected="selected" ' if k==sort else '', - xml(k), xml(n), ) for k, n in - sorted(sort_opts, key=operator.itemgetter(1)) if k and n] - ans = ans.replace('{sort_select_options}', ('\n'+' '*20).join(opts)) - lp = self.db.library_path - if isbytestring(lp): - lp = force_unicode(lp, filesystem_encoding) - if isinstance(ans, unicode): - ans = ans.encode('utf-8') - ans = ans.replace('{library_name}', xml(os.path.basename(lp))) - ans = ans.replace('{library_path}', xml(lp, True)) - ans = ans.replace('{initial_search}', initial_search) - return ans + if category: + sort_opts = [('rating', _('Average rating')), ('name', + _('Name')), ('popularity', _('Popularity'))] + scn += 'category' + else: + scn += 'list' + fm = self.db.field_metadata + sort_opts, added = [], set([]) + for x in fm.sortable_field_keys(): + n = fm[x]['name'] + if n not in added: + added.add(n) + sort_opts.append((x, n)) + + ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':')) + ans = ans.replace('{sort_cookie_name}', scn) + opts = ['' % ( + 'selected="selected" ' if k==sort else '', + xml(k), xml(n), ) for k, n in + sorted(sort_opts, key=operator.itemgetter(1)) if k and n] + ans = ans.replace('{sort_select_options}', ('\n'+' '*20).join(opts)) + lp = self.db.library_path + if isbytestring(lp): + lp = force_unicode(lp, filesystem_encoding) + if isinstance(ans, unicode): + ans = ans.encode('utf-8') + ans = ans.replace('{library_name}', xml(os.path.basename(lp))) + ans = ans.replace('{library_path}', xml(lp, True)) + ans = ans.replace('{initial_search}', initial_search) + return ans - if self.opts.develop: - return generate() - if not hasattr(self, '__browse_template__'): - self.__browse_template__ = generate() return self.__browse_template__ @property From 40808b4a1b5e257ba1465b55e251931c9bae5ad4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 09:04:38 -0600 Subject: [PATCH 112/140] /browse: Preserve aspect ratio when resizing category icons --- src/calibre/library/server/browse.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index d3cfc0e84a..5e24bb1bc7 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -12,7 +12,8 @@ from binascii import hexlify, unhexlify import cherrypy from calibre.constants import filesystem_encoding -from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml +from calibre import isbytestring, force_unicode, fit_image, \ + prepare_string_for_xml as xml from calibre.utils.ordered_dict import OrderedDict from calibre.utils.filenames import ascii_filename from calibre.utils.config import prefs @@ -267,7 +268,10 @@ class BrowseServer(object): raise cherrypy.HTTPError(404, 'no icon named: %r'%name) img = Image() img.load(data) - img.size = (48, 48) + width, height = img.size + scaled, width, height = fit_image(width, height, 48, 48) + if scaled: + img.size = (width, height) cherrypy.response.headers['Content-Type'] = 'image/png' cherrypy.response.headers['Last-Modified'] = self.last_modified(self.build_time) @@ -277,7 +281,7 @@ class BrowseServer(object): categories = self.categories_cache() category_meta = self.db.field_metadata cats = [ - (_('Newest'), 'newest', 'blank.png'), + (_('Newest'), 'newest', 'forward.png'), ] def getter(x): From 9181d87588a0fc3fefd7ab0ac57bf04ea7a0c778 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 09:07:36 -0600 Subject: [PATCH 113/140] /browse: Cache top level resized icons --- src/calibre/library/server/browse.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 5e24bb1bc7..4557ed8773 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -262,20 +262,25 @@ class BrowseServer(object): # Catalogs {{{ def browse_icon(self, name='blank.png'): - try: - data = I(name, data=True) - except: - raise cherrypy.HTTPError(404, 'no icon named: %r'%name) - img = Image() - img.load(data) - width, height = img.size - scaled, width, height = fit_image(width, height, 48, 48) - if scaled: - img.size = (width, height) cherrypy.response.headers['Content-Type'] = 'image/png' cherrypy.response.headers['Last-Modified'] = self.last_modified(self.build_time) - return img.export('png') + if not hasattr(self, '__browse_icon_cache__'): + self.__browse_icon_cache__ = {} + if name not in self.__browse_icon_cache__: + try: + data = I(name, data=True) + except: + raise cherrypy.HTTPError(404, 'no icon named: %r'%name) + img = Image() + img.load(data) + width, height = img.size + scaled, width, height = fit_image(width, height, 48, 48) + if scaled: + img.size = (width, height) + + self.__browse_icon_cache__[name] = img.export('png') + return self.__browse_icon_cache__[name] def browse_toplevel(self): categories = self.categories_cache() From 0acd342fef67a015db8edab21f713674697ca428 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 16 Oct 2010 16:11:27 +0100 Subject: [PATCH 114/140] Filter ondevice, comments, and non-displayed custom fields from sort box --- src/calibre/library/server/browse.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index d3cfc0e84a..5881bc9207 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -216,7 +216,14 @@ class BrowseServer(object): scn += 'list' fm = self.db.field_metadata sort_opts, added = [], set([]) + displayed_custom_fields = custom_fields_to_display(self.db) for x in fm.sortable_field_keys(): + if x == 'ondevice': + continue + if fm[x]['is_custom'] and x not in displayed_custom_fields: + continue + if x == 'comments' or fm[x]['datatype'] == 'comments': + continue n = fm[x]['name'] if n not in added: added.add(n) From 1aab3395cccf8e621cf37f1ce9ed723565e9c474 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 09:18:38 -0600 Subject: [PATCH 115/140] ... --- resources/content_server/browse/browse.css | 5 ++++- src/calibre/library/server/browse.py | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index bd713625b4..6f740875f4 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -214,7 +214,10 @@ h2.library_name { } -.toplevel li span { display: none } +.toplevel li span.url { display: none } +.toplevel li span.label { +} + /* }}} */ diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 4557ed8773..53c4771e38 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -315,8 +315,9 @@ class BrowseServer(object): icon = 'blank.png' cats.append((meta['name'], category, icon)) - cats = [('
  • {0} {0}' - '/browse/category/{1}
  • ') + cats = [('
  • {0}' + '{0}' + '/browse/category/{1}
  • ') .format(xml(x, True), xml(quote(y)), xml(_('Browse books by')), src='/browse/icon/'+z) for x, y, z in cats] From fa9f2134c0c216e2b969cca8a525aa54a7cb3cd5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 09:56:58 -0600 Subject: [PATCH 116/140] /browse: A grid layout for the toplevel page --- resources/content_server/browse/browse.css | 10 +++++++++- resources/content_server/browse/browse.js | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 6f740875f4..80f71db55e 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -187,6 +187,9 @@ h2.library_name { list-style-type: none; margin: 0; padding: 0; + margin-left: auto; + margin-right: auto; + display: block; } .toplevel li { @@ -194,15 +197,20 @@ h2.library_name { padding: 0.75em; cursor: pointer; font-size: larger; + float: left; border-radius: 15px; -moz-border-radius: 15px; -webkit-border-radius: 15px; + display: inline; + width: 250px; + height: 48px; + overflow: hidden; } .toplevel li img { vertical-align: middle; - margin-right: 2em; + margin-right: 1em; } .toplevel li:hover { diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 367b8341d9..8637ef2c96 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -81,6 +81,12 @@ function toplevel() { var href = $(this).children("span").html(); window.location = href; }); + + + var last = $(".toplevel li").last(); + var bottom = last.position().top + last.height(); + $("#main").height(Math.max(680, bottom+150)); + } // }}} From 144fd1ddf3f31473c1ed41406106053dbda235d0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 09:59:14 -0600 Subject: [PATCH 117/140] ... --- resources/content_server/browse/browse.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 8637ef2c96..5505c5bccc 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -78,7 +78,7 @@ function toplevel() { $(".sort_select").hide(); $(".toplevel li").click(function() { - var href = $(this).children("span").html(); + var href = $(this).children("span.url").text(); window.location = href; }); From d4d2f2b2e38e1fe6a59c814c395dadaa6b6d9bd4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 10:14:22 -0600 Subject: [PATCH 118/140] /browse: Removing quoting and fix the News category --- src/calibre/library/server/browse.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index aae3067ba0..d17733dd70 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -6,7 +6,6 @@ __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' import operator, os, json -from urllib import quote from binascii import hexlify, unhexlify import cherrypy @@ -136,7 +135,7 @@ def get_category_items(category, items, db, datatype): # {{{ desc += '[' + _('%d books')%i.count + ']' href = '/browse/matches/%s/%s'%(category, id_) return templ.format(xml(name), rating, - xml(desc), xml(quote(href)), rstring) + xml(desc), xml(href), rstring) items = list(map(item, items)) return '\n'.join(['
    '] + items + ['
    ']) @@ -325,7 +324,7 @@ class BrowseServer(object): cats = [('
  • {0}' '{0}' '/browse/category/{1}
  • ') - .format(xml(x, True), xml(quote(y)), xml(_('Browse books by')), + .format(xml(x, True), xml(y), xml(_('Browse books by')), src='/browse/icon/'+z) for x, y, z in cats] @@ -492,7 +491,10 @@ class BrowseServer(object): ids = list(self.db.data.iterallids()) hide_sort = 'true' else: - ids = self.db.get_books_for_category(category, cid) + q = category + if q == 'news': + q = 'tags' + ids = self.db.get_books_for_category(q, cid) items = [self.db.data._data[x] for x in ids] if category == 'newest': From 7f3212585a7b9da896663c0a3cb5e2b835408c55 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 10:21:26 -0600 Subject: [PATCH 119/140] /browse: Fix handling of user categories --- src/calibre/library/server/browse.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index d17733dd70..9d483d1b4e 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -133,7 +133,10 @@ def get_category_items(category, items, db, datatype): # {{{ desc = '' if i.count > 0: desc += '[' + _('%d books')%i.count + ']' - href = '/browse/matches/%s/%s'%(category, id_) + q = i.category + if not q: + q = category + href = '/browse/matches/%s/%s'%(q, id_) return templ.format(xml(name), rating, xml(desc), xml(href), rstring) From 40d35ff9f7b9c3522112960bf7d5c15c29213ffd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 10:43:24 -0600 Subject: [PATCH 120/140] /browse: Make the sort cookies session cookies and remove formats and sort from list of sortable fields --- resources/content_server/browse/browse.js | 2 +- src/calibre/library/server/browse.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 5505c5bccc..99755f5f99 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -55,7 +55,7 @@ function init_sort_combobox() { selectedList: 1, click: function(event, ui){ $(this).multiselect("close"); - cookie(sort_cookie_name, ui.value, {expires: 365}); + cookie(sort_cookie_name, ui.value); window.location.reload(); } }); diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 9d483d1b4e..c45a47932d 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -221,7 +221,7 @@ class BrowseServer(object): sort_opts, added = [], set([]) displayed_custom_fields = custom_fields_to_display(self.db) for x in fm.sortable_field_keys(): - if x == 'ondevice': + if x in ('ondevice', 'formats', 'sort'): continue if fm[x]['is_custom'] and x not in displayed_custom_fields: continue From 9beeea57b116981bcccad9aa1ce594f990f28e59 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 10:45:01 -0600 Subject: [PATCH 121/140] /browse: Make series sorting descending --- src/calibre/library/server/browse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index c45a47932d..f3a319f173 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -464,7 +464,8 @@ class BrowseServer(object): sort = 'title' self.sort(items, 'title', True) if sort != 'title': - ascending = fm[sort]['datatype'] not in ('rating', 'datetime') + ascending = fm[sort]['datatype'] not in ('rating', 'datetime', + 'series') self.sort(items, sort, ascending) return sort From eb83ea3b9f0f09028b82e4dfedbf76b0b9b94a54 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 12:07:31 -0600 Subject: [PATCH 122/140] /browse: Proper resizing of top level grid view --- resources/content_server/browse/browse.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 99755f5f99..777a285878 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -74,6 +74,14 @@ function init() { } // Top-level feed {{{ + +function toplevel_layout() { + var last = $(".toplevel li").last(); + var title = $('.toplevel h3').first(); + var bottom = last.position().top + last.height() - title.position().top; + $("#main").height(Math.max(200, bottom)); +} + function toplevel() { $(".sort_select").hide(); @@ -82,10 +90,8 @@ function toplevel() { window.location = href; }); - - var last = $(".toplevel li").last(); - var bottom = last.position().top + last.height(); - $("#main").height(Math.max(680, bottom+150)); + toplevel_layout(); + $(window).resize(toplevel_layout); } // }}} From 0f7478206258a7fbbaa47367d323f189dc1a2950 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 12:08:15 -0600 Subject: [PATCH 123/140] ... --- resources/content_server/browse/browse.js | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index 777a285878..b19b1c0804 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -1,5 +1,35 @@ // Cookies {{{ +/** + * Create a cookie with the given name and value and other optional parameters. + * + * @example $.cookie('the_cookie', 'the_value'); + * @desc Set the value of a cookie. + * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); + * @desc Create a cookie with all available options. + * @example $.cookie('the_cookie', 'the_value'); + * @desc Create a session cookie. + * @example $.cookie('the_cookie', null); + * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain + * used when the cookie was set. + * + * @param String name The name of the cookie. + * @param String value The value of the cookie. + * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. + * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. + * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. + * If set to null or omitted, the cookie will be a session cookie and will not be retained + * when the the browser exits. + * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). + * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). + * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will + * require a secure protocol (like HTTPS). + * @type undefined + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ function cookie(name, value, options) { if (typeof value != 'undefined') { // name and value given, set cookie From a490b3fbed8549e61107d7ab88fa69fddd8d6ad9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 12:52:04 -0600 Subject: [PATCH 124/140] /browse: When browsing by series, force sorting to alway be by series --- src/calibre/library/server/browse.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index f3a319f173..7331442934 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -477,14 +477,17 @@ class BrowseServer(object): if category not in categories and category != 'newest': raise cherrypy.HTTPError(404, 'category not found') + fm = self.db.field_metadata try: - category_name = self.db.field_metadata[category]['name'] + category_name = fm[category]['name'] + dt = fm[category]['datatype'] except: if category != 'newest': raise category_name = _('Newest') + dt = None - hide_sort = 'false' + hide_sort = 'true' if dt == 'series' else 'false' if category == 'search': which = unhexlify(cid) try: @@ -503,6 +506,8 @@ class BrowseServer(object): items = [self.db.data._data[x] for x in ids] if category == 'newest': list_sort = 'timestamp' + if dt == 'series': + list_sort = category sort = self.browse_sort_book_list(items, list_sort) ids = [x[0] for x in items] html = render_book_list(ids, suffix=_('in') + ' ' + category_name) From c5ebc3e5a3679a9dc327df9aa23e1f45df997d54 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 13:23:24 -0600 Subject: [PATCH 125/140] /browse: Add permalink for every book --- resources/content_server/browse/browse.css | 4 +-- resources/content_server/browse/browse.js | 14 +++++++- resources/content_server/browse/summary.html | 1 + src/calibre/library/server/browse.py | 37 +++++++++++++++----- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 80f71db55e..92ed4c3ce6 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -417,12 +417,12 @@ h2.library_name { margin-bottom: 2ex; } -#book_details_dialog .details a { +.details .right .formats a { color: blue; text-decoration: none; } -#book_details_dialog .details a:hover { +.details .right .formats a:hover { color: red; } diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js index b19b1c0804..29b84ac2d7 100644 --- a/resources/content_server/browse/browse.js +++ b/resources/content_server/browse/browse.js @@ -235,8 +235,10 @@ function load_page(elem) { elem.show(); } +function hidesort() {$("#content > .sort_select").hide();} + function booklist(hide_sort) { - if (hide_sort) $("#content > .sort_select").hide(); + if (hide_sort) hidesort(); var test = $("#booklist #page0").html(); if (!test) { $("#booklist").html(render_error("No books found")); @@ -275,3 +277,13 @@ function show_details(a_dom) { } // }}} + +function book() { + hidesort(); + $('.details .left img').load(function() { + var img = $('.details .left img'); + var height = $('#main').height(); + height = Math.max(height, img.height() + 100); + $('#main').height(height); + }); +} diff --git a/resources/content_server/browse/summary.html b/resources/content_server/browse/summary.html index 4e9c9d2a77..de175d3b53 100644 --- a/resources/content_server/browse/summary.html +++ b/resources/content_server/browse/summary.html @@ -8,6 +8,7 @@ {stars} {series} {details} + {permalink}
    {title}
    {authors}
    diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 7331442934..247e6945e6 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -197,6 +197,8 @@ class BrowseServer(object): self.browse_search) connect('browse_details', base_href+'/details/{id}', self.browse_details) + connect('browse_book', base_href+'/book/{id}', + self.browse_book) connect('browse_category_icon', base_href+'/icon/{name}', self.browse_icon) @@ -589,23 +591,19 @@ class BrowseServer(object): args['series'] = args['series'] args['details'] = xml(_('Details'), True) args['details_tt'] = xml(_('Show book details'), True) + args['permalink'] = xml(_('Permalink'), True) + args['permalink_tt'] = xml(_('A permanent link to this book'), True) summs.append(self.browse_summary_template.format(**args)) return json.dumps('\n'.join(summs), ensure_ascii=False) - @Endpoint(mimetype='application/json; charset=utf-8') - def browse_details(self, id=None): - try: - id_ = int(id) - except: - raise cherrypy.HTTPError(404, 'invalid id: %r'%id) - + def browse_render_details(self, id_): try: mi = self.db.get_metadata(id_, index_is_id=True) except: - ans = _('This book has been deleted') + return _('This book has been deleted') else: args, fmt, fmts, fname = self.browse_get_book_args(mi, id_) args['formats'] = '' @@ -646,13 +644,34 @@ class BrowseServer(object): u'
    %s
    ') % (xml(c[0]), c[1]) for c in comments] comments = u'
    %s
    '%('\n\n'.join(comments)) - ans = self.browse_details_template.format(id=id_, + + return self.browse_details_template.format(id=id_, title=xml(mi.title, True), fields=fields, formats=args['formats'], comments=comments) + @Endpoint(mimetype='application/json; charset=utf-8') + def browse_details(self, id=None): + try: + id_ = int(id) + except: + raise cherrypy.HTTPError(404, 'invalid id: %r'%id) + + ans = self.browse_render_details(id_) + return json.dumps(ans, ensure_ascii=False) + @Endpoint() + def browse_book(self, id=None, category_sort=None): + try: + id_ = int(id) + except: + raise cherrypy.HTTPError(404, 'invalid id: %r'%id) + + ans = self.browse_render_details(id_) + return self.browse_template('').format( + title='', script='book();', main=ans) + # }}} From 1022ddd1014601649b4688adcd70e5b668418a4f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 17:02:49 -0600 Subject: [PATCH 126/140] Linux device mounting: Use udisks, if it is available, to mount devices, so that I no longer have to hear bug reports from users using distro packages that have crippled calibre-mount-helper. You can turn off udisks by setting the environment variable CALIBRE_DISABLE_UDISKS=1 --- src/calibre/devices/udisks.py | 88 +++++++++++++++++++++++++++++ src/calibre/devices/usbms/device.py | 25 ++++---- src/calibre/manual/customize.rst | 1 + 3 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 src/calibre/devices/udisks.py diff --git a/src/calibre/devices/udisks.py b/src/calibre/devices/udisks.py new file mode 100644 index 0000000000..ba26c2b56c --- /dev/null +++ b/src/calibre/devices/udisks.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import dbus +import os + +def node_mountpoint(node): + + def de_mangle(raw): + return raw.replace('\\040', ' ').replace('\\011', '\t').replace('\\012', + '\n').replace('\\0134', '\\') + + for line in open('/proc/mounts').readlines(): + line = line.split() + if line[0] == node: + return de_mangle(line[1]) + return None + + +class UDisks(object): + + def __init__(self): + if os.environ.get('CALIBRE_DISABLE_UDISKS', False): + raise Exception('User has aborted use of UDISKS') + self.bus = dbus.SystemBus() + self.main = dbus.Interface(self.bus.get_object('org.freedesktop.UDisks', + '/org/freedesktop/UDisks'), 'org.freedesktop.UDisks') + + def device(self, device_node_path): + devpath = self.main.FindDeviceByDeviceFile(device_node_path) + return dbus.Interface(self.bus.get_object('org.freedesktop.UDisks', + devpath), 'org.freedesktop.UDisks.Device') + + def mount(self, device_node_path): + d = self.device(device_node_path) + try: + return unicode(d.FilesystemMount('', + ['auth_no_user_interaction', 'rw', 'noexec', 'nosuid', + 'sync', 'nodev', 'uid=1000', 'gid=1000'])) + except: + # May be already mounted, check + mp = node_mountpoint(str(device_node_path)) + if mp is None: + raise + return mp + + def unmount(self, device_node_path): + d = self.device(device_node_path) + d.FilesystemUnmount(['force']) + + def eject(self, device_node_path): + parent = device_node_path + while parent[-1] in '0123456789': + parent = parent[:-1] + devices = [str(x) for x in self.main.EnumerateDeviceFiles()] + for d in devices: + if d.startswith(parent) and d != parent: + try: + self.unmount(d) + except: + import traceback + print 'Failed to unmount:', d + traceback.print_exc() + d = self.device(parent) + d.DriveEject([]) + +def mount(node_path): + u = UDisks() + u.mount(node_path) + +def eject(node_path): + u = UDisks() + u.eject(node_path) + +if __name__ == '__main__': + import sys + dev = sys.argv[1] + print 'Testing with node', dev + u = UDisks() + print 'Mounted at:', u.mount(dev) + print 'Ejecting' + u.eject(dev) + + diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 6fcfb9e7f0..6f938cbcbd 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -530,16 +530,8 @@ class Device(DeviceConfig, DevicePlugin): return drives def node_mountpoint(self, node): - - def de_mangle(raw): - return raw.replace('\\040', ' ').replace('\\011', '\t').replace('\\012', - '\n').replace('\\0134', '\\') - - for line in open('/proc/mounts').readlines(): - line = line.split() - if line[0] == node: - return de_mangle(line[1]) - return None + from calibre.devices.udisks import node_mountpoint + return node_mountpoint(node) def find_largest_partition(self, path): node = path.split('/')[-1] @@ -585,6 +577,13 @@ class Device(DeviceConfig, DevicePlugin): label += ' (%d)'%extra def do_mount(node, label): + try: + from calibre.devices.udisks import mount + mount(node) + return 0 + except: + pass + cmd = 'calibre-mount-helper' if getattr(sys, 'frozen_path', False): cmd = os.path.join(sys.frozen_path, cmd) @@ -617,6 +616,7 @@ class Device(DeviceConfig, DevicePlugin): if not mp.endswith('/'): mp += '/' self._linux_mount_map[main] = mp self._main_prefix = mp + self._linux_main_device_node = main cards = [(carda, '_card_a_prefix', 'carda'), (cardb, '_card_b_prefix', 'cardb')] for card, prefix, typ in cards: @@ -732,6 +732,11 @@ class Device(DeviceConfig, DevicePlugin): pass def eject_linux(self): + try: + from calibre.devices.udisks import eject + return eject(self._linux_main_device_node) + except: + pass drives = self.find_device_nodes() for drive in drives: if drive: diff --git a/src/calibre/manual/customize.rst b/src/calibre/manual/customize.rst index c35defc0b0..e0f799f572 100644 --- a/src/calibre/manual/customize.rst +++ b/src/calibre/manual/customize.rst @@ -24,6 +24,7 @@ Environment variables * ``CALIBRE_OVERRIDE_DATABASE_PATH`` - allows you to specify the full path to metadata.db. Using this variable you can have metadata.db be in a location other than the library folder. Useful if your library folder is on a networked drive that does not support file locking. * ``CALIBRE_DEVELOP_FROM`` - Used to run from a calibre development environment. See :ref:`develop`. * ``CALIBRE_OVERRIDE_LANG`` - Used to force the language used by the interface (ISO 639 language code) + * ``CALIBRE_DISABLE_UDISKS`` - Used to disable the use of udisks for mounting/ejecting. Set it to 1 to use calibre-mount-helper instead. * ``SYSFS_PATH`` - Use if sysfs is mounted somewhere other than /sys * ``http_proxy`` - Used on linux to specify an HTTP proxy From c2d0a57a4187bf32de5b669e922f1efc9c6c2907 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 20:09:46 -0600 Subject: [PATCH 127/140] EPUB Input: Make parsing of toc.ncx more robust. Fixes #7170 (Some epub hierarchical tables of contents are not interpreted correctly) --- src/calibre/ebooks/metadata/toc.py | 74 +++++++++++++++++------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/src/calibre/ebooks/metadata/toc.py b/src/calibre/ebooks/metadata/toc.py index 8c6f3f6baf..0ed527d26a 100644 --- a/src/calibre/ebooks/metadata/toc.py +++ b/src/calibre/ebooks/metadata/toc.py @@ -2,7 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' -import os, glob, re +import os, glob, re, functools from urlparse import urlparse from urllib import unquote from uuid import uuid4 @@ -11,7 +11,7 @@ from lxml import etree from lxml.builder import ElementMaker from calibre.constants import __appname__, __version__ -from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, BeautifulSoup +from calibre.ebooks.BeautifulSoup import BeautifulSoup from calibre.ebooks.chardet import xml_to_unicode NCX_NS = "http://www.daisy.org/z3986/2005/ncx/" @@ -26,14 +26,6 @@ E = ElementMaker(namespace=NCX_NS, nsmap=NSMAP) C = ElementMaker(namespace=CALIBRE_NS, nsmap=NSMAP) -class NCXSoup(BeautifulStoneSoup): - - NESTABLE_TAGS = {'navpoint':[]} - - def __init__(self, raw): - BeautifulStoneSoup.__init__(self, raw, - convertEntities=BeautifulSoup.HTML_ENTITIES, - selfClosingTags=['meta', 'content']) class TOC(list): @@ -166,40 +158,60 @@ class TOC(list): def read_ncx_toc(self, toc): self.base_path = os.path.dirname(toc) - raw = xml_to_unicode(open(toc, 'rb').read(), assume_utf8=True)[0] - soup = NCXSoup(raw) + raw = xml_to_unicode(open(toc, 'rb').read(), assume_utf8=True, + strip_encoding_pats=True)[0] + root = etree.fromstring(raw, parser=etree.XMLParser(recover=True, + no_network=True)) + xpn = {'re': 'http://exslt.org/regular-expressions'} + XPath = functools.partial(etree.XPath, namespaces=xpn) + + def get_attr(node, default=None, attr='playorder'): + for name, val in node.attrib.items(): + if name and val and name.lower().endswith(attr): + return val + return default + + nl_path = XPath('./*[re:match(local-name(), "navlabel$", "i")]') + txt_path = XPath('./*[re:match(local-name(), "text$", "i")]') + content_path = XPath('./*[re:match(local-name(), "content$", "i")]') + np_path = XPath('./*[re:match(local-name(), "navpoint$", "i")]') def process_navpoint(np, dest): - play_order = np.get('playOrder', None) - if play_order is None: - play_order = int(np.get('playorder', 1)) + try: + play_order = int(get_attr(np, 1)) + except: + play_order = 1 href = fragment = text = None - nl = np.find(re.compile('navlabel')) - if nl is not None: + nl = nl_path(np) + if nl: + nl = nl[0] text = u'' - for txt in nl.findAll(re.compile('text')): - text += u''.join([unicode(s) for s in txt.findAll(text=True)]) - content = np.find(re.compile('content')) - if content is None or not content.has_key('src') or not txt: + for txt in txt_path(nl): + text += etree.tostring(txt, method='text', + encoding=unicode, with_tail=False) + content = content_path(np) + if not content or not text: + return + content = content[0] + src = get_attr(content, attr='src') + if src is None: return - purl = urlparse(unquote(content['src'])) + purl = urlparse(unquote(content.get('src'))) href, fragment = purl[2], purl[5] nd = dest.add_item(href, fragment, text) nd.play_order = play_order - for c in np: - if 'navpoint' in getattr(c, 'name', ''): - process_navpoint(c, nd) + for c in np_path(np): + process_navpoint(c, nd) - nm = soup.find(re.compile('navmap')) - if nm is None: + nm = XPath('//*[re:match(local-name(), "navmap$", "i")]')(root) + if not nm: raise ValueError('NCX files must have a element.') + nm = nm[0] - for elem in nm: - if 'navpoint' in getattr(elem, 'name', ''): - process_navpoint(elem, self) - + for child in np_path(nm): + process_navpoint(child, self) def read_html_toc(self, toc): self.base_path = os.path.dirname(toc) From 3a5de491d08eeeb83b65c22e772e235d2dbce579 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Oct 2010 22:13:39 -0600 Subject: [PATCH 128/140] Malaysian Mirror by Tony Stegall --- resources/recipes/malaysian_mirror.recipe | 89 +++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 resources/recipes/malaysian_mirror.recipe diff --git a/resources/recipes/malaysian_mirror.recipe b/resources/recipes/malaysian_mirror.recipe new file mode 100644 index 0000000000..e61538431a --- /dev/null +++ b/resources/recipes/malaysian_mirror.recipe @@ -0,0 +1,89 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__author__ = 'Tony Stegall' +__copyright__ = '2010, Tony Stegall or Tonythebookworm on mobiread.com' +__version__ = '1' +__date__ = '16, October 2010' +__docformat__ = 'English' + + + +from calibre.web.feeds.news import BasicNewsRecipe + +class MalaysianMirror(BasicNewsRecipe): + title = 'MalaysianMirror' + __author__ = 'Tonythebookworm' + description = 'The Pulse of the Nation' + language = 'en' + no_stylesheets = True + publisher = 'Tonythebookworm' + category = 'news' + use_embedded_content= False + no_stylesheets = True + oldest_article = 24 + + remove_javascript = True + remove_empty_feeds = True + conversion_options = {'linearize_tables' : True} + extra_css = ''' + #content_heading{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + + td{text-align:right; font-size:small;margin-top:0px;margin-bottom: 0px;} + + #content_body{font-family:Helvetica,Arial,sans-serif;font-size:small;} + ''' + + keep_only_tags = [dict(name='table', attrs={'class':['contentpaneopen']}) + ] + remove_tags = [dict(name='table', attrs={'class':['buttonheading']})] + ####################################################################################################################### + + + max_articles_per_feed = 10 + + ''' + Make a variable that will hold the url for the main site because our links do not include the index + ''' + + INDEX = 'http://www.malaysianmirror.com' + + + + + def parse_index(self): + feeds = [] + for title, url in [ + (u"Media Buzz", u"http://www.malaysianmirror.com/media-buzz-front"), + (u"Life Style", u"http://www.malaysianmirror.com/lifestylefront"), + (u"Features", u"http://www.malaysianmirror.com/featurefront"), + + + ]: + articles = self.make_links(url) + if articles: + feeds.append((title, articles)) + return feeds + + def make_links(self, url): + title = 'Temp' + current_articles = [] + soup = self.index_to_soup(url) + # print 'The soup is: ', soup + for item in soup.findAll('div', attrs={'class':'contentheading'}): + #print 'item is: ', item + link = item.find('a') + #print 'the link is: ', link + if link: + url = self.INDEX + link['href'] + title = self.tag_to_string(link) + #print 'the title is: ', title + #print 'the url is: ', url + #print 'the title is: ', title + current_articles.append({'title': title, 'url': url, 'description':'', 'date':''}) # append all this + return current_articles + + def preprocess_html(self, soup): + for item in soup.findAll(attrs={'style':True}): + del item['style'] + return soup + From 9c0ac34187901475b0c61bb4db885994db09594f Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 17 Oct 2010 05:30:09 +0100 Subject: [PATCH 129/140] Extend functionality of template function 'lookup' --- src/calibre/utils/formatter.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 5e2cb6535a..b7ec14ce7a 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -22,11 +22,21 @@ class TemplateFormatter(string.Formatter): self.book = None self.kwargs = None - def _lookup(self, val, field_if_set, field_not_set): - if val: - return self.vformat('{'+field_if_set.strip()+'}', [], self.kwargs) - else: - return self.vformat('{'+field_not_set.strip()+'}', [], self.kwargs) + def _lookup(self, val, *args): + if len(args) == 2: + if val: + return self.vformat('{'+args[0].strip()+'}', [], self.kwargs) + else: + return self.vformat('{'+args[1].strip()+'}', [], self.kwargs) + if (len(args) % 2) != 1: + raise ValueError(_('lookup requires either 2 or an odd number of arguments')) + i = 0 + while i < len(args): + if i + 1 >= len(args): + return self.vformat('{' + args[i].strip() + '}', [], self.kwargs) + if re.search(args[i], val): + return self.vformat('{'+args[i+1].strip() + '}', [], self.kwargs) + i += 2 def _test(self, val, value_if_set, value_not_set): if val: @@ -41,6 +51,8 @@ class TemplateFormatter(string.Formatter): return value_if_not def _switch(self, val, *args): + if (len(args) % 2) != 1: + raise ValueError(_('switch requires an odd number of arguments')) i = 0 while i < len(args): if i + 1 >= len(args): @@ -73,7 +85,7 @@ class TemplateFormatter(string.Formatter): 'capitalize' : (0, lambda s,x: x.capitalize()), 'contains' : (3, _contains), 'ifempty' : (1, _ifempty), - 'lookup' : (2, _lookup), + 'lookup' : (-1, _lookup), 're' : (2, _re), 'shorten' : (3, _shorten), 'switch' : (-1, _switch), @@ -129,9 +141,9 @@ class TemplateFormatter(string.Formatter): (func[0] > 0 and func[0] != len(args)): raise ValueError('Incorrect number of arguments for function '+ fmt[0:p]) if func[0] == 0: - val = func[1](self, val) + val = func[1](self, val).strip() else: - val = func[1](self, val, *args) + val = func[1](self, val, *args).strip() if val: val = string.Formatter.format_field(self, val, dispfmt) if not val: From d4f409646a4a8556741957c21f31232b6adb1ac8 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 17 Oct 2010 05:36:10 +0100 Subject: [PATCH 130/140] 1) change author_sort tweak documentation to note that the values must be recomputed 2) change 'lookup' documentation in the template faq --- resources/default_tweaks.py | 3 +++ src/calibre/manual/template_lang.rst | 4 ++-- src/calibre/utils/formatter.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 48845da920..86921886ad 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -25,6 +25,9 @@ series_index_auto_increment = 'next' # copy : copy author to author_sort without modification # comma : use 'copy' if there is a ',' in the name, otherwise use 'invert' # nocomma : "fn ln" -> "ln fn" (without the comma) +# When this tweak is changed, the author_sort values stored with each author +# must be recomputed by right-clicking on an author in the left-hand tags pane, +# selecting 'manage authors', and pressing 'Recalculate all author sort values'. author_sort_copy_method = 'invert' diff --git a/src/calibre/manual/template_lang.rst b/src/calibre/manual/template_lang.rst index b731dfe26e..e1eb876cb7 100644 --- a/src/calibre/manual/template_lang.rst +++ b/src/calibre/manual/template_lang.rst @@ -122,7 +122,7 @@ The functions available are: * ``switch(pattern, value, pattern, value, ..., else_value)`` -- for each ``pattern, value`` pair, checks if the field matches the regular expression ``pattern`` and if so, returns that ``value``. If no ``pattern`` matches, then ``else_value`` is returned. You can have as many ``pattern, value`` pairs as you want. * ``re(pattern, replacement)`` -- return the field after applying the regular expression. All instances of `pattern` are replaced with `replacement`. As in all of |app|, these are python-compatible regular expressions. * ``shorten(left chars, middle text, right chars)`` -- Return a shortened version of the field, consisting of `left chars` characters from the beginning of the field, followed by `middle text`, followed by `right chars` characters from the end of the string. `Left chars` and `right chars` must be integers. For example, assume the title of the book is `Ancient English Laws in the Times of Ivanhoe`, and you want it to fit in a space of at most 15 characters. If you use ``{title:shorten(9,-,5)}``, the result will be `Ancient E-nhoe`. If the field's length is less than ``left chars`` + ``right chars`` + the length of ``middle text``, then the field will be used intact. For example, the title `The Dome` would not be changed. - * ``lookup(field if not empty, field if empty)`` -- like test, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths (more later). + * ``lookup(pattern, field, pattern, field, ..., else_field)`` -- like switch, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths (more later). Now, about using functions and formatting in the same field. Suppose you have an integer custom column called ``#myint`` that you want to see with leading zeros, as in ``003``. To do this, you would use a format of ``0>3s``. However, by default, if a number (integer or float) equals zero then the field produces the empty value, so zero values will produce nothing, not ``000``. If you really want to see ``000`` values, then you use both the format string and the ``ifempty`` function to change the empty value back to a zero. The field reference would be:: @@ -151,7 +151,7 @@ The lookup function lets us do even fancier processing. For example, assume that To accomplish this, we: 1. Create a composite field (call it AA) containing ``{series}/{series_index} - {title'}``. If the series is not empty, then this template will produce `series/series_index - title`. 2. Create a composite field (call it BB) containing ``{#genre:ifempty(Unknown)}/{author_sort}/{title}``. This template produces `genre/author_sort/title`, where an empty genre is replaced wuth `Unknown`. - 3. Set the save template to ``{series:lookup(AA,BB)}``. This template chooses composite field AA if series is not empty, and composite field BB if series is empty. We therefore have two completely different save paths, depending on whether or not `series` is empty. + 3. Set the save template to ``{series:lookup(.,AA,BB)}``. This template chooses composite field AA if series is not empty, and composite field BB if series is empty. We therefore have two completely different save paths, depending on whether or not `series` is empty. Templates and Plugboards ------------------------ diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index b7ec14ce7a..76c086cc58 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -23,7 +23,7 @@ class TemplateFormatter(string.Formatter): self.kwargs = None def _lookup(self, val, *args): - if len(args) == 2: + if len(args) == 2: # here for backwards compatibility if val: return self.vformat('{'+args[0].strip()+'}', [], self.kwargs) else: From 2ac585471492e0dd4c3a2d5863878b1b1f9a818e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 07:21:15 -0600 Subject: [PATCH 131/140] Fix #7195 (Height of Metadata Bulk Edit dialog) --- src/calibre/gui2/dialogs/metadata_bulk.ui | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 3897d6dbf9..8c60715ae2 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -7,7 +7,7 @@ 0 0 752 - 715 + 633 @@ -39,7 +39,7 @@ - 0 + 2 @@ -660,8 +660,8 @@ nothing should be put between the original text and the inserted text 0 0 - 122 - 34 + 726 + 334 @@ -682,19 +682,6 @@ nothing should be put between the original text and the inserted text - - - - Qt::Vertical - - - - 20 - 0 - - - - From 7020ce507edcf494d141697a1734081e6994c69b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 08:14:12 -0600 Subject: [PATCH 132/140] Fix #7198 (EPUB issue- Incorrect format for language in toc.ncx) --- src/calibre/ebooks/oeb/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index e85098e293..2d2945c26a 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -1908,6 +1908,7 @@ class OEBBook(object): def _to_ncx(self): lang = unicode(self.metadata.language[0]) + lang = lang.replace('_', '-') ncx = etree.Element(NCX('ncx'), attrib={'version': '2005-1', XML('lang'): lang}, nsmap={None: NCX_NS}) From ca4953f0287194cc1bde6548d1e00e02c2314898 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 08:33:32 -0600 Subject: [PATCH 133/140] Implement #7199 (Optional subscription) --- src/calibre/gui2/dialogs/metadata_bulk.ui | 2 +- src/calibre/gui2/dialogs/scheduler.py | 21 +++++++++++++++------ src/calibre/web/feeds/news.py | 7 +++++-- src/calibre/web/feeds/recipes/collection.py | 7 ++++++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 8c60715ae2..0fe537b598 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -39,7 +39,7 @@ - 2 + 0 diff --git a/src/calibre/gui2/dialogs/scheduler.py b/src/calibre/gui2/dialogs/scheduler.py index 30f4a2d8a2..071c5778a8 100644 --- a/src/calibre/gui2/dialogs/scheduler.py +++ b/src/calibre/gui2/dialogs/scheduler.py @@ -120,12 +120,15 @@ class SchedulerDialog(QDialog, Ui_Dialog): if self.account.isVisible(): un, pw = map(unicode, (self.username.text(), self.password.text())) + un, pw = un.strip(), pw.strip() if not un and not pw and self.schedule.isChecked(): - error_dialog(self, _('Need username and password'), - _('You must provide a username and/or password to ' - 'use this news source.'), show=True) - return False - self.recipe_model.set_account_info(urn, un.strip(), pw.strip()) + if not getattr(self, 'subscription_optional', False): + error_dialog(self, _('Need username and password'), + _('You must provide a username and/or password to ' + 'use this news source.'), show=True) + return False + if un or pw: + self.recipe_model.set_account_info(urn, un, pw) if self.schedule.isChecked(): schedule_type = 'interval' if self.interval_button.isChecked() else 'day/time' @@ -157,7 +160,13 @@ class SchedulerDialog(QDialog, Ui_Dialog): account_info = self.recipe_model.account_info_from_urn(urn) customize_info = self.recipe_model.get_customize_info(urn) - self.account.setVisible(recipe.get('needs_subscription', '') == 'yes') + ns = recipe.get('needs_subscription', '') + self.account.setVisible(ns in ('yes', 'optional')) + self.subscription_optional = ns == 'optional' + act = _('Account') + act2 = _('(optional)') if self.subscription_optional else \ + _('(required)') + self.account.setTitle(act+' '+act2) un = pw = '' if account_info is not None: un, pw = account_info[:2] diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index d1e7866198..f3d77061c3 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -110,9 +110,11 @@ class BasicNewsRecipe(Recipe): #: If True the GUI will ask the user for a username and password #: to use while downloading - #: @type: boolean + #: If set to "optional" the use of a username and password becomes optional needs_subscription = False + #: + #: If True the navigation bar is center aligned, otherwise it is left aligned center_navbar = True @@ -609,7 +611,8 @@ class BasicNewsRecipe(Recipe): if self.needs_subscription and (\ self.username is None or self.password is None or \ (not self.username and not self.password)): - raise ValueError(_('The "%s" recipe needs a username and password.')%self.title) + if self.needs_subscription != 'optional': + raise ValueError(_('The "%s" recipe needs a username and password.')%self.title) self.browser = self.get_browser() self.image_map, self.image_counter = {}, 1 diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py index 1dd19dc524..012e24a799 100644 --- a/src/calibre/web/feeds/recipes/collection.py +++ b/src/calibre/web/feeds/recipes/collection.py @@ -45,12 +45,17 @@ def serialize_recipe(urn, recipe_class): return ans default_author = _('You') if urn.startswith('custom:') else _('Unknown') + ns = attr('needs_subscription', False) + if not ns: + ns = 'no' + if ns is True: + ns = 'yes' return E.recipe({ 'id' : str(urn), 'title' : attr('title', _('Unknown')), 'author' : attr('__author__', default_author), 'language' : attr('language', 'und'), - 'needs_subscription' : 'yes' if attr('needs_subscription', False) else 'no', + 'needs_subscription' : ns, 'description' : attr('description', '') }) From 30b3d2a56485b01d68e18ad0a5a0dd94909e0fb7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 09:05:40 -0600 Subject: [PATCH 134/140] CHM Input: Fix handling of relative file paths in tags. Fixes #7159 (Large CHM file fails conversion) --- src/calibre/ebooks/chm/reader.py | 42 +++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/calibre/ebooks/chm/reader.py b/src/calibre/ebooks/chm/reader.py index 73587edfa4..025e252005 100644 --- a/src/calibre/ebooks/chm/reader.py +++ b/src/calibre/ebooks/chm/reader.py @@ -93,6 +93,7 @@ class CHMReader(CHMFile): return data def ExtractFiles(self, output_dir=os.getcwdu()): + html_files = set([]) for path in self.Contents(): lpath = os.path.join(output_dir, path) self._ensure_dir(lpath) @@ -106,14 +107,27 @@ class CHMReader(CHMFile): lpath = lpath.split(';')[0] try: with open(lpath, 'wb') as f: - if guess_mimetype(path)[0] == ('text/html'): - data = self._reformat(data) f.write(data) + try: + if 'html' in guess_mimetype(path)[0]: + html_files.add(lpath) + except: + pass except: if iswindows and len(lpath) > 250: self.log.warn('%r filename too long, skipping'%path) continue raise + for lpath in html_files: + with open(lpath, 'r+b') as f: + data = f.read() + data = self._reformat(data, lpath) + if isinstance(data, unicode): + data = data.encode('utf-8') + f.seek(0) + f.truncate() + f.write(data) + self._extracted = True files = [x for x in os.listdir(output_dir) if os.path.isfile(os.path.join(output_dir, x))] @@ -125,7 +139,7 @@ class CHMReader(CHMFile): if self.hhc_path not in files and files: self.hhc_path = files[0] - def _reformat(self, data): + def _reformat(self, data, htmlpath): try: data = xml_to_unicode(data, strip_encoding_pats=True)[0] soup = BeautifulSoup(data) @@ -169,15 +183,19 @@ class CHMReader(CHMFile): br[0].extract() # some images seem to be broken in some chm's :/ - for img in soup('img'): - try: - # some are supposedly "relative"... lies. - while img['src'].startswith('../'): img['src'] = img['src'][3:] - # some have ";" at the end. - img['src'] = img['src'].split(';')[0] - except KeyError: - # and some don't even have a src= ?! - pass + base = os.path.dirname(htmlpath) + for img in soup('img', src=True): + src = img['src'] + ipath = os.path.join(base, *src.split('/')) + if os.path.exists(ipath): + continue + src = src.split(';')[0] + if not src: continue + ipath = os.path.join(base, *src.split('/')) + if not os.path.exists(ipath): + while src.startswith('../'): + src = src[3:] + img['src'] = src try: # if there is only a single table with a single element # in the body, replace it by the contents of this single element From 30296b34268d9bd7a37fd48bccd1c9b55a159754 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 09:24:45 -0600 Subject: [PATCH 135/140] MOBI Output: Fix regression that broke conversion of elements in the input document when the element was followed by non-whitespace text. Fixes #7083 (Problem to convert epub->mobi) --- src/calibre/ebooks/oeb/base.py | 4 ++-- src/calibre/ebooks/oeb/transforms/rasterize.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index 2d2945c26a..cf80e4abe2 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -282,9 +282,9 @@ def XPath(expr): def xpath(elem, expr): return elem.xpath(expr, namespaces=XPNSMAP) -def xml2str(root, pretty_print=False, strip_comments=False): +def xml2str(root, pretty_print=False, strip_comments=False, with_tail=True): ans = etree.tostring(root, encoding='utf-8', xml_declaration=True, - pretty_print=pretty_print) + pretty_print=pretty_print, with_tail=with_tail) if strip_comments: ans = re.compile(r'', re.DOTALL).sub('', ans) diff --git a/src/calibre/ebooks/oeb/transforms/rasterize.py b/src/calibre/ebooks/oeb/transforms/rasterize.py index 1026b625bf..b09037498a 100644 --- a/src/calibre/ebooks/oeb/transforms/rasterize.py +++ b/src/calibre/ebooks/oeb/transforms/rasterize.py @@ -55,7 +55,7 @@ class SVGRasterizer(object): self.rasterize_cover() def rasterize_svg(self, elem, width=0, height=0, format='PNG'): - data = QByteArray(xml2str(elem)) + data = QByteArray(xml2str(elem, with_tail=False)) svg = QSvgRenderer(data) size = svg.defaultSize() view_box = elem.get('viewBox', elem.get('viewbox', None)) From 0f29b108c759d0d10023f2cd1f5463fd0f5f482a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 09:42:11 -0600 Subject: [PATCH 136/140] Edit metadata dialog: If metadata is downloaded successfully, set focus to download cover button --- src/calibre/gui2/dialogs/metadata_single.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 8043016f9f..ef1bddca0c 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -729,10 +729,13 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.series.setText(book.series) if book.series_index is not None: self.series_index.setValue(book.series_index) + # Needed because of Qt focus bug on OS X + self.fetch_cover_button.setFocus(Qt.OtherFocusReason) else: error_dialog(self, _('Cannot fetch metadata'), _('You must specify at least one of ISBN, Title, ' 'Authors or Publisher'), show=True) + self.title.setFocus(Qt.OtherFocusReason) def enable_series_index(self, *args): self.series_index.setEnabled(True) From 23461c99b9e34b47ebb9112b18d5046f1ec6e92f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 10:20:15 -0600 Subject: [PATCH 137/140] ... --- src/calibre/ebooks/snb/output.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/snb/output.py b/src/calibre/ebooks/snb/output.py index 73a726bd26..549ee51446 100644 --- a/src/calibre/ebooks/snb/output.py +++ b/src/calibre/ebooks/snb/output.py @@ -30,8 +30,7 @@ class SNBOutput(OutputFormatPlugin): OptionRecommendation(name='snb_output_encoding', recommended_value='utf-8', level=OptionRecommendation.LOW, help=_('Specify the character encoding of the output document. ' \ - 'The default is utf-8. Note: This option is not honored by all ' \ - 'formats.')), + 'The default is utf-8.')), # OptionRecommendation(name='inline_toc', # recommended_value=False, level=OptionRecommendation.LOW, # help=_('Add Table of Contents to beginning of the book.')), @@ -55,7 +54,7 @@ class SNBOutput(OutputFormatPlugin): rasterizer = SVGRasterizer() rasterizer(oeb_book, opts) except Unavailable: - self.log.warn('SVG rasterizer unavailable, SVG will not be converted') + log.warn('SVG rasterizer unavailable, SVG will not be converted') # Create temp dir with TemporaryDirectory('_snb_output') as tdir: From 00f358b17eff4dfd4bc8d033a73d7f3f59a1153c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 14:16:44 -0600 Subject: [PATCH 138/140] Fix #7118 (Problem with MSWord HTML/CSS comments and Smart Punct combo) --- src/calibre/utils/smartypants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/utils/smartypants.py b/src/calibre/utils/smartypants.py index 44aac4de8c..62845b8d7a 100644 --- a/src/calibre/utils/smartypants.py +++ b/src/calibre/utils/smartypants.py @@ -376,7 +376,8 @@ default_smartypants_attr = "1" import re -tags_to_skip_regex = re.compile(r"<(/)?(pre|code|kbd|script|math)[^>]*>", re.I) +# style added by Kovid +tags_to_skip_regex = re.compile(r"<(/)?(style|pre|code|kbd|script|math)[^>]*>", re.I) def verify_installation(request): From 258812b7a5380fccc38a9d97c5d8e5981b502a91 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 14:44:24 -0600 Subject: [PATCH 139/140] ... --- setup/installer/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/setup/installer/__init__.py b/setup/installer/__init__.py index f38d175b4c..b976c4d448 100644 --- a/setup/installer/__init__.py +++ b/setup/installer/__init__.py @@ -38,13 +38,19 @@ class Push(Command): description = 'Push code to another host' def run(self, opts): + from threading import Thread + threads = [] for host in ( r'Owner@winxp:/cygdrive/c/Documents\ and\ Settings/Owner/calibre', 'kovid@ox:calibre' ): rcmd = BASE_RSYNC + EXCLUDES + ['.', host] print '\n\nPushing to:', host, '\n' + threads.append(Thread(target=subprocess.check_call, args=(rcmd,))) + threads[-1].start() subprocess.check_call(rcmd) + for thread in threads: + thread.join() From d3fa7cd6ee05db6ff75a9e39ad338b3fb19b7475 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Oct 2010 14:46:28 -0600 Subject: [PATCH 140/140] Fix #6964 (ebook-convert: problems with conversion in Mac OS X, If the file or folder name contains Cyrillic characters) --- src/calibre/startup.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/calibre/startup.py b/src/calibre/startup.py index b5741c1991..e384153993 100644 --- a/src/calibre/startup.py +++ b/src/calibre/startup.py @@ -16,7 +16,7 @@ __builtin__.__dict__['_'] = lambda s: s # immediately translated to the environment language __builtin__.__dict__['__'] = lambda s: s -from calibre.constants import iswindows, preferred_encoding, plugins +from calibre.constants import iswindows, preferred_encoding, plugins, isosx _run_once = False winutil = winutilerror = None @@ -35,9 +35,17 @@ if not _run_once: ################################################################################ # Convert command line arguments to unicode + enc = preferred_encoding + if isosx: + # Newer versions of OS X seem to use UTF-8 + try: + [x.decode('utf-8') for x in sys.argv[1:]] + enc = 'utf-8' + except: + pass for i in range(1, len(sys.argv)): if not isinstance(sys.argv[i], unicode): - sys.argv[i] = sys.argv[i].decode(preferred_encoding, 'replace') + sys.argv[i] = sys.argv[i].decode(enc, 'replace') ################################################################################ # Setup resources