diff options
Diffstat (limited to 'deluge/ui')
228 files changed, 5477 insertions, 3162 deletions
diff --git a/deluge/ui/client.py b/deluge/ui/client.py index 686f962..6b657d5 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import subprocess import sys @@ -33,7 +30,7 @@ def format_kwargs(kwargs): return ', '.join([key + '=' + str(value) for key, value in kwargs.items()]) -class DelugeRPCRequest(object): +class DelugeRPCRequest: """ This object is created whenever there is a RPCRequest to be sent to the daemon. It is generally only used by the DaemonProxy's call method. @@ -65,7 +62,7 @@ class DelugeRPCRequest(object): Returns a properly formatted RPCRequest based on the properties. Will raise a TypeError if the properties haven't been set yet. - :returns: a properly formated RPCRequest + :returns: a properly formatted RPCRequest """ if ( self.request_id is None @@ -150,7 +147,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol): # so it could pass back to the 2nd deferred on the chain. But, # that does not always happen. # So, just do some instance checking and just log rpc error at - # diferent levels. + # different levels. r = self.__rpc_requests[request_id] msg = 'RPCError Message Received!' msg += '\n' + '-' * 80 @@ -168,7 +165,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol): # Let's log these as errors log.error(msg) else: - # The rest just get's logged in debug level, just to log + # The rest just gets logged in debug level, just to log # what's happening log.debug(msg) except Exception: @@ -243,7 +240,7 @@ class DelugeRPCClientFactory(ClientFactory): self.daemon.disconnect_callback() -class DaemonProxy(object): +class DaemonProxy: pass @@ -526,7 +523,7 @@ class DaemonStandaloneProxy(DaemonProxy): self.__daemon.core.eventmanager.deregister_event_handler(event, handler) -class DottedObject(object): +class DottedObject: """ This is used for dotted name calls to client """ @@ -551,7 +548,7 @@ class RemoteMethod(DottedObject): return self.daemon.call(self.base, *args, **kwargs) -class Client(object): +class Client: """ This class is used to connect to a daemon process and issue RPC requests. """ @@ -615,7 +612,7 @@ class Client(object): d.addErrback(on_authenticate_fail) return d - d.addCallback(on_connected) + d.addCallbacks(on_connected) d.addErrback(on_connect_fail) if not skip_authentication: d.addCallback(authenticate, username, password) diff --git a/deluge/ui/common.py b/deluge/ui/common.py index 21bcafd..f9f774e 100644 --- a/deluge/ui/common.py +++ b/deluge/ui/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) Damien Churchill 2008-2009 <damoxc@gmail.com> # Copyright (C) Andrew Resch 2009 <andrewresch@gmail.com> @@ -11,11 +10,8 @@ """ The ui common module contains methods and classes that are deemed useful for all the interfaces. """ -from __future__ import unicode_literals - import logging import os -from binascii import hexlify from hashlib import sha1 as sha from deluge import bencode @@ -161,7 +157,6 @@ DISK_CACHE_KEYS = [ 'disk.num_blocks_written', 'disk.num_read_ops', 'disk.num_write_ops', - 'disk.num_blocks_cache_hits', 'read_hit_ratio', 'write_hit_ratio', 'disk.disk_blocks_in_use', @@ -169,7 +164,7 @@ DISK_CACHE_KEYS = [ ] -class TorrentInfo(object): +class TorrentInfo: """Collects information about a torrent file. Args: @@ -188,7 +183,7 @@ class TorrentInfo(object): try: with open(filename, 'rb') as _file: self._filedata = _file.read() - except IOError as ex: + except OSError as ex: log.warning('Unable to open %s: %s', filename, ex) return @@ -206,12 +201,9 @@ class TorrentInfo(object): self._info_hash = sha(bencode.bencode(info_dict)).hexdigest() # Get encoding from torrent file if available - encoding = info_dict.get('encoding', None) - codepage = info_dict.get('codepage', None) - if not encoding: - encoding = codepage if codepage else b'UTF-8' - if encoding: - encoding = encoding.decode() + encoding = info_dict.get( + 'encoding', info_dict.get('codepage', b'UTF-8') + ).decode() # Decode 'name' with encoding unless 'name.utf-8' found. if 'name.utf-8' in info_dict: @@ -231,27 +223,20 @@ class TorrentInfo(object): if 'path.utf-8' in f: path = decode_bytes(os.path.join(*f['path.utf-8'])) - del f['path.utf-8'] else: path = decode_bytes(os.path.join(*f['path']), encoding) if prefix: path = os.path.join(prefix, path) + # Ensure agnostic path separator + path = path.replace('\\', '/') + self._files.append( {'path': path, 'size': f['length'], 'download': True} ) + paths[path] = {'path': path, 'index': index, 'length': f['length']} - f['path'] = path - f['index'] = index - if 'sha1' in f and len(f['sha1']) == 20: - f['sha1'] = hexlify(f['sha1']).decode() - if 'ed2k' in f and len(f['ed2k']) == 16: - f['ed2k'] = hexlify(f['ed2k']).decode() - if 'filehash' in f and len(f['filehash']) == 20: - f['filehash'] = hexlify(f['filehash']).decode() - - paths[path] = f dirname = os.path.dirname(path) while dirname: dirinfo = dirs.setdefault(dirname, {}) @@ -399,7 +384,7 @@ class TorrentInfo(object): return self._filedata -class FileTree2(object): +class FileTree2: """ Converts a list of paths in to a file tree. @@ -479,7 +464,7 @@ class FileTree2(object): return '\n'.join(lines) -class FileTree(object): +class FileTree: """ Convert a list of paths in a file tree. @@ -538,7 +523,7 @@ class FileTree(object): def walk(directory, parent_path): for path in list(directory): - full_path = os.path.join(parent_path, path) + full_path = os.path.join(parent_path, path).replace('\\', '/') if isinstance(directory[path], dict): directory[path] = ( callback(full_path, directory[path]) or directory[path] diff --git a/deluge/ui/console/__init__.py b/deluge/ui/console/__init__.py index 56e8d62..7da04a6 100644 --- a/deluge/ui/console/__init__.py +++ b/deluge/ui/console/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from deluge.ui.console.console import Console UI_PATH = __path__[0] @@ -16,4 +13,4 @@ UI_PATH = __path__[0] def start(): - Console().start() + return Console().start() diff --git a/deluge/ui/console/cmdline/command.py b/deluge/ui/console/cmdline/command.py index 2ff32df..40edd78 100644 --- a/deluge/ui/console/cmdline/command.py +++ b/deluge/ui/console/cmdline/command.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -9,8 +8,6 @@ # See LICENSE for more details. # -from __future__ import print_function, unicode_literals - import logging import shlex @@ -23,7 +20,7 @@ from deluge.ui.console.utils.colors import strip_colors log = logging.getLogger(__name__) -class Commander(object): +class Commander: def __init__(self, cmds, interactive=False): self._commands = cmds self.interactive = interactive @@ -144,7 +141,7 @@ class Commander(object): return ret -class BaseCommand(object): +class BaseCommand: usage = None interactive_only = False diff --git a/deluge/ui/console/cmdline/commands/__init__.py b/deluge/ui/console/cmdline/commands/__init__.py index 628fae5..39dbefe 100644 --- a/deluge/ui/console/cmdline/commands/__init__.py +++ b/deluge/ui/console/cmdline/commands/__init__.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - from deluge.ui.console.cmdline.command import BaseCommand __all__ = ['BaseCommand'] diff --git a/deluge/ui/console/cmdline/commands/add.py b/deluge/ui/console/cmdline/commands/add.py index 34881d8..706ae16 100644 --- a/deluge/ui/console/cmdline/commands/add.py +++ b/deluge/ui/console/cmdline/commands/add.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,10 +7,10 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import os from base64 import b64encode +from urllib.parse import urlparse +from urllib.request import url2pathname from twisted.internet import defer @@ -21,14 +20,6 @@ from deluge.ui.client import client from . import BaseCommand -try: - from urllib.parse import urlparse - from urllib.request import url2pathname -except ImportError: - # PY2 fallback - from urlparse import urlparse # pylint: disable=ungrouped-imports - from urllib import url2pathname # pylint: disable=ungrouped-imports - class Command(BaseCommand): """Add torrents""" @@ -81,7 +72,7 @@ class Command(BaseCommand): continue if deluge.common.is_url(torrent): self.console.write( - '{!info!}Attempting to add torrent from url: %s' % torrent + '{!info!}Attempting to add torrent from URL: %s' % torrent ) deferreds.append( client.core.add_torrent_url(torrent, t_options) @@ -90,7 +81,7 @@ class Command(BaseCommand): ) elif deluge.common.is_magnet(torrent): self.console.write( - '{!info!}Attempting to add torrent from magnet uri: %s' % torrent + '{!info!}Attempting to add torrent from magnet URI: %s' % torrent ) deferreds.append( client.core.add_torrent_magnet(torrent, t_options) diff --git a/deluge/ui/console/cmdline/commands/cache.py b/deluge/ui/console/cmdline/commands/cache.py index e427f08..fe6cd58 100644 --- a/deluge/ui/console/cmdline/commands/cache.py +++ b/deluge/ui/console/cmdline/commands/cache.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.component as component from deluge.ui.client import client from deluge.ui.common import DISK_CACHE_KEYS @@ -24,7 +21,7 @@ class Command(BaseCommand): def on_cache_status(status): for key, value in sorted(status.items()): - self.console.write('{!info!}%s: {!input!}%s' % (key, value)) + self.console.write(f'{{!info!}}{key}: {{!input!}}{value}') return client.core.get_session_status(DISK_CACHE_KEYS).addCallback( on_cache_status diff --git a/deluge/ui/console/cmdline/commands/config.py b/deluge/ui/console/cmdline/commands/config.py index bd0a1e1..8b31ca3 100644 --- a/deluge/ui/console/cmdline/commands/config.py +++ b/deluge/ui/console/cmdline/commands/config.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,11 +7,9 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - +import json import logging -import tokenize -from io import StringIO +import re import deluge.component as component import deluge.ui.console.utils.colors as colors @@ -23,54 +20,25 @@ from . import BaseCommand log = logging.getLogger(__name__) -def atom(src, token): - """taken with slight modifications from http://effbot.org/zone/simple-iterator-parser.htm""" - if token[1] == '(': - out = [] - token = next(src) - while token[1] != ')': - out.append(atom(src, token)) - token = next(src) - if token[1] == ',': - token = next(src) - return tuple(out) - elif token[0] is tokenize.NUMBER or token[1] == '-': - try: - if token[1] == '-': - return int(token[-1], 0) - else: - if token[1].startswith('0x'): - # Hex number so return unconverted as string. - return token[1].decode('string-escape') - else: - return int(token[1], 0) - except ValueError: - try: - return float(token[-1]) - except ValueError: - return str(token[-1]) - elif token[1].lower() == 'true': - return True - elif token[1].lower() == 'false': - return False - elif token[0] is tokenize.STRING or token[1] == '/': - return token[-1].decode('string-escape') - elif token[1].isalpha(): - # Parse Windows paths e.g. 'C:\\xyz' or 'C:/xyz'. - if next()[1] == ':' and next()[1] in '\\/': - return token[-1].decode('string-escape') - - raise SyntaxError('malformed expression (%s)' % token[1]) - - -def simple_eval(source): - """ evaluates the 'source' string into a combination of primitive python objects - taken from http://effbot.org/zone/simple-iterator-parser.htm""" - src = StringIO(source).readline - src = tokenize.generate_tokens(src) - src = (token for token in src if token[0] is not tokenize.NL) - res = atom(src, next(src)) - return res +def json_eval(source): + """Evaluates string as json data and returns Python objects.""" + if source == '': + return source + + src = source.splitlines()[0] + + # Substitutions to enable usage of pythonic syntax. + if src.startswith('(') and src.endswith(')'): + src = re.sub(r'^\((.*)\)$', r'[\1]', src) + elif src.lower() in ('true', 'false'): + src = src.lower() + elif src.lower() == 'none': + src = 'null' + + try: + return json.loads(src) + except ValueError: + return src class Command(BaseCommand): @@ -126,10 +94,10 @@ class Command(BaseCommand): value = pprint.pformat(value, 2, 80) new_value = [] for line in value.splitlines(): - new_value.append('%s%s' % (color, line)) + new_value.append(f'{color}{line}') value = '\n'.join(new_value) - string += '%s: %s%s\n' % (key, color, value) + string += f'{key}: {color}{value}\n' self.console.write(string.strip()) return client.core.get_config().addCallback(_on_get_config) @@ -140,8 +108,8 @@ class Command(BaseCommand): val = ' '.join(options.values) try: - val = simple_eval(val) - except SyntaxError as ex: + val = json_eval(val) + except Exception as ex: self.console.write('{!error!}%s' % ex) return @@ -161,7 +129,7 @@ class Command(BaseCommand): def on_set_config(result): self.console.write('{!success!}Configuration value successfully updated.') - self.console.write('Setting "%s" to: %s' % (key, val)) + self.console.write(f'Setting "{key}" to: {val!r}') return client.core.set_config({key: val}).addCallback(on_set_config) def complete(self, text): diff --git a/deluge/ui/console/cmdline/commands/connect.py b/deluge/ui/console/cmdline/commands/connect.py index 6588f7a..4c76de3 100644 --- a/deluge/ui/console/cmdline/commands/connect.py +++ b/deluge/ui/console/cmdline/commands/connect.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -57,17 +54,12 @@ class Command(BaseCommand): def on_connect(result): if self.console.interactive: - self.console.write('{!success!}Connected to %s:%s!' % (host, port)) + self.console.write(f'{{!success!}}Connected to {host}:{port}!') return component.start() def on_connect_fail(result): - try: - msg = result.value.exception_msg - except AttributeError: - msg = result.value.message self.console.write( - '{!error!}Failed to connect to %s:%s with reason: %s' - % (host, port, msg) + f'{{!error!}}Failed to connect to {host}:{port} with reason: {result.value.message}' ) return result diff --git a/deluge/ui/console/cmdline/commands/debug.py b/deluge/ui/console/cmdline/commands/debug.py index 3ca06ed..af48a8b 100644 --- a/deluge/ui/console/cmdline/commands/debug.py +++ b/deluge/ui/console/cmdline/commands/debug.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from twisted.internet import defer import deluge.component as component diff --git a/deluge/ui/console/cmdline/commands/gui.py b/deluge/ui/console/cmdline/commands/gui.py index 10e4c49..575bc9b 100644 --- a/deluge/ui/console/cmdline/commands/gui.py +++ b/deluge/ui/console/cmdline/commands/gui.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component diff --git a/deluge/ui/console/cmdline/commands/halt.py b/deluge/ui/console/cmdline/commands/halt.py index 6355958..608f2de 100644 --- a/deluge/ui/console/cmdline/commands/halt.py +++ b/deluge/ui/console/cmdline/commands/halt.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.component as component from deluge.ui.client import client diff --git a/deluge/ui/console/cmdline/commands/help.py b/deluge/ui/console/cmdline/commands/help.py index 2711eea..754dadb 100644 --- a/deluge/ui/console/cmdline/commands/help.py +++ b/deluge/ui/console/cmdline/commands/help.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from twisted.internet import defer diff --git a/deluge/ui/console/cmdline/commands/info.py b/deluge/ui/console/cmdline/commands/info.py index 0d22f76..7ea9a67 100644 --- a/deluge/ui/console/cmdline/commands/info.py +++ b/deluge/ui/console/cmdline/commands/info.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - from os.path import sep as dirsep import deluge.component as component @@ -70,6 +67,7 @@ STATUS_KEYS = [ 'total_payload_download', 'total_payload_upload', 'time_added', + 'label', ] # Add filter specific state to torrent states @@ -177,7 +175,7 @@ class Command(BaseCommand): sort_key = 'name' sort_reverse = False for key, value in sorted( - list(status.items()), + status.items(), key=lambda x: x[1].get(sort_key), reverse=sort_reverse, ): @@ -218,9 +216,9 @@ class Command(BaseCommand): for depth, subdir in enumerate(filepath): indent = ' ' * depth * spaces_per_level if depth >= len(prevpath): - self.console.write('%s{!cyan!}%s' % (indent, subdir)) + self.console.write(f'{indent}{{!cyan!}}{subdir}') elif subdir != prevpath[depth]: - self.console.write('%s{!cyan!}%s' % (indent, subdir)) + self.console.write(f'{indent}{{!cyan!}}{subdir}') depth = len(filepath) @@ -296,7 +294,7 @@ class Command(BaseCommand): s += peer['ip'] else: # IPv6 - s += '[%s]:%s' % ( + s += '[{}]:{}'.format( ':'.join(peer['ip'].split(':')[:-1]), peer['ip'].split(':')[-1], ) @@ -308,7 +306,7 @@ class Command(BaseCommand): s += '\t\t' else: s += '\t' - s += '%s%s\t%s%s' % ( + s += '{}{}\t{}{}'.format( colors.state_color['Seeding'], fspeed(peer['up_speed']), colors.state_color['Downloading'], @@ -336,7 +334,7 @@ class Command(BaseCommand): if verbose or detailed: self.console.write('{!info!}Name: {!input!}%s' % (status['name'])) self.console.write('{!info!}ID: {!input!}%s' % (torrent_id)) - s = '{!info!}State: %s%s' % ( + s = '{{!info!}}State: {}{}'.format( colors.state_color[status['state']], status['state'], ) @@ -354,12 +352,12 @@ class Command(BaseCommand): self.console.write(s) if status['state'] in ('Seeding', 'Downloading', 'Queued'): - s = '{!info!}Seeds: {!input!}%s (%s)' % ( + s = '{{!info!}}Seeds: {{!input!}}{} ({})'.format( status['num_seeds'], status['total_seeds'], ) s += sep - s += '{!info!}Peers: {!input!}%s (%s)' % ( + s += '{{!info!}}Peers: {{!input!}}{} ({})'.format( status['num_peers'], status['total_peers'], ) @@ -378,7 +376,7 @@ class Command(BaseCommand): if total_done == total_size: s = '{!info!}Size: {!input!}%s' % (total_size) else: - s = '{!info!}Size: {!input!}%s/%s' % (total_done, total_size) + s = f'{{!info!}}Size: {{!input!}}{total_done}/{total_size}' s += sep s += '{!info!}Downloaded: {!input!}%s' % fsize( status['all_time_download'], shortform=True @@ -418,14 +416,20 @@ class Command(BaseCommand): pbar = f_progressbar( status['progress'], cols - (13 + len('%.2f%%' % status['progress'])) ) - s = '{!info!}Progress: {!input!}%.2f%% %s' % (status['progress'], pbar) + s = '{{!info!}}Progress: {{!input!}}{:.2f}% {}'.format( + status['progress'], pbar + ) self.console.write(s) s = '{!info!}Download Folder: {!input!}%s' % status['download_location'] - self.console.write(s + '\n') + self.console.write(s) + + if 'label' in status: + s = '{!info!}Label: {!input!}%s' % status['label'] + self.console.write(s) if detailed: - self.console.write('{!info!}Files in torrent') + self.console.write('\n{!info!}Files in torrent') self.show_file_info(torrent_id, status) self.console.write('{!info!}Connected peers') self.show_peer_info(torrent_id, status) @@ -433,7 +437,7 @@ class Command(BaseCommand): up_color = colors.state_color['Seeding'] down_color = colors.state_color['Downloading'] - s = '%s%s' % ( + s = '{}{}'.format( colors.state_color[status['state']], '[' + status['state'][0] + ']', ) @@ -458,7 +462,7 @@ class Command(BaseCommand): ) if status['download_payload_rate'] > 0: - dl_info += ' @ %s%s' % ( + dl_info += ' @ {}{}'.format( down_color, fspeed(status['download_payload_rate'], shortform=True), ) @@ -468,7 +472,7 @@ class Command(BaseCommand): status['total_uploaded'], status['total_payload_upload'] ) if status['upload_payload_rate'] > 0: - ul_info += ' @ %s%s' % ( + ul_info += ' @ {}{}'.format( up_color, fspeed(status['upload_payload_rate'], shortform=True), ) diff --git a/deluge/ui/console/cmdline/commands/manage.py b/deluge/ui/console/cmdline/commands/manage.py index 6375a74..e5ea9b2 100644 --- a/deluge/ui/console/cmdline/commands/manage.py +++ b/deluge/ui/console/cmdline/commands/manage.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from twisted.internet import defer @@ -69,7 +66,7 @@ class Command(BaseCommand): self.console.write('{!info!}ID: {!input!}%s' % torrentid) for k, v in data.items(): if k != 'name': - self.console.write('{!info!}%s: {!input!}%s' % (k, v)) + self.console.write(f'{{!info!}}{k}: {{!input!}}{v}') def on_torrents_status_fail(reason): self.console.write('{!error!}Failed to get torrent data.') @@ -106,9 +103,7 @@ class Command(BaseCommand): self.console.write('{!success!}Torrent option successfully updated.') deferred.callback(True) - self.console.write( - 'Setting %s to %s for torrents %s..' % (key, val, torrent_ids) - ) + self.console.write(f'Setting {key} to {val} for torrents {torrent_ids}..') client.core.set_torrent_options(torrent_ids, {key: val}).addCallback( on_set_config ) diff --git a/deluge/ui/console/cmdline/commands/move.py b/deluge/ui/console/cmdline/commands/move.py index 13e475e..67ee0af 100644 --- a/deluge/ui/console/cmdline/commands/move.py +++ b/deluge/ui/console/cmdline/commands/move.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path @@ -52,7 +49,7 @@ class Command(BaseCommand): names.append(self.console.get_torrent_name(tid)) def on_move(res): - msg = 'Moved "%s" to %s' % (', '.join(names), options.path) + msg = 'Moved "{}" to {}'.format(', '.join(names), options.path) self.console.write(msg) log.info(msg) diff --git a/deluge/ui/console/cmdline/commands/pause.py b/deluge/ui/console/cmdline/commands/pause.py index 1f7ef31..1334242 100644 --- a/deluge/ui/console/cmdline/commands/pause.py +++ b/deluge/ui/console/cmdline/commands/pause.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.component as component from deluge.ui.client import client diff --git a/deluge/ui/console/cmdline/commands/plugin.py b/deluge/ui/console/cmdline/commands/plugin.py index fafc77a..c424cb2 100644 --- a/deluge/ui/console/cmdline/commands/plugin.py +++ b/deluge/ui/console/cmdline/commands/plugin.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.component as component import deluge.configmanager from deluge.ui.client import client @@ -106,8 +103,8 @@ class Command(BaseCommand): elif options.install: import os.path - from base64 import b64encode import shutil + from base64 import b64encode filepath = options.install diff --git a/deluge/ui/console/cmdline/commands/quit.py b/deluge/ui/console/cmdline/commands/quit.py index 261a01a..4459dfc 100644 --- a/deluge/ui/console/cmdline/commands/quit.py +++ b/deluge/ui/console/cmdline/commands/quit.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.component as component from . import BaseCommand diff --git a/deluge/ui/console/cmdline/commands/recheck.py b/deluge/ui/console/cmdline/commands/recheck.py index c9b6360..046cb0b 100644 --- a/deluge/ui/console/cmdline/commands/recheck.py +++ b/deluge/ui/console/cmdline/commands/recheck.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.component as component from deluge.ui.client import client diff --git a/deluge/ui/console/cmdline/commands/resume.py b/deluge/ui/console/cmdline/commands/resume.py index 1f62c5f..27b8528 100644 --- a/deluge/ui/console/cmdline/commands/resume.py +++ b/deluge/ui/console/cmdline/commands/resume.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.component as component from deluge.ui.client import client diff --git a/deluge/ui/console/cmdline/commands/rm.py b/deluge/ui/console/cmdline/commands/rm.py index ff3125d..4a3fd00 100644 --- a/deluge/ui/console/cmdline/commands/rm.py +++ b/deluge/ui/console/cmdline/commands/rm.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -70,9 +67,11 @@ class Command(BaseCommand): def on_removed_finished(errors): if errors: - self.console.write('Error(s) occured when trying to delete torrent(s).') + self.console.write( + 'Error(s) occurred when trying to delete torrent(s).' + ) for t_id, e_msg in errors: - self.console.write('Error removing torrent %s : %s' % (t_id, e_msg)) + self.console.write(f'Error removing torrent {t_id} : {e_msg}') log.info('Removing %d torrents', len(torrent_ids)) d = client.core.remove_torrents(torrent_ids, options.remove_data) diff --git a/deluge/ui/console/cmdline/commands/status.py b/deluge/ui/console/cmdline/commands/status.py index 948ad6b..05c9796 100644 --- a/deluge/ui/console/cmdline/commands/status.py +++ b/deluge/ui/console/cmdline/commands/status.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from twisted.internet import defer @@ -65,7 +62,12 @@ class Command(BaseCommand): deferreds = [] ds = client.core.get_session_status( - ['num_peers', 'payload_upload_rate', 'payload_download_rate', 'dht_nodes'] + [ + 'peer.num_peers_connected', + 'payload_upload_rate', + 'payload_download_rate', + 'dht.dht_nodes', + ] ) ds.addCallback(on_session_status) deferreds.append(ds) @@ -95,7 +97,7 @@ class Command(BaseCommand): '{!info!}Total download: %s' % fspeed(self.status['payload_download_rate']) ) - self.console.write('{!info!}DHT Nodes: %i' % self.status['dht_nodes']) + self.console.write('{!info!}DHT Nodes: %i' % self.status['dht.dht_nodes']) if isinstance(self.torrents, int): if self.torrents == -2: diff --git a/deluge/ui/console/cmdline/commands/update_tracker.py b/deluge/ui/console/cmdline/commands/update_tracker.py index 591b951..c05569d 100644 --- a/deluge/ui/console/cmdline/commands/update_tracker.py +++ b/deluge/ui/console/cmdline/commands/update_tracker.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.component as component from deluge.ui.client import client diff --git a/deluge/ui/console/console.py b/deluge/ui/console/console.py index 58d31d5..8ef87e8 100644 --- a/deluge/ui/console/console.py +++ b/deluge/ui/console/console.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -7,8 +6,6 @@ # the additional special exception to link portions of this program with the OpenSSL library. # See LICENSE for more details. # -from __future__ import print_function, unicode_literals - import fnmatch import logging import os @@ -53,7 +50,7 @@ def load_commands(command_dir): return dict(commands) -class LogStream(object): +class LogStream: out = sys.stdout def write(self, data): @@ -68,9 +65,7 @@ class Console(UI): cmd_description = """Console or command-line user interface""" def __init__(self, *args, **kwargs): - super(Console, self).__init__( - 'console', *args, log_stream=LogStream(), **kwargs - ) + super().__init__('console', *args, log_stream=LogStream(), **kwargs) group = self.parser.add_argument_group( _('Console Options'), @@ -112,9 +107,9 @@ class Console(UI): ) # To properly print help message for the console commands ( e.g. deluge-console info -h), # we add a subparser for each command which will trigger the help/usage when given - from deluge.ui.console.parser import ( + from deluge.ui.console.parser import ( # import here because (see top) ConsoleCommandParser, - ) # import here because (see top) + ) self.console_parser = ConsoleCommandParser( parents=[self.parser], @@ -150,7 +145,7 @@ class Console(UI): self.console_parser.subcommand = False self.parser.subcommand = False if i == -1 else True - super(Console, self).start(self.console_parser) + super().start(self.console_parser) from deluge.ui.console.main import ConsoleUI # import here because (see top) def run(options): diff --git a/deluge/ui/console/main.py b/deluge/ui/console/main.py index 23965bb..31d1db1 100644 --- a/deluge/ui/console/main.py +++ b/deluge/ui/console/main.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import print_function, unicode_literals - import locale import logging import os @@ -67,6 +64,14 @@ DEFAULT_CONSOLE_PREFS = { } +class MockConsoleLog: + def write(self, data): + pass + + def flush(self): + pass + + class ConsoleUI(component.Component, TermResizeHandler): def __init__(self, options, cmds, log_stream): component.Component.__init__(self, 'ConsoleUI') @@ -114,6 +119,7 @@ class ConsoleUI(component.Component, TermResizeHandler): all commands are executed. Else None is returned. """ if self.options.parsed_cmds: + # Non-Interactive mode self.interactive = False if not self._commands: print('No valid console commands found') @@ -122,41 +128,37 @@ class ConsoleUI(component.Component, TermResizeHandler): deferred = self.exec_args(self.options) reactor.run() return deferred - else: - # Interactive - if deluge.common.windows_check(): - print( - """\nDeluge-console does not run in interactive mode on Windows. \n -Please use commands from the command line, e.g.:\n - deluge-console.exe help - deluge-console.exe info - deluge-console.exe "add --help" - deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent" -""" - ) - else: - class ConsoleLog(object): - def write(self, data): - pass + # Interactive - def flush(self): - pass + # We use the curses.wrapper function to prevent the console from getting + # messed up if an uncaught exception is experienced. + try: + from curses import wrapper + except ImportError: + wrapper = None - # We don't ever want log output to terminal when running in - # interactive mode, so insert a dummy here - self.log_stream.out = ConsoleLog() + if deluge.common.windows_check() and not wrapper: + print( + """\nDeluge-console does not run in interactive mode on Windows. \n +Please use commands from the command line, e.g.:\n +deluge-console.exe help +deluge-console.exe info +deluge-console.exe "add --help" +deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent" +""" + ) - # Set Esc key delay to 0 to avoid a very annoying delay - # due to curses waiting in case of other key are pressed - # after ESC is pressed - os.environ.setdefault('ESCDELAY', '0') + # We don't ever want log output to terminal when running in + # interactive mode, so insert a dummy here + self.log_stream.out = MockConsoleLog() - # We use the curses.wrapper function to prevent the console from getting - # messed up if an uncaught exception is experienced. - from curses import wrapper + # Set Esc key delay to 0 to avoid a very annoying delay + # due to curses waiting in case of other key are pressed + # after ESC is pressed + os.environ.setdefault('ESCDELAY', '0') - wrapper(self.run) + wrapper(self.run) def quit(self): if client.connected(): @@ -281,7 +283,7 @@ Please use commands from the command line, e.g.:\n @overrides(TermResizeHandler) def on_terminal_size(self, *args): - rows, cols = super(ConsoleUI, self).on_terminal_size(args) + rows, cols = super().on_terminal_size(args) for mode in self.modes: self.modes[mode].on_resize(rows, cols) @@ -706,7 +708,7 @@ class EventLog(component.Component): if not t_name: return - self.write('%s: {!info!}%s ({!cyan!}%s{!info!})' % (state, t_name, torrent_id)) + self.write(f'{state}: {{!info!}}{t_name} ({{!cyan!}}{torrent_id}{{!info!}})') def on_torrent_finished_event(self, torrent_id): if component.get('TorrentList').config['ring_bell']: @@ -734,7 +736,7 @@ class EventLog(component.Component): except KeyError: pass - self.write('ConfigValueChanged: {!input!}%s: %s%s' % (key, color, value)) + self.write(f'ConfigValueChanged: {{!input!}}{key}: {color}{value}') def write(self, s): current_time = time.localtime() @@ -748,8 +750,6 @@ class EventLog(component.Component): if date_different: string = time.strftime(self.date_change_format) - if deluge.common.PY2: - string = string.decode() self.console.write_event(' ') self.console.write_event(string) diff --git a/deluge/ui/console/modes/add_util.py b/deluge/ui/console/modes/add_util.py index 88a24d0..9d29a1f 100644 --- a/deluge/ui/console/modes/add_util.py +++ b/deluge/ui/console/modes/add_util.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -9,15 +8,11 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import glob import logging import os from base64 import b64encode -from six import unichr as chr - import deluge.common from deluge.ui.client import client from deluge.ui.common import TorrentInfo diff --git a/deluge/ui/console/modes/addtorrents.py b/deluge/ui/console/modes/addtorrents.py index 6b2c105..217b63d 100644 --- a/deluge/ui/console/modes/addtorrents.py +++ b/deluge/ui/console/modes/addtorrents.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012 Arek Stefański <asmageddon@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os from base64 import b64encode @@ -24,12 +21,6 @@ from deluge.ui.console.utils import format_utils from deluge.ui.console.widgets.popup import InputPopup, MessagePopup try: - from future_builtins import zip -except ImportError: - # Ignore on Py3. - pass - -try: import curses except ImportError: pass @@ -377,7 +368,7 @@ class AddTorrents(BaseMode): def fail_cb(msg, t_file, ress): log.debug('failed to add torrent: %s: %s', t_file, msg) ress['fail'] += 1 - ress['fmsg'].append('{!input!} * %s: {!error!}%s' % (t_file, msg)) + ress['fmsg'].append(f'{{!input!}} * {t_file}: {{!error!}}{msg}') if (ress['succ'] + ress['fail']) >= ress['total']: report_add_status( component.get('TorrentList'), @@ -526,9 +517,9 @@ class AddTorrents(BaseMode): self.last_mark = self.cursel elif chr(c) == 'j': - self.scroll_list_up(1) - elif chr(c) == 'k': self.scroll_list_down(1) + elif chr(c) == 'k': + self.scroll_list_up(1) elif chr(c) == 'M': if self.last_mark != -1: if self.last_mark > self.cursel: diff --git a/deluge/ui/console/modes/basemode.py b/deluge/ui/console/modes/basemode.py index dd3681f..5ebaf86 100644 --- a/deluge/ui/console/modes/basemode.py +++ b/deluge/ui/console/modes/basemode.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import sys @@ -26,9 +23,9 @@ except ImportError: try: import signal - from fcntl import ioctl - import termios import struct + import termios + from fcntl import ioctl except ImportError: pass @@ -36,7 +33,7 @@ except ImportError: log = logging.getLogger(__name__) -class InputKeyHandler(object): +class InputKeyHandler: def __init__(self): self._input_result = None @@ -62,12 +59,14 @@ class InputKeyHandler(object): return util.ReadState.IGNORED -class TermResizeHandler(object): +class TermResizeHandler: def __init__(self): try: signal.signal(signal.SIGWINCH, self.on_terminal_size) except ValueError as ex: - log.debug('Unable to catch SIGWINCH signal: %s', ex) + log.debug('TermResize unavailable, unable to catch SIGWINCH signal: %s', ex) + except AttributeError as ex: + log.debug('TermResize unavailable, no SIGWINCH signal on Windows: %s', ex) def on_terminal_size(self, *args): # Get the new rows and cols value @@ -78,14 +77,14 @@ class TermResizeHandler(object): return rows, cols -class CursesStdIO(object): +class CursesStdIO: """ fake fd to be registered as a reader with the twisted reactor. Curses classes needing input should extend this """ def fileno(self): - """ We want to select on FD 0 """ + """We want to select on FD 0""" return 0 def doRead(self): # NOQA: N802 diff --git a/deluge/ui/console/modes/cmdline.py b/deluge/ui/console/modes/cmdline.py index 2735168..7b0ff2d 100644 --- a/deluge/ui/console/modes/cmdline.py +++ b/deluge/ui/console/modes/cmdline.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> @@ -8,16 +7,12 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os import re -from io import open import deluge.component as component import deluge.configmanager -from deluge.common import PY2 from deluge.decorators import overrides from deluge.ui.console.cmdline.command import Commander from deluge.ui.console.modes.basemode import BaseMode, move_cursor @@ -139,18 +134,18 @@ class CmdLine(BaseMode, Commander): self._hf_lines = [0, 0] if self.console_config['cmdline']['save_command_history']: try: - with open(self.history_file[0], 'r', encoding='utf8') as _file: + with open(self.history_file[0], encoding='utf8') as _file: lines1 = _file.read().splitlines() self._hf_lines[0] = len(lines1) - except IOError: + except OSError: lines1 = [] self._hf_lines[0] = 0 try: - with open(self.history_file[1], 'r', encoding='utf8') as _file: + with open(self.history_file[1], encoding='utf8') as _file: lines2 = _file.read().splitlines() self._hf_lines[1] = len(lines2) - except IOError: + except OSError: lines2 = [] self._hf_lines[1] = 0 @@ -332,10 +327,10 @@ class CmdLine(BaseMode, Commander): # A key to add to the input string else: - if c > 31 and c < 256: + if 31 < c < 256: # Emulate getwch stroke = chr(c) - uchar = '' if PY2 else stroke + uchar = stroke while not uchar: try: uchar = stroke.decode(self.encoding) @@ -826,21 +821,21 @@ class CmdLine(BaseMode, Commander): # Let's avoid listing all torrents twice if there's no pattern if not empty and torrent_id.startswith(line): # Highlight the matching part - text = '{!info!}%s{!input!}%s - "%s"' % ( + text = '{{!info!}}{}{{!input!}}{} - "{}"'.format( torrent_id[:line_len], torrent_id[line_len:], torrent_name, ) possible_matches.append(text) if torrent_name.startswith(line): - text = '{!info!}%s{!input!}%s ({!cyan!}%s{!input!})' % ( + text = '{{!info!}}{}{{!input!}}{} ({{!cyan!}}{}{{!input!}})'.format( escaped_name[:line_len], escaped_name[line_len:], torrent_id, ) possible_matches.append(text) elif torrent_name.lower().startswith(line.lower()): - text = '{!info!}%s{!input!}%s ({!cyan!}%s{!input!})' % ( + text = '{{!info!}}{}{{!input!}}{} ({{!cyan!}}{}{{!input!}})'.format( escaped_name[:line_len], escaped_name[line_len:], torrent_id, diff --git a/deluge/ui/console/modes/connectionmanager.py b/deluge/ui/console/modes/connectionmanager.py index 84a3fbc..0ccdd93 100644 --- a/deluge/ui/console/modes/connectionmanager.py +++ b/deluge/ui/console/modes/connectionmanager.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -32,11 +29,13 @@ class ConnectionManager(BaseMode, PopupsHandler): self.statuses = {} self.all_torrents = None self.hostlist = HostList() - self.update_hosts_status() BaseMode.__init__(self, stdscr, encoding=encoding) - self.update_select_host_popup() def update_select_host_popup(self): + if self.popup and not isinstance(self.popup, SelectablePopup): + # Ignore MessagePopup on popup stack upon connect fail + return + selected_index = self.popup.current_selection() if self.popup else None popup = SelectablePopup( @@ -51,32 +50,33 @@ class ConnectionManager(BaseMode, PopupsHandler): % (_('Quit'), _('Add Host'), _('Delete Host')), space_below=True, ) - self.push_popup(popup, clear=True) for host_entry in self.hostlist.get_hosts_info(): host_id, hostname, port, user = host_entry - args = {'data': host_id, 'foreground': 'red'} - state = 'Offline' - if host_id in self.statuses: - state = 'Online' - args.update({'data': self.statuses[host_id], 'foreground': 'green'}) - host_str = '%s:%d [%s]' % (hostname, port, state) - self.popup.add_line( + host_status = self.statuses.get(host_id) + + state = host_status[1] if host_status else 'Offline' + state_color = 'green' if state in ('Online', 'Connected') else 'red' + host_str = f'{hostname}:{port} [{state}]' + + args = {'data': host_id, 'foreground': state_color} + popup.add_line( host_id, host_str, selectable=True, use_underline=True, **args ) if selected_index: - self.popup.set_selection(selected_index) + popup.set_selection(selected_index) + + self.push_popup(popup, clear=True) self.inlist = True self.refresh() def update_hosts_status(self): - for host_entry in self.hostlist.get_hosts_info(): - - def on_host_status(status_info): - self.statuses[status_info[0]] = status_info - self.update_select_host_popup() + def on_host_status(status_info): + self.statuses[status_info[0]] = status_info + self.update_select_host_popup() + for host_entry in self.hostlist.get_hosts_info(): self.hostlist.get_host_status(host_entry[0]).addCallback(on_host_status) def _on_connected(self, result): @@ -87,7 +87,7 @@ class ConnectionManager(BaseMode, PopupsHandler): d.addCallback(on_console_start) def _on_connect_fail(self, result): - self.report_message('Failed to connect!', result) + self.report_message('Failed to connect!', result.getErrorMessage()) self.refresh() if hasattr(result, 'getTraceback'): log.exception(result) @@ -130,7 +130,7 @@ class ConnectionManager(BaseMode, PopupsHandler): try: self.hostlist.add_host(hostname, port, username, password) except ValueError as ex: - self.report_message(_('Error adding host'), '%s: %s' % (hostname, ex)) + self.report_message(_('Error adding host'), f'{hostname}: {ex}') else: self.update_select_host_popup() @@ -169,7 +169,9 @@ class ConnectionManager(BaseMode, PopupsHandler): if not self.popup: self.update_select_host_popup() - self.popup.refresh() + if self.popup: + self.popup.refresh() + curses.doupdate() @overrides(BaseMode) diff --git a/deluge/ui/console/modes/eventview.py b/deluge/ui/console/modes/eventview.py index cd3308c..b6e63b0 100644 --- a/deluge/ui/console/modes/eventview.py +++ b/deluge/ui/console/modes/eventview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -100,9 +97,9 @@ class EventView(BaseMode): elif c == curses.KEY_END: self.offset += num_events elif c == ord('j'): - self.offset -= 1 - elif c == ord('k'): self.offset += 1 + elif c == ord('k'): + self.offset -= 1 if self.offset <= 0: self.offset = 0 diff --git a/deluge/ui/console/modes/preferences/__init__.py b/deluge/ui/console/modes/preferences/__init__.py index 15d77c4..e827d91 100644 --- a/deluge/ui/console/modes/preferences/__init__.py +++ b/deluge/ui/console/modes/preferences/__init__.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from deluge.ui.console.modes.preferences.preferences import Preferences __all__ = ['Preferences'] diff --git a/deluge/ui/console/modes/preferences/preference_panes.py b/deluge/ui/console/modes/preferences/preference_panes.py index 62029a6..b47bc4b 100644 --- a/deluge/ui/console/modes/preferences/preference_panes.py +++ b/deluge/ui/console/modes/preferences/preference_panes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,11 +6,9 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging -from deluge.common import is_ip +from deluge.common import is_interface from deluge.decorators import overrides from deluge.i18n import get_languages from deluge.ui.client import client @@ -94,11 +91,12 @@ class BasePreferencePane(BaseInputPane, BaseWindow, PopupsHandler): ) elif ipt.name == 'listen_interface': listen_interface = ipt.get_value().strip() - if is_ip(listen_interface) or not listen_interface: + if is_interface(listen_interface) or not listen_interface: conf_dict['listen_interface'] = listen_interface elif ipt.name == 'outgoing_interface': outgoing_interface = ipt.get_value().strip() - conf_dict['outgoing_interface'] = outgoing_interface + if is_interface(outgoing_interface) or not outgoing_interface: + conf_dict['outgoing_interface'] = outgoing_interface elif ipt.name.startswith('proxy_'): if ipt.name == 'proxy_type': conf_dict.setdefault('proxy', {})['type'] = ipt.get_value() @@ -724,11 +722,6 @@ class CachePane(BasePreferencePane): self.add_info_field( 'blocks_read', ' %s:' % _('Blocks Read'), status['disk.num_blocks_read'] ) - self.add_info_field( - 'blocks_read_hit', - ' %s:' % _('Blocks Read hit'), - status['disk.num_blocks_cache_hits'], - ) self.add_info_field('reads', ' %s:' % _('Reads'), status['disk.num_read_ops']) self.add_info_field( 'read_hit_ratio', diff --git a/deluge/ui/console/modes/preferences/preferences.py b/deluge/ui/console/modes/preferences/preferences.py index 45a39a6..2c95323 100644 --- a/deluge/ui/console/modes/preferences/preferences.py +++ b/deluge/ui/console/modes/preferences/preferences.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from collections import deque @@ -74,7 +71,7 @@ arrow to edit the other value, and escape to get back to the check box. """ -class ZONE(object): +class ZONE: length = 3 CATEGORIES, PREFRENCES, ACTIONS = list(range(length)) diff --git a/deluge/ui/console/modes/torrentdetail.py b/deluge/ui/console/modes/torrentdetail.py index d02a0d3..16bd08a 100644 --- a/deluge/ui/console/modes/torrentdetail.py +++ b/deluge/ui/console/modes/torrentdetail.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging import deluge.component as component @@ -220,7 +217,7 @@ class TorrentDetail(BaseMode, PopupsHandler): self.refresh() def build_file_list(self, torrent_files, progress, priority): - """ Split file list from torrent state into a directory tree. + """Split file list from torrent state into a directory tree. Returns: @@ -425,9 +422,9 @@ class TorrentDetail(BaseMode, PopupsHandler): attr = 'bold' if attr: - color_string = '{!%s,%s,%s!}' % (fg, bg, attr) + color_string = f'{{!{fg},{bg},{attr}!}}' else: - color_string = '{!%s,%s!}' % (fg, bg) + color_string = f'{{!{fg},{bg}!}}' # actually draw the dir/file string if fl[3] and fl[4]: # this is an expanded directory @@ -439,7 +436,7 @@ class TorrentDetail(BaseMode, PopupsHandler): r = format_row( [ - '%s%s %s' % (' ' * depth, xchar, fl[0]), + '{}{} {}'.format(' ' * depth, xchar, fl[0]), fsize(fl[2]), fl[5], format_priority(fl[6]), @@ -447,7 +444,7 @@ class TorrentDetail(BaseMode, PopupsHandler): self.column_widths, ) - self.add_string(off, '%s%s' % (color_string, r), trim=False) + self.add_string(off, f'{color_string}{r}', trim=False) off += 1 if fl[3] and fl[4]: @@ -502,7 +499,7 @@ class TorrentDetail(BaseMode, PopupsHandler): download_color = colors.state_color['Downloading'] def add_field(name, row, pre_color='{!info!}', post_color='{!input!}'): - s = '%s%s: %s%s' % ( + s = '{}{}: {}{}'.format( pre_color, torrent_data_fields[name]['name'], post_color, @@ -523,7 +520,7 @@ class TorrentDetail(BaseMode, PopupsHandler): if status['progress'] != 100.0: s += '/%s' % fsize(status['total_wanted']) if status['download_payload_rate'] > 0: - s += ' {!yellow!}@ %s%s' % ( + s += ' {{!yellow!}}@ {}{}'.format( download_color, fsize(status['download_payload_rate']), ) @@ -534,7 +531,7 @@ class TorrentDetail(BaseMode, PopupsHandler): # Print UL info and ratio s = add_field('uploaded', 0, download_color) if status['upload_payload_rate'] > 0: - s += ' {!yellow!}@ %s%s' % ( + s += ' {{!yellow!}}@ {}{}'.format( colors.state_color['Seeding'], fsize(status['upload_payload_rate']), ) @@ -542,13 +539,13 @@ class TorrentDetail(BaseMode, PopupsHandler): row = self.add_string(row, s) # Seed/peer info - s = '{!info!}%s:{!green!} %s {!input!}(%s)' % ( + s = '{{!info!}}{}:{{!green!}} {} {{!input!}}({})'.format( torrent_data_fields['seeds']['name'], status['num_seeds'], status['total_seeds'], ) row = self.add_string(row, s) - s = '{!info!}%s:{!red!} %s {!input!}(%s)' % ( + s = '{{!info!}}{}:{{!red!}} {} {{!input!}}({})'.format( torrent_data_fields['peers']['name'], status['num_peers'], status['total_peers'], @@ -557,7 +554,7 @@ class TorrentDetail(BaseMode, PopupsHandler): # Tracker tracker_color = '{!green!}' if status['message'] == 'OK' else '{!red!}' - s = '{!info!}%s: {!magenta!}%s{!input!} says "%s%s{!input!}"' % ( + s = '{{!info!}}{}: {{!magenta!}}{}{{!input!}} says "{}{}{{!input!}}"'.format( torrent_data_fields['tracker']['name'], status['tracker_host'], tracker_color, @@ -566,13 +563,13 @@ class TorrentDetail(BaseMode, PopupsHandler): row = self.add_string(row, s) # Pieces and availability - s = '{!info!}%s: {!yellow!}%s {!input!}x {!yellow!}%s' % ( + s = '{{!info!}}{}: {{!yellow!}}{} {{!input!}}x {{!yellow!}}{}'.format( torrent_data_fields['pieces']['name'], status['num_pieces'], fsize(status['piece_length']), ) if status['distributed_copies']: - s += '{!info!}%s: {!input!}%s' % ( + s += '{{!info!}}{}: {{!input!}}{}'.format( torrent_data_fields['seed_rank']['name'], status['seed_rank'], ) @@ -710,7 +707,7 @@ class TorrentDetail(BaseMode, PopupsHandler): 'skip_priority', '_Skip', foreground='red', - cb_arg=FILE_PRIORITY['Low'], + cb_arg=FILE_PRIORITY['Skip'], was_empty=was_empty, ) popup.add_line( @@ -878,7 +875,7 @@ class TorrentDetail(BaseMode, PopupsHandler): idx += 1 continue if num == idx: - return '%s%s/' % (path, element[0]) + return f'{path}{element[0]}/' if element[4]: i = self._get_full_folder_path( num, element[3], path + element[0] + '/', idx + 1 @@ -923,7 +920,7 @@ class TorrentDetail(BaseMode, PopupsHandler): self.popup.close(None, call_cb=False) return old_fname = self._get_full_folder_path(self.current_file_idx) - new_fname = '%s/%s/' % ( + new_fname = '{}/{}/'.format( old_fname.strip('/').rpartition('/')[0], result['new_foldername']['value'], ) @@ -949,7 +946,7 @@ class TorrentDetail(BaseMode, PopupsHandler): ): self.popup.close(None, call_cb=False) return - fname = '%s/%s' % ( + fname = '{}/{}'.format( self.full_names[idx].rpartition('/')[0], result['new_filename']['value'], ) @@ -1019,8 +1016,8 @@ class TorrentDetail(BaseMode, PopupsHandler): elif c == ord('h'): self.push_popup(MessagePopup(self, 'Help', HELP_STR, width_req=0.75)) elif c == ord('j'): - self.file_list_up() - elif c == ord('k'): self.file_list_down() + elif c == ord('k'): + self.file_list_up() self.refresh() diff --git a/deluge/ui/console/modes/torrentlist/__init__.py b/deluge/ui/console/modes/torrentlist/__init__.py index 18c4db3..48c60ce 100644 --- a/deluge/ui/console/modes/torrentlist/__init__.py +++ b/deluge/ui/console/modes/torrentlist/__init__.py @@ -1,7 +1,4 @@ -from __future__ import unicode_literals - - -class ACTION(object): +class ACTION: PAUSE = 'pause' RESUME = 'resume' REANNOUNCE = 'update_tracker' diff --git a/deluge/ui/console/modes/torrentlist/add_torrents_popup.py b/deluge/ui/console/modes/torrentlist/add_torrents_popup.py index b0ac483..3ff9ab7 100644 --- a/deluge/ui/console/modes/torrentlist/add_torrents_popup.py +++ b/deluge/ui/console/modes/torrentlist/add_torrents_popup.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.common @@ -40,7 +37,7 @@ def show_torrent_add_popup(torrentlist): def fail_cb(msg, url): log.debug('failed to add torrent: %s: %s', url, msg) - error_msg = '{!input!} * %s: {!error!}%s' % (url, msg) + error_msg = f'{{!input!}} * {url}: {{!error!}}{msg}' report_add_status(torrentlist, 0, 1, [error_msg]) def success_cb(tid, url): diff --git a/deluge/ui/console/modes/torrentlist/filtersidebar.py b/deluge/ui/console/modes/torrentlist/filtersidebar.py index 0f39b5c..982e245 100644 --- a/deluge/ui/console/modes/torrentlist/filtersidebar.py +++ b/deluge/ui/console/modes/torrentlist/filtersidebar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import curses import logging diff --git a/deluge/ui/console/modes/torrentlist/queue_mode.py b/deluge/ui/console/modes/torrentlist/queue_mode.py index 0c44aaf..33af013 100644 --- a/deluge/ui/console/modes/torrentlist/queue_mode.py +++ b/deluge/ui/console/modes/torrentlist/queue_mode.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from deluge.ui.client import client from deluge.ui.console.utils import curses_util as util from deluge.ui.console.widgets.popup import MessagePopup, SelectablePopup @@ -38,7 +35,7 @@ Change queue position of selected torrents """ -class QueueMode(object): +class QueueMode: def __init__(self, torrentslist, torrent_ids): self.torrentslist = torrentslist self.torrentview = torrentslist.torrentview diff --git a/deluge/ui/console/modes/torrentlist/search_mode.py b/deluge/ui/console/modes/torrentlist/search_mode.py index 57a8e5f..6f79628 100644 --- a/deluge/ui/console/modes/torrentlist/search_mode.py +++ b/deluge/ui/console/modes/torrentlist/search_mode.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,11 +6,8 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging -from deluge.common import PY2 from deluge.decorators import overrides from deluge.ui.console.modes.basemode import InputKeyHandler, move_cursor from deluge.ui.console.modes.torrentlist.torrentactions import torrent_actions_popup @@ -49,7 +45,7 @@ SEARCH_FORMAT = { class SearchMode(InputKeyHandler): def __init__(self, torrentlist): - super(SearchMode, self).__init__() + super().__init__() self.torrentlist = torrentlist self.torrentview = torrentlist.torrentview self.search_state = SEARCH_EMPTY @@ -176,7 +172,7 @@ class SearchMode(InputKeyHandler): elif c > 31 and c < 256: old_search_string = self.search_string stroke = chr(c) - uchar = '' if PY2 else stroke + uchar = stroke while not uchar: try: uchar = stroke.decode(self.torrentlist.encoding) diff --git a/deluge/ui/console/modes/torrentlist/torrentactions.py b/deluge/ui/console/modes/torrentlist/torrentactions.py index f3cd395..6450118 100644 --- a/deluge/ui/console/modes/torrentlist/torrentactions.py +++ b/deluge/ui/console/modes/torrentlist/torrentactions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os @@ -46,7 +43,7 @@ def action_remove(mode=None, torrent_ids=None, **kwargs): if errors: error_msgs = '' for t_id, e_msg in errors: - error_msgs += 'Error removing torrent %s : %s\n' % (t_id, e_msg) + error_msgs += f'Error removing torrent {t_id} : {e_msg}\n' mode.report_message( 'Error(s) occured when trying to delete torrent(s).', error_msgs ) @@ -77,7 +74,7 @@ def action_remove(mode=None, torrent_ids=None, **kwargs): show_max = 6 for i, (name, state) in enumerate(status): color = colors.state_color[state] - rem_msg += '\n %s* {!input!}%s' % (color, name) + rem_msg += f'\n {color}* {{!input!}}{name}' if i == show_max - 1: if i < len(status) - 1: rem_msg += '\n {!red!}And %i more' % (len(status) - show_max) diff --git a/deluge/ui/console/modes/torrentlist/torrentlist.py b/deluge/ui/console/modes/torrentlist/torrentlist.py index a427d65..d3c32ec 100644 --- a/deluge/ui/console/modes/torrentlist/torrentlist.py +++ b/deluge/ui/console/modes/torrentlist/torrentlist.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from collections import deque @@ -179,7 +176,7 @@ class TorrentList(BaseMode, PopupsHandler): @overrides(BaseMode) def resume(self): - super(TorrentList, self).resume() + super().resume() @overrides(BaseMode) def on_resize(self, rows, cols): @@ -222,7 +219,9 @@ class TorrentList(BaseMode, PopupsHandler): # Update the status bars statusbar_args = {'scr': self.stdscr, 'bottombar_help': True} if self.torrentview.curr_filter is not None: - statusbar_args['topbar'] = '%s {!filterstatus!}Current filter: %s' % ( + statusbar_args[ + 'topbar' + ] = '{} {{!filterstatus!}}Current filter: {}'.format( self.statusbars.topbar, self.torrentview.curr_filter, ) diff --git a/deluge/ui/console/modes/torrentlist/torrentview.py b/deluge/ui/console/modes/torrentlist/torrentview.py index 67de3e7..1ce5097 100644 --- a/deluge/ui/console/modes/torrentlist/torrentview.py +++ b/deluge/ui/console/modes/torrentlist/torrentview.py @@ -1,12 +1,9 @@ -# -*- coding: utf-8 -*- # # This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with # the additional special exception to link portions of this program with the OpenSSL library. # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -90,7 +87,7 @@ for col_i, col_name in enumerate(torrentviewcolumns.column_pref_names): class TorrentView(InputKeyHandler): def __init__(self, torrentlist, config): - super(TorrentView, self).__init__() + super().__init__() self.torrentlist = torrentlist self.config = config self.filter_dict = {} @@ -331,7 +328,7 @@ class TorrentView(InputKeyHandler): self.torrentlist.add_string( currow + self.torrentlist_offset, - '%s%s' % (colorstr, row[0]), + f'{colorstr}{row[0]}', trim=False, scr=self.torrentlist.torrentview_panel, ) @@ -467,9 +464,9 @@ class TorrentView(InputKeyHandler): ) self.torrentlist.refresh() elif c == ord('j'): - affected_lines = self._scroll_up(1) - elif c == ord('k'): affected_lines = self._scroll_down(1) + elif c == ord('k'): + affected_lines = self._scroll_up(1) elif c == ord('m'): self.mark_unmark(self.cursel) affected_lines = [self.cursel] diff --git a/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py b/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py index 9dff843..586a569 100644 --- a/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py +++ b/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from deluge.decorators import overrides from deluge.ui.console.utils import curses_util as util from deluge.ui.console.utils.column import torrent_data_fields diff --git a/deluge/ui/console/parser.py b/deluge/ui/console/parser.py index 27f2485..c0686b1 100644 --- a/deluge/ui/console/parser.py +++ b/deluge/ui/console/parser.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import print_function, unicode_literals - import argparse import shlex @@ -23,13 +20,13 @@ class OptionParserError(Exception): class ConsoleBaseParser(argparse.ArgumentParser): def format_help(self): """Differs from ArgumentParser.format_help by adding the raw epilog - as formatted in the string. Default bahavior mangles the formatting. + as formatted in the string. Default behavior mangles the formatting. """ # Handle epilog manually to keep the text formatting epilog = self.epilog self.epilog = '' - help_str = super(ConsoleBaseParser, self).format_help() + help_str = super().format_help() if epilog is not None: help_str += epilog self.epilog = epilog @@ -51,7 +48,7 @@ class ConsoleCommandParser(ConsoleBaseParser): for cmd_line in cmd_lines: cmds = shlex.split(cmd_line) - cmd_options = super(ConsoleCommandParser, self).parse_args(args=cmds) + cmd_options = super().parse_args(args=cmds) cmd_options.command = cmds[0] command_options.append(cmd_options) @@ -60,14 +57,14 @@ class ConsoleCommandParser(ConsoleBaseParser): def parse_args(self, args=None): """Parse known UI args and handle common and process group options. - Notes: - If started by deluge entry script this has already been done. + Notes: + If started by deluge entry script this has already been done. - Args: - args (list, optional): The arguments to parse. + Args: + args (list, optional): The arguments to parse. - Returns: - argparse.Namespace: The parsed arguments. + Returns: + argparse.Namespace: The parsed arguments. """ from deluge.ui.ui_entry import AMBIGUOUS_CMD_ARGS @@ -96,7 +93,7 @@ class ConsoleCommandParser(ConsoleBaseParser): options = self.base_parser.parse_args(args=args) options.parsed_cmds = [] else: - options = super(ConsoleCommandParser, self).parse_args(args=args) + options = super().parse_args(args=args) options.parsed_cmds = [options] if not hasattr(options, 'remaining'): @@ -107,7 +104,7 @@ class ConsoleCommandParser(ConsoleBaseParser): class OptionParser(ConsoleBaseParser): def __init__(self, **kwargs): - super(OptionParser, self).__init__(**kwargs) + super().__init__(**kwargs) self.formatter = ConsoleColorFormatter() def exit(self, status=0, msg=None): @@ -118,9 +115,9 @@ class OptionParser(ConsoleBaseParser): def error(self, msg): """error(msg : string) - Print a usage message incorporating 'msg' to stderr and exit. - If you override this in a subclass, it should not return -- it - should either exit or raise an exception. + Print a usage message incorporating 'msg' to stderr and exit. + If you override this in a subclass, it should not return -- it + should either exit or raise an exception. """ raise OptionParserError(msg) @@ -139,5 +136,5 @@ class OptionParser(ConsoleBaseParser): def format_help(self): """Return help formatted with colors.""" - help_str = super(OptionParser, self).format_help() + help_str = super().format_help() return self.formatter.format_colors(help_str) diff --git a/deluge/ui/console/utils/colors.py b/deluge/ui/console/utils/colors.py index 587c1f3..cc414fe 100644 --- a/deluge/ui/console/utils/colors.py +++ b/deluge/ui/console/utils/colors.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import re @@ -91,8 +88,8 @@ def init_colors(): curses.init_pair(counter, fg, bg) color_pairs[(fg_name, bg_name)] = counter counter += 1 - except curses.error as ex: - log.warning('Error: %s', ex) + except (curses.error, ValueError) as ex: + log.debug(f'Color pair {fg_name} {bg_name} not available: {ex}') return counter # Create the color_pairs dict @@ -271,7 +268,7 @@ def parse_color_string(string): last_color_attr = color_pair attrs = attrs[2:] # Remove colors except KeyError: - raise BadColorString('Bad color value in tag: %s,%s' % (fg, bg)) + raise BadColorString(f'Bad color value in tag: {fg},{bg}') # Check for additional attributes and OR them to the color_pair color_pair = apply_attrs(color_pair, attrs) last_color_attr = color_pair @@ -292,7 +289,7 @@ def parse_color_string(string): return ret -class ConsoleColorFormatter(object): +class ConsoleColorFormatter: """ Format help in a way suited to deluge CmdLine mode - colors, format, indentation... """ diff --git a/deluge/ui/console/utils/column.py b/deluge/ui/console/utils/column.py index d932159..ecbe04b 100644 --- a/deluge/ui/console/utils/column.py +++ b/deluge/ui/console/utils/column.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import copy import logging diff --git a/deluge/ui/console/utils/common.py b/deluge/ui/console/utils/common.py index df1c079..fdc88c4 100644 --- a/deluge/ui/console/utils/common.py +++ b/deluge/ui/console/utils/common.py @@ -1,12 +1,9 @@ -# -*- coding: utf-8 -*- # # This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with # the additional special exception to link portions of this program with the OpenSSL library. # See LICENSE for more details. # -from __future__ import unicode_literals - TORRENT_OPTIONS = { 'max_download_speed': float, 'max_upload_speed': float, diff --git a/deluge/ui/console/utils/curses_util.py b/deluge/ui/console/utils/curses_util.py index a0cd6dc..50b0444 100644 --- a/deluge/ui/console/utils/curses_util.py +++ b/deluge/ui/console/utils/curses_util.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - try: import curses except ImportError: @@ -39,7 +36,7 @@ def is_int_chr(c): return c > 47 and c < 58 -class Curser(object): +class Curser: INVISIBLE = 0 NORMAL = 1 VERY_VISIBLE = 2 @@ -59,7 +56,7 @@ def safe_curs_set(visibility): pass -class ReadState(object): +class ReadState: IGNORED = 0 READ = 1 CHANGED = 2 diff --git a/deluge/ui/console/utils/format_utils.py b/deluge/ui/console/utils/format_utils.py index 029fb20..50ec191 100644 --- a/deluge/ui/console/utils/format_utils.py +++ b/deluge/ui/console/utils/format_utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import re from collections import deque from unicodedata import east_asian_width, normalize @@ -98,7 +95,7 @@ def f_seedrank_dash(seed_rank, seeding_time): def ftotal_sized(first, second): - return '%s (%s)' % ( + return '{} ({})'.format( deluge.common.fsize(first, shortform=True), deluge.common.fsize(second, shortform=True), ) @@ -159,7 +156,7 @@ def format_column(col, lim): if size >= lim - 1: return trim_string(col, lim, dbls > 0) else: - return '%s%s' % (col, ' ' * (lim - size)) + return '{}{}'.format(col, ' ' * (lim - size)) def format_row(row, column_widths): @@ -213,7 +210,7 @@ def wrap_string(string, width, min_lines=0, strip_colors=True): mtc = mtchs.popleft() - offset clr = clrs.popleft() end_pos += len(clr) - s = '%s%s%s' % (s[:mtc], clr, s[mtc:]) + s = f'{s[:mtc]}{clr}{s[mtc:]}' return s for s in s1: @@ -238,11 +235,11 @@ def wrap_string(string, width, min_lines=0, strip_colors=True): else: cstr = s - def append_indent(l, string, offset): + def append_indent(line, string, offset): """Prepends indent to string if specified""" if indent and offset != 0: string = indent + string - l.append(string) + line.append(string) while cstr: # max with for a line. If indent is specified, we account for this @@ -290,7 +287,7 @@ def wrap_string(string, width, min_lines=0, strip_colors=True): last_color_string = '' for i, line in enumerate(ret): if i != 0: - ret[i] = '%s%s' % (last_color_string, ret[i]) + ret[i] = f'{last_color_string}{ret[i]}' colors = re.findall('\\{![^!]+!\\}', line) if colors: @@ -313,9 +310,9 @@ def pad_string(string, length, character=' ', side='right'): w = strwidth(string) diff = length - w if side == 'left': - return '%s%s' % (character * diff, string) + return f'{character * diff}{string}' elif side == 'right': - return '%s%s' % (string, character * diff) + return f'{string}{character * diff}' def delete_alt_backspace(input_text, input_cursor, sep_chars=' *?!._~-#$^;\'"/'): diff --git a/deluge/ui/console/widgets/__init__.py b/deluge/ui/console/widgets/__init__.py index a11e3f2..bc88a3b 100644 --- a/deluge/ui/console/widgets/__init__.py +++ b/deluge/ui/console/widgets/__init__.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from deluge.ui.console.widgets.inputpane import BaseInputPane from deluge.ui.console.widgets.statusbars import StatusBars from deluge.ui.console.widgets.window import BaseWindow diff --git a/deluge/ui/console/widgets/fields.py b/deluge/ui/console/widgets/fields.py index 1966c66..d8d892d 100644 --- a/deluge/ui/console/widgets/fields.py +++ b/deluge/ui/console/widgets/fields.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> @@ -9,12 +8,9 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os -from deluge.common import PY2 from deluge.decorators import overrides from deluge.ui.console.modes.basemode import InputKeyHandler from deluge.ui.console.utils import colors @@ -35,7 +31,7 @@ log = logging.getLogger(__name__) class BaseField(InputKeyHandler): def __init__(self, parent=None, name=None, selectable=True, **kwargs): - super(BaseField, self).__init__() + super().__init__() self.name = name self.parent = parent self.fmt_keys = {} @@ -74,7 +70,7 @@ class BaseField(InputKeyHandler): def build_fmt_string(self, focused, active, value_key='msg', **kwargs): color_key, font_key = self.get_fmt_keys(focused, active, **kwargs) - return '{!%%(%s)s,%%(%s)s!}%%(%s)s{!%%(%s)s!}' % ( + return '{{!%({})s,%({})s!}}%({})s{{!%({})s!}}'.format( color_key, font_key, value_key, @@ -176,7 +172,7 @@ class InfoField(NoInputField): NoInputField.__init__(self, parent=parent, name=name, **kwargs) self.label = label self.value = value - self.txt = '%s %s' % (label, value) + self.txt = f'{label} {value}' @overrides(BaseField) def render(self, screen, row, col=0, **kwargs): @@ -187,9 +183,9 @@ class InfoField(NoInputField): def set_value(self, v): self.value = v if isinstance(v, float): - self.txt = '%s %.2f' % (self.label, self.value) + self.txt = f'{self.label} {self.value:.2f}' else: - self.txt = '%s %s' % (self.label, self.value) + self.txt = f'{self.label} {self.value}' class CheckedInput(InputField): @@ -202,7 +198,7 @@ class CheckedInput(InputField): checked_char='X', unchecked_char=' ', checkbox_format='[%s] ', - **kwargs + **kwargs, ): InputField.__init__(self, parent, name, message, **kwargs) self.set_value(checked) @@ -231,9 +227,7 @@ class CheckedInput(InputField): @overrides(BaseField) def get_fmt_keys(self, focused, active, **kwargs): - color_key, font_key = super(CheckedInput, self).get_fmt_keys( - focused, active, **kwargs - ) + color_key, font_key = super().get_fmt_keys(focused, active, **kwargs) if self.checked: color_key += '_checked' font_key += '_checked' @@ -284,7 +278,7 @@ class CheckedPlusInput(CheckedInput): child_always_visible=False, show_usage_hints=True, msg_fmt='%s ', - **kwargs + **kwargs, ): CheckedInput.__init__(self, parent, name, message, **kwargs) self.child = child @@ -372,7 +366,7 @@ class IntSpinInput(InputField): incr_large=10, strict_validation=False, fmt='%d', - **kwargs + **kwargs, ): InputField.__init__(self, parent, name, message, **kwargs) self.convert_func = int @@ -618,7 +612,7 @@ class SelectInput(InputField): active_index, active_default=False, require_select_action=True, - **kwargs + **kwargs, ): InputField.__init__(self, parent, name, message, **kwargs) self.opts = opts @@ -667,9 +661,7 @@ class SelectInput(InputField): @overrides(BaseField) def get_fmt_keys(self, focused, active, selected=False, **kwargs): - color_key, font_key = super(SelectInput, self).get_fmt_keys( - focused, active, **kwargs - ) + color_key, font_key = super().get_fmt_keys(focused, active, **kwargs) if selected: color_key += '_selected' font_key += '_selected' @@ -739,7 +731,7 @@ class TextInput(InputField): value, complete=False, activate_input=False, - **kwargs + **kwargs, ): InputField.__init__(self, parent, name, message, **kwargs) self.move_func = move_func @@ -815,7 +807,7 @@ class TextInput(InputField): focused=True, col=0, cursor_offset=0, - **kwargs + **kwargs, ): if not self.value and not active and len(self.default_value) != 0: self.value = self.default_value @@ -951,7 +943,7 @@ class TextInput(InputField): elif c > 31 and c < 256: # Emulate getwch stroke = chr(c) - uchar = '' if PY2 else stroke + uchar = stroke while not uchar: try: uchar = stroke.decode(self.parent.encoding) @@ -1061,9 +1053,9 @@ class ComboInput(InputField): # No match, so start at beginning select_in_range(0, selected) - from deluge.ui.console.widgets.popup import ( + from deluge.ui.console.widgets.popup import ( # Must import here SelectablePopup, - ) # Must import here + ) select_popup = SelectablePopup( self.parent, @@ -1081,7 +1073,7 @@ class ComboInput(InputField): choice[1], selectable=True, selected=choice[0] == self.get_value(), - **args + **args, ) self.parent.push_popup(select_popup) return util.ReadState.CHANGED @@ -1149,7 +1141,7 @@ class TextArea(TextField): for i, line in enumerate(lines): self.parent.add_string( row + i, - '%s%s' % (color, line), + f'{color}{line}', scr=screen, col=col, pad=False, @@ -1176,7 +1168,7 @@ class DividerField(NoInputField): selectable=False, fill_width=True, value_fmt='%s', - **kwargs + **kwargs, ): NoInputField.__init__( self, parent=parent, name=name, selectable=selectable, **kwargs diff --git a/deluge/ui/console/widgets/inputpane.py b/deluge/ui/console/widgets/inputpane.py index 097a6cb..d8d2175 100644 --- a/deluge/ui/console/widgets/inputpane.py +++ b/deluge/ui/console/widgets/inputpane.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> @@ -9,8 +8,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from deluge.decorators import overrides diff --git a/deluge/ui/console/widgets/popup.py b/deluge/ui/console/widgets/popup.py index d588bbb..4b0d027 100644 --- a/deluge/ui/console/widgets/popup.py +++ b/deluge/ui/console/widgets/popup.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from deluge.decorators import overrides @@ -25,7 +22,7 @@ except ImportError: log = logging.getLogger(__name__) -class ALIGN(object): +class ALIGN: TOP_LEFT = 1 TOP_CENTER = 2 TOP_RIGHT = 3 @@ -38,7 +35,7 @@ class ALIGN(object): DEFAULT = MIDDLE_CENTER -class PopupsHandler(object): +class PopupsHandler: def __init__(self): self._popups = [] diff --git a/deluge/ui/console/widgets/sidebar.py b/deluge/ui/console/widgets/sidebar.py index cc23717..4015a13 100644 --- a/deluge/ui/console/widgets/sidebar.py +++ b/deluge/ui/console/widgets/sidebar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import curses import logging diff --git a/deluge/ui/console/widgets/statusbars.py b/deluge/ui/console/widgets/statusbars.py index fcf4f2f..1b91737 100644 --- a/deluge/ui/console/widgets/statusbars.py +++ b/deluge/ui/console/widgets/statusbars.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,13 +6,12 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import deluge.common import deluge.component as component -from deluge.core.preferencesmanager import DEFAULT_PREFS from deluge.ui.client import client +DEFAULT_DAEMON_PORT = 58846 + class StatusBars(component.Component): def __init__(self): @@ -38,19 +36,23 @@ class StatusBars(component.Component): def on_get_session_status(status): self.upload = deluge.common.fsize(status['payload_upload_rate']) self.download = deluge.common.fsize(status['payload_download_rate']) - self.connections = status['num_peers'] + self.connections = status['peer.num_peers_connected'] if 'dht_nodes' in status: - self.dht = status['dht_nodes'] + self.dht = status['dht.dht_nodes'] self.update_statusbars() def on_get_external_ip(external_ip): self.external_ip = external_ip - keys = ['num_peers', 'payload_upload_rate', 'payload_download_rate'] + keys = [ + 'peer.num_peers_connected', + 'payload_upload_rate', + 'payload_download_rate', + ] if self.config['dht']: - keys.append('dht_nodes') + keys.append('dht.dht_nodes') client.core.get_session_status(keys).addCallback(on_get_session_status) client.core.get_external_ip().addCallback(on_get_external_ip) @@ -76,7 +78,7 @@ class StatusBars(component.Component): connection_info += '{!white,blue,bold!}@{!red,blue,bold!}%s' # Port - if info[1] == DEFAULT_PREFS['daemon_port']: + if info[1] == DEFAULT_DAEMON_PORT: connection_info += '{!white,blue!}:%s' else: connection_info += '{!status!}:%s' diff --git a/deluge/ui/console/widgets/window.py b/deluge/ui/console/widgets/window.py index 2ef3528..77aff88 100644 --- a/deluge/ui/console/widgets/window.py +++ b/deluge/ui/console/widgets/window.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham <nick@afternight.org> # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> @@ -9,8 +8,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from deluge.ui.console.modes.basemode import add_string, mkpad, mkpanel @@ -24,7 +21,7 @@ except ImportError: log = logging.getLogger(__name__) -class BaseWindow(object): +class BaseWindow: """ BaseWindow creates a curses screen to be used for showing panels and popup dialogs """ diff --git a/deluge/ui/coreconfig.py b/deluge/ui/coreconfig.py index ed6b614..1e2927b 100644 --- a/deluge/ui/coreconfig.py +++ b/deluge/ui/coreconfig.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component diff --git a/deluge/ui/countries.py b/deluge/ui/countries.py index fe17da1..eb94df6 100644 --- a/deluge/ui/countries.py +++ b/deluge/ui/countries.py @@ -1,10 +1,7 @@ -# -*- coding: utf-8 -*- # # This file is public domain. # -from __future__ import unicode_literals - # ISO 3166-1 country names and codes COUNTRIES = { 'AF': _('Afghanistan'), @@ -60,7 +57,7 @@ COUNTRIES = { 'CD': _('Congo, The Democratic Republic of the'), 'CK': _('Cook Islands'), 'CR': _('Costa Rica'), - 'CI': _('Cote d\'Ivoire'), + 'CI': _("Cote d'Ivoire"), 'HR': _('Croatia'), 'CU': _('Cuba'), 'CY': _('Cyprus'), @@ -122,11 +119,11 @@ COUNTRIES = { 'KZ': _('Kazakhstan'), 'KE': _('Kenya'), 'KI': _('Kiribati'), - 'KP': _('Korea, Democratic People\'s Republic of'), + 'KP': _("Korea, Democratic People's Republic of"), 'KR': _('Korea, Republic of'), 'KW': _('Kuwait'), 'KG': _('Kyrgyzstan'), - 'LA': _('Lao People\'s Democratic Republic'), + 'LA': _("Lao People's Democratic Republic"), 'LV': _('Latvia'), 'LB': _('Lebanon'), 'LS': _('Lesotho'), diff --git a/deluge/ui/data/__pycache__/__init__.cpython-37.pyc b/deluge/ui/data/__pycache__/__init__.cpython-37.pyc Binary files differdeleted file mode 100644 index 498f846..0000000 --- a/deluge/ui/data/__pycache__/__init__.cpython-37.pyc +++ /dev/null diff --git a/deluge/ui/data/pixmaps/__pycache__/__init__.cpython-37.pyc b/deluge/ui/data/pixmaps/__pycache__/__init__.cpython-37.pyc Binary files differdeleted file mode 100644 index e07f647..0000000 --- a/deluge/ui/data/pixmaps/__pycache__/__init__.cpython-37.pyc +++ /dev/null diff --git a/deluge/ui/data/pixmaps/checking16.png b/deluge/ui/data/pixmaps/checking16.png Binary files differindex 0ac2c65..6758e36 100644 --- a/deluge/ui/data/pixmaps/checking16.png +++ b/deluge/ui/data/pixmaps/checking16.png diff --git a/deluge/ui/data/pixmaps/inactive16.png b/deluge/ui/data/pixmaps/inactive16.png Binary files differindex 9f38e77..cae8b2c 100644 --- a/deluge/ui/data/pixmaps/inactive16.png +++ b/deluge/ui/data/pixmaps/inactive16.png diff --git a/deluge/ui/data/pixmaps/magnet.png b/deluge/ui/data/pixmaps/magnet.png Binary files differdeleted file mode 100644 index a192cd8..0000000 --- a/deluge/ui/data/pixmaps/magnet.png +++ /dev/null diff --git a/deluge/ui/data/pixmaps/magnet.svg b/deluge/ui/data/pixmaps/magnet.svg new file mode 100644 index 0000000..f470b33 --- /dev/null +++ b/deluge/ui/data/pixmaps/magnet.svg @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="48px" + height="48px" + id="svg16888" + sodipodi:version="0.32" + inkscape:version="1.0.2 (e86c870879, 2021-01-15)" + sodipodi:docname="magnet.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.1" + inkscape:export-filename="/home/kali/code/deluge/deluge/ui/data/pixmaps/x-deluge-copy-magnet-uri-icon.png" + inkscape:export-xdpi="227.45343" + inkscape:export-ydpi="227.45343"> + <defs + id="defs16890" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="9.3390267" + inkscape:cx="18.71894" + inkscape:cy="13.37636" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="1920" + inkscape:window-height="925" + inkscape:window-x="0" + inkscape:window-y="31" + inkscape:grid-points="true" + inkscape:document-rotation="0" + inkscape:window-maximized="1" /> + <metadata + id="metadata16893"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + <dc:date>November 2007</dc:date> + <dc:creator> + <cc:Agent> + <dc:title>Luca Ferretti</dc:title> + </cc:Agent> + </dc:creator> + <dc:source>http://www.gnome.org</dc:source> + <dc:subject> + <rdf:Bag> + <rdf:li>edit</rdf:li> + <rdf:li>copy</rdf:li> + <rdf:li>copy to clipboard</rdf:li> + </rdf:Bag> + </dc:subject> + <cc:license + rdf:resource="http://creativecommons.org/licenses/LGPL/2.1/" /> + </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/LGPL/2.1/"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/Notice" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/ShareAlike" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/SourceCode" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <path + style="stroke-width:0.084844;fill:#ff0000;fill-opacity:1" + d="m 19.334232,31.418784 a 0.90623357,1.1438367 0 0 0 0.906233,-1.14384 V 25.31832 c 0,-12.210457 12.083111,-12.191395 12.083111,0 v 4.956624 a 0.90623357,1.1438367 0 0 0 0.906233,1.14384 h 11.471402 a 0.90623357,1.1438367 0 0 0 0.906234,-1.153375 c -0.0073,-2.039841 -0.04535,-3.841381 0,-5.080542 0,-14.35515 -10.283474,-23.5058431 -19.389614,-23.5058431 -9.106142,0 -19.257461,9.1506931 -19.257461,23.5153781 0.045378,1.220091 0.015034,3.145549 0,5.071007 a 0.90623357,1.1438367 0 0 0 0.9062351,1.153375 z" + id="path1134" /> + <path + style="stroke-width:0.084844;fill:#515151;fill-opacity:0.54381627" + d="m 6.9490455,35.612845 v 7.63511 a 2.7187006,3.4315101 0 0 0 2.7186865,3.431514 h 7.85403 a 2.7187006,3.4315101 0 0 0 2.718703,-3.431514 V 35.62238 A 0.90623357,1.1438367 0 0 0 19.334232,34.478548 H 7.8552793 a 0.89792643,1.1333516 0 0 0 -0.9062338,1.134297 z" + id="path1132" /> + <path + style="stroke-width:0.084844;fill:#515151;fill-opacity:0.54381627" + d="m 33.224525,34.469013 h 11.484252 a 0.90623357,1.1438367 0 0 1 0.906219,1.143832 v 7.625583 a 2.7187006,3.4315101 0 0 1 -2.718688,3.431506 h -7.85403 a 2.7187006,3.4315101 0 0 1 -2.718702,-3.431506 v -7.625583 a 0.89792643,1.1333516 0 0 1 0.900949,-1.143832 z" + id="path267" /> + </g> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer" + style="display:none"> + <rect + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:7.09866;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3157" + width="30.975992" + height="30.975992" + x="18.097294" + y="17.130117" /> + <rect + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect11064" + width="15.487996" + height="13.551996" + x="21.323959" + y="31.327448" + ry="1.9584693" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect11066" + width="11.615996" + height="9.6799974" + x="23.259958" + y="33.263447" + ry="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 25.195958,36.167447 h 7.743998" + id="path12037" /> + <path + id="path13008" + d="m 25.195958,40.039446 h 7.743998" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect16900" + width="15.487996" + height="13.551996" + x="30.358624" + y="20.356785" + ry="2.0447536" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect16902" + width="11.615996" + height="9.6799974" + x="32.294624" + y="22.292784" + ry="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 34.230623,25.196783 h 7.743998" + id="path16904" /> + <path + id="path16906" + d="m 34.230623,29.068782 h 7.743998" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> +</svg> diff --git a/deluge/ui/data/pixmaps/magnet16.png b/deluge/ui/data/pixmaps/magnet16.png Binary files differnew file mode 100644 index 0000000..61d6dab --- /dev/null +++ b/deluge/ui/data/pixmaps/magnet16.png diff --git a/deluge/ui/data/pixmaps/magnet_add.svg b/deluge/ui/data/pixmaps/magnet_add.svg new file mode 100644 index 0000000..d9ba67a --- /dev/null +++ b/deluge/ui/data/pixmaps/magnet_add.svg @@ -0,0 +1,175 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="48px" + height="48px" + id="svg16888" + sodipodi:version="0.32" + inkscape:version="1.0.2 (e86c870879, 2021-01-15)" + sodipodi:docname="add-magnet.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.1" + inkscape:export-filename="/home/kali/code/deluge/deluge/ui/data/pixmaps/x-deluge-copy-magnet-uri-icon.png" + inkscape:export-xdpi="227.45343" + inkscape:export-ydpi="227.45343"> + <defs + id="defs16890" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="6.6036891" + inkscape:cx="24.635975" + inkscape:cy="13.381649" + inkscape:current-layer="layer3" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="1920" + inkscape:window-height="925" + inkscape:window-x="0" + inkscape:window-y="31" + inkscape:grid-points="true" + inkscape:document-rotation="0" + inkscape:window-maximized="1" /> + <metadata + id="metadata16893"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + <dc:date>November 2007</dc:date> + <dc:creator> + <cc:Agent> + <dc:title>Luca Ferretti</dc:title> + </cc:Agent> + </dc:creator> + <dc:source>http://www.gnome.org</dc:source> + <dc:subject> + <rdf:Bag> + <rdf:li>edit</rdf:li> + <rdf:li>copy</rdf:li> + <rdf:li>copy to clipboard</rdf:li> + </rdf:Bag> + </dc:subject> + <cc:license + rdf:resource="http://creativecommons.org/licenses/LGPL/2.1/" /> + </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/LGPL/2.1/"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/Notice" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/ShareAlike" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/SourceCode" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <path + style="fill:#ff0000;fill-opacity:1;stroke-width:0.084844" + d="m 19.334232,31.418784 a 0.90623357,1.1438367 0 0 0 0.906233,-1.14384 V 25.31832 c 0,-12.210457 12.083111,-12.191395 12.083111,0 v 4.956624 a 0.90623357,1.1438367 0 0 0 0.906233,1.14384 h 11.471402 a 0.90623357,1.1438367 0 0 0 0.906234,-1.153375 c -0.0073,-2.039841 -0.04535,-3.841381 0,-5.080542 0,-14.35515 -10.283474,-23.5058431 -19.389614,-23.5058431 -9.106142,0 -19.257461,9.1506931 -19.257461,23.5153781 0.045378,1.220091 0.015034,3.145549 0,5.071007 a 0.90623357,1.1438367 0 0 0 0.9062351,1.153375 z" + id="path1134" /> + <path + style="fill:#515151;fill-opacity:0.543816;stroke-width:0.084844" + d="m 6.9490455,35.612845 v 7.63511 a 2.7187006,3.4315101 0 0 0 2.7186865,3.431514 h 7.85403 a 2.7187006,3.4315101 0 0 0 2.718703,-3.431514 V 35.62238 A 0.90623357,1.1438367 0 0 0 19.334232,34.478548 H 7.8552793 a 0.89792643,1.1333516 0 0 0 -0.9062338,1.134297 z" + id="path1132" /> + <path + style="fill:#515151;fill-opacity:0.543816;stroke-width:0.084844" + d="m 33.224525,34.469013 h 11.484252 a 0.90623357,1.1438367 0 0 1 0.906219,1.143832 v 7.625583 a 2.7187006,3.4315101 0 0 1 -2.718688,3.431506 h -7.85403 a 2.7187006,3.4315101 0 0 1 -2.718702,-3.431506 v -7.625583 a 0.89792643,1.1333516 0 0 1 0.900949,-1.143832 z" + id="path267" /> + </g> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer" + style="display:none"> + <rect + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:7.09866;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3157" + width="30.975992" + height="30.975992" + x="18.097294" + y="17.130117" /> + <rect + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect11064" + width="15.487996" + height="13.551996" + x="21.323959" + y="31.327448" + ry="1.9584693" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect11066" + width="11.615996" + height="9.6799974" + x="23.259958" + y="33.263447" + ry="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 25.195958,36.167447 h 7.743998" + id="path12037" /> + <path + id="path13008" + d="m 25.195958,40.039446 h 7.743998" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect16900" + width="15.487996" + height="13.551996" + x="30.358624" + y="20.356785" + ry="2.0447536" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect16902" + width="11.615996" + height="9.6799974" + x="32.294624" + y="22.292784" + ry="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 34.230623,25.196783 h 7.743998" + id="path16904" /> + <path + id="path16906" + d="m 34.230623,29.068782 h 7.743998" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + <g + inkscape:groupmode="layer" + id="layer3" + inkscape:label="Layer 3"> + <path + id="text1353" + style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;fill:#03ff48;fill-opacity:1;stroke:#000000;stroke-width:0.674513;stroke-opacity:1" + d="m 8.4078166,2.1897337 c 10e-7,1.9963132 0,3.9926271 0,5.9889403 -2.445989,0 -4.891978,0 -7.337967,0 0,2.545234 0,5.090468 0,7.635701 2.445989,0 4.891978,0 7.337967,0 0,2.445989 0,4.891978 0,7.337968 2.5364514,0 5.0729034,0 7.6093534,0 1e-6,-2.44599 0,-4.891979 0,-7.337968 2.44599,0 4.891979,0 7.337968,0 0,-2.545233 0,-5.090467 0,-7.635701 -2.445989,0 -4.891978,0 -7.337968,0 1e-6,-2.445989 0,-4.891978 0,-7.33796734 -2.53645,0 -5.072902,0 -7.6093534,0 0,0.44967504 10e-7,0.89935104 0,1.34902704 z" /> + </g> +</svg> diff --git a/deluge/ui/data/pixmaps/magnet_add16.png b/deluge/ui/data/pixmaps/magnet_add16.png Binary files differnew file mode 100644 index 0000000..37c1c36 --- /dev/null +++ b/deluge/ui/data/pixmaps/magnet_add16.png diff --git a/deluge/ui/data/pixmaps/magnet_copy.svg b/deluge/ui/data/pixmaps/magnet_copy.svg new file mode 100644 index 0000000..dd4ba42 --- /dev/null +++ b/deluge/ui/data/pixmaps/magnet_copy.svg @@ -0,0 +1,163 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="48px" + height="48px" + id="svg16888" + sodipodi:version="0.32" + inkscape:version="1.0.2 (e86c870879, 2021-01-15)" + sodipodi:docname="copy-magnet.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.1"> + <defs + id="defs16890" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="9.3390267" + inkscape:cx="20.00387" + inkscape:cy="13.37636" + inkscape:current-layer="layer2" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="1920" + inkscape:window-height="925" + inkscape:window-x="0" + inkscape:window-y="31" + inkscape:grid-points="true" + inkscape:document-rotation="0" + inkscape:window-maximized="1" /> + <metadata + id="metadata16893"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + <dc:date>November 2007</dc:date> + <dc:creator> + <cc:Agent> + <dc:title>Luca Ferretti</dc:title> + </cc:Agent> + </dc:creator> + <dc:source>http://www.gnome.org</dc:source> + <dc:subject> + <rdf:Bag> + <rdf:li>edit</rdf:li> + <rdf:li>copy</rdf:li> + <rdf:li>copy to clipboard</rdf:li> + </rdf:Bag> + </dc:subject> + <cc:license + rdf:resource="http://creativecommons.org/licenses/LGPL/2.1/" /> + </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/LGPL/2.1/"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/Notice" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/ShareAlike" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/SourceCode" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <path + style="stroke-width:0.084844;fill:#ff0000;fill-opacity:1" + d="m 19.334232,31.418784 a 0.90623357,1.1438367 0 0 0 0.906233,-1.14384 V 25.31832 c 0,-12.210457 12.083111,-12.191395 12.083111,0 v 4.956624 a 0.90623357,1.1438367 0 0 0 0.906233,1.14384 h 11.471402 a 0.90623357,1.1438367 0 0 0 0.906234,-1.153375 c -0.0073,-2.039841 -0.04535,-3.841381 0,-5.080542 0,-14.35515 -10.283474,-23.5058431 -19.389614,-23.5058431 -9.106142,0 -19.257461,9.1506931 -19.257461,23.5153781 0.045378,1.220091 0.015034,3.145549 0,5.071007 a 0.90623357,1.1438367 0 0 0 0.9062351,1.153375 z" + id="path1134" /> + <path + style="stroke-width:0.084844;fill:#515151;fill-opacity:0.54381627" + d="m 6.9490455,35.612845 v 7.63511 a 2.7187006,3.4315101 0 0 0 2.7186865,3.431514 h 7.85403 a 2.7187006,3.4315101 0 0 0 2.718703,-3.431514 V 35.62238 A 0.90623357,1.1438367 0 0 0 19.334232,34.478548 H 7.8552793 a 0.89792643,1.1333516 0 0 0 -0.9062338,1.134297 z" + id="path1132" /> + <path + style="stroke-width:0.084844;fill:#515151;fill-opacity:0.54381627" + d="m 33.224525,34.469013 h 11.484252 a 0.90623357,1.1438367 0 0 1 0.906219,1.143832 v 7.625583 a 2.7187006,3.4315101 0 0 1 -2.718688,3.431506 h -7.85403 a 2.7187006,3.4315101 0 0 1 -2.718702,-3.431506 v -7.625583 a 0.89792643,1.1333516 0 0 1 0.900949,-1.143832 z" + id="path267" /> + </g> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer" + style="display:inline"> + <rect + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:7.09866;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3157" + width="30.975992" + height="30.975992" + x="18.097294" + y="17.130117" /> + <rect + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect11064" + width="15.487996" + height="13.551996" + x="21.323959" + y="31.327448" + ry="1.9584693" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect11066" + width="11.615996" + height="9.6799974" + x="23.259958" + y="33.263447" + ry="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 25.195958,36.167447 h 7.743998" + id="path12037" /> + <path + id="path13008" + d="m 25.195958,40.039446 h 7.743998" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect16900" + width="15.487996" + height="13.551996" + x="30.358624" + y="20.356785" + ry="2.0447536" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.16267;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect16902" + width="11.615996" + height="9.6799974" + x="32.294624" + y="22.292784" + ry="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 34.230623,25.196783 h 7.743998" + id="path16904" /> + <path + id="path16906" + d="m 34.230623,29.068782 h 7.743998" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.936;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> +</svg> diff --git a/deluge/ui/data/pixmaps/magnet_copy16.png b/deluge/ui/data/pixmaps/magnet_copy16.png Binary files differnew file mode 100644 index 0000000..a4be9d2 --- /dev/null +++ b/deluge/ui/data/pixmaps/magnet_copy16.png diff --git a/deluge/ui/data/share/applications/deluge.desktop.in b/deluge/ui/data/share/applications/deluge.desktop.in index c952d42..4335b6d 100644 --- a/deluge/ui/data/share/applications/deluge.desktop.in +++ b/deluge/ui/data/share/applications/deluge.desktop.in @@ -4,6 +4,7 @@ _Name=Deluge _GenericName=BitTorrent Client _X-GNOME-FullName=Deluge BitTorrent Client _Comment=Download and share files over BitTorrent +_Keywords=bittorrent;torrent;magnet;download;p2p;torrents;downloading;uploading;share;sharing; TryExec=deluge-gtk Exec=deluge-gtk %U Icon=deluge diff --git a/deluge/ui/gtk3/__init__.py b/deluge/ui/gtk3/__init__.py index 3b9d2b1..8db2773 100644 --- a/deluge/ui/gtk3/__init__.py +++ b/deluge/ui/gtk3/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,13 +6,14 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging +from os import environ from deluge.ui.ui import UI log = logging.getLogger(__name__) +# Hide pygame community banner +environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1' # Keep this class in __init__.py to avoid the console having to import everything in gtkui.py @@ -22,7 +22,7 @@ class Gtk(UI): cmd_description = """GTK-based graphical user interface""" def __init__(self, *args, **kwargs): - super(Gtk, self).__init__( + super().__init__( 'gtk', *args, description='Starts the Deluge GTK+ interface', **kwargs ) @@ -39,10 +39,11 @@ class Gtk(UI): ) def start(self): - super(Gtk, self).start() - from .gtkui import GtkUI + super().start() import deluge.common + from .gtkui import GtkUI + def run(options): try: gtkui = GtkUI(options) diff --git a/deluge/ui/gtk3/aboutdialog.py b/deluge/ui/gtk3/aboutdialog.py index 9974a13..fe3452b 100644 --- a/deluge/ui/gtk3/aboutdialog.py +++ b/deluge/ui/gtk3/aboutdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Marcos Mobley ('markybob') <markybob@gmail.com> # @@ -7,7 +6,7 @@ # See LICENSE for more details. # -from __future__ import unicode_literals +from datetime import date from gi.repository import Gtk @@ -18,7 +17,7 @@ from deluge.ui.client import client from .common import get_deluge_icon, get_pixbuf -class AboutDialog(object): +class AboutDialog: def __init__(self): self.about = Gtk.AboutDialog() self.about.set_transient_for(component.get('MainWindow').window) @@ -38,7 +37,7 @@ class AboutDialog(object): self.about.set_copyright( _('Copyright %(year_start)s-%(year_end)s Deluge Team') - % {'year_start': 2007, 'year_end': 2019} + % {'year_start': 2007, 'year_end': date.today().year} ) self.about.set_comments( _('A peer-to-peer file sharing program\nutilizing the BitTorrent protocol.') diff --git a/deluge/ui/gtk3/addtorrentdialog.py b/deluge/ui/gtk3/addtorrentdialog.py index 9ede710..cf3851d 100644 --- a/deluge/ui/gtk3/addtorrentdialog.py +++ b/deluge/ui/gtk3/addtorrentdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # @@ -7,11 +6,9 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging import os -from base64 import b64encode +from base64 import b64decode, b64encode from xml.sax.saxutils import escape as xml_escape from xml.sax.saxutils import unescape as xml_unescape @@ -19,6 +16,7 @@ from gi.repository import Gtk from gi.repository.GObject import TYPE_INT64, TYPE_UINT64 import deluge.component as component +from deluge.bencode import bdecode from deluge.common import ( create_magnet_uri, decode_bytes, @@ -271,6 +269,7 @@ class AddTorrentDialog(component.Component): return if metadata: + metadata = bdecode(b64decode(metadata)) info = TorrentInfo.from_metadata(metadata, [[t] for t in trackers]) self.files[info_hash] = info.files self.infos[info_hash] = info.filedata @@ -301,7 +300,7 @@ class AddTorrentDialog(component.Component): self.builder.get_object('prefetch_hbox').hide() def add_from_magnets(self, uris): - """Add a list of magnet uris to torrent_liststore.""" + """Add a list of magnet URIs to torrent_liststore.""" already_added = 0 for uri in uris: @@ -775,7 +774,7 @@ class AddTorrentDialog(component.Component): else: ErrorDialog( _('Invalid URL'), - '%s %s' % (url, _('is not a valid URL.')), + '{} {}'.format(url, _('is not a valid URL.')), self.dialog, ).run() @@ -817,7 +816,7 @@ class AddTorrentDialog(component.Component): dialog.destroy() ErrorDialog( _('Download Failed'), - '%s %s' % (_('Failed to download:'), url), + '{} {}'.format(_('Failed to download:'), url), details=result.getErrorMessage(), parent=self.dialog, ).run() @@ -853,11 +852,11 @@ class AddTorrentDialog(component.Component): log.debug('Create torrent tracker lines: %s', trackers_text) trackers = list(trackers_tiers_from_text(trackers_text).keys()) - # Convert the information to a magnet uri, this is just easier to + # Convert the information to a magnet URI, this is just easier to # handle this way. log.debug('trackers: %s', trackers) magnet = create_magnet_uri(infohash, infohash, trackers) - log.debug('magnet uri: %s', magnet) + log.debug('magnet URI: %s', magnet) self.add_from_magnets([magnet]) entry.set_text('') diff --git a/deluge/ui/gtk3/common.py b/deluge/ui/gtk3/common.py index 8359327..42a14b4 100644 --- a/deluge/ui/gtk3/common.py +++ b/deluge/ui/gtk3/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Marcos Mobley ('markybob') <markybob@gmail.com> # @@ -7,16 +6,14 @@ # See LICENSE for more details. # """Common functions for various parts of gtkui to use.""" -from __future__ import unicode_literals - import contextlib import logging import os +import pickle import shutil import sys -import six.moves.cPickle as pickle # noqa: N813 -from gi.repository.Gdk import SELECTION_CLIPBOARD, Display +from gi.repository.Gdk import SELECTION_CLIPBOARD, SELECTION_PRIMARY, Display from gi.repository.GdkPixbuf import Colorspace, Pixbuf from gi.repository.GLib import GError from gi.repository.Gtk import ( @@ -29,7 +26,7 @@ from gi.repository.Gtk import ( SortType, ) -from deluge.common import PY2, get_pixmap, osx_check, windows_check +from deluge.common import get_pixmap, is_ip, osx_check, windows_check log = logging.getLogger(__name__) @@ -42,7 +39,18 @@ def cmp(x, y): and strictly positive if x > y. """ - return (x > y) - (x < y) + try: + return (x > y) - (x < y) + except TypeError: + # Handle NoneType comparison + if x is None: + if y is None: + return 0 + return -1 + elif y is None: + return 1 + else: + raise def create_blank_pixbuf(size=16): @@ -51,12 +59,36 @@ def create_blank_pixbuf(size=16): return pix -def get_pixbuf(filename): +def get_pixbuf(filename: str, size: int = 0) -> Pixbuf: + """Creates a new pixbuf by loading an image from file + + Args: + filename: An image file to load + size: Specify a size constraint (equal aspect ratio) + + Returns: + A newly created pixbuf + + """ + # Skip ico and gif that cause Pixbuf crash on Windows + # https://dev.deluge-torrent.org/ticket/3501 + if windows_check() and filename.endswith(('.ico', '.gif')): + return create_blank_pixbuf(size) + + if not os.path.isabs(filename): + filename = get_pixmap(filename) + + pixbuf = None try: - return Pixbuf.new_from_file(get_pixmap(filename)) + if size: + pixbuf = Pixbuf.new_from_file_at_size(filename, size, size) + else: + pixbuf = Pixbuf.new_from_file(filename) except GError as ex: + # Failed to load the pixbuf (Bad image file), so return a blank pixbuf. log.warning(ex) - return create_blank_pixbuf() + + return pixbuf or create_blank_pixbuf(size or 16) # Status icons.. Create them from file only once to avoid constantly re-creating them. @@ -68,17 +100,6 @@ icon_queued = get_pixbuf('queued16.png') icon_checking = get_pixbuf('checking16.png') -def get_pixbuf_at_size(filename, size): - if not os.path.isabs(filename): - filename = get_pixmap(filename) - try: - return Pixbuf.new_from_file_at_size(filename, size, size) - except GError as ex: - # Failed to load the pixbuf (Bad image file), so return a blank pixbuf. - log.warning(ex) - return create_blank_pixbuf(size) - - def get_logo(size): """A Deluge logo. @@ -91,7 +112,7 @@ def get_logo(size): filename = 'deluge.svg' if windows_check(): filename = 'deluge.png' - return get_pixbuf_at_size(filename, size) + return get_pixbuf(filename, size) def build_menu_radio_list( @@ -221,14 +242,11 @@ def associate_magnet_links(overwrite=False): """ if windows_check(): - try: - import winreg - except ImportError: - import _winreg as winreg # For Python 2. + import winreg try: hkey = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'Magnet') - except WindowsError: + except OSError: overwrite = True else: winreg.CloseKey(hkey) @@ -237,7 +255,7 @@ def associate_magnet_links(overwrite=False): deluge_exe = os.path.join(os.path.dirname(sys.executable), 'deluge.exe') try: magnet_key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT, 'Magnet') - except WindowsError: + except OSError: # Could not create for all users, falling back to current user magnet_key = winreg.CreateKey( winreg.HKEY_CURRENT_USER, 'Software\\Classes\\Magnet' @@ -246,14 +264,12 @@ def associate_magnet_links(overwrite=False): winreg.SetValue(magnet_key, '', winreg.REG_SZ, 'URL:Magnet Protocol') winreg.SetValueEx(magnet_key, 'URL Protocol', 0, winreg.REG_SZ, '') winreg.SetValueEx(magnet_key, 'BrowserFlags', 0, winreg.REG_DWORD, 0x8) - winreg.SetValue( - magnet_key, 'DefaultIcon', winreg.REG_SZ, '{},0'.format(deluge_exe) - ) + winreg.SetValue(magnet_key, 'DefaultIcon', winreg.REG_SZ, f'{deluge_exe},0') winreg.SetValue( magnet_key, r'shell\open\command', winreg.REG_SZ, - '"{}" "%1"'.format(deluge_exe), + f'"{deluge_exe}" "%1"', ) winreg.CloseKey(magnet_key) @@ -267,7 +283,7 @@ def associate_magnet_links(overwrite=False): from gi.repository import GConf except ValueError: log.debug( - 'gconf not available, so will not attempt to register magnet uri handler' + 'gconf not available, so will not attempt to register magnet URI handler' ) return False else: @@ -282,11 +298,11 @@ def associate_magnet_links(overwrite=False): gconf_client.set_bool( '/desktop/gnome/url-handlers/magnet/enabled', True ) - log.info('Deluge registered as default magnet uri handler!') + log.info('Deluge registered as default magnet URI handler!') return True else: log.error( - 'Unable to register Deluge as default magnet uri handler.' + 'Unable to register Deluge as default magnet URI handler.' ) return False return False @@ -309,7 +325,7 @@ def save_pickled_state_file(filename, state): if os.path.isfile(filepath): log.debug('Creating backup of %s at: %s', filename, filepath_bak) shutil.copy2(filepath, filepath_bak) - except IOError as ex: + except OSError as ex: log.error('Unable to backup %s to %s: %s', filepath, filepath_bak, ex) else: log.info('Saving the %s at: %s', filename, filepath) @@ -320,7 +336,7 @@ def save_pickled_state_file(filename, state): _file.flush() os.fsync(_file.fileno()) shutil.move(filepath_tmp, filepath) - except (IOError, EOFError, pickle.PicklingError) as ex: + except (OSError, EOFError, pickle.PicklingError) as ex: log.error('Unable to save %s: %s', filename, ex) if os.path.isfile(filepath_bak): log.info('Restoring backup of %s from: %s', filename, filepath_bak) @@ -345,11 +361,8 @@ def load_pickled_state_file(filename): log.info('Opening %s for load: %s', filename, _filepath) try: with open(_filepath, 'rb') as _file: - if PY2: - state = pickle.load(_file) - else: - state = pickle.load(_file, encoding='utf8') - except (IOError, pickle.UnpicklingError) as ex: + state = pickle.load(_file, encoding='utf8') + except (OSError, pickle.UnpicklingError) as ex: log.warning('Unable to load %s: %s', _filepath, ex) else: log.info('Successfully loaded %s: %s', filename, _filepath) @@ -384,8 +397,8 @@ def listview_replace_treestore(listview): def get_clipboard_text(): text = ( - Clipboard.get(selection=SELECTION_CLIPBOARD).wait_for_text() - or Clipboard.get().wait_for_text() + Clipboard.get(SELECTION_CLIPBOARD).wait_for_text() + or Clipboard.get(SELECTION_PRIMARY).wait_for_text() ) if text: return text.strip() @@ -393,3 +406,30 @@ def get_clipboard_text(): def windowing(like): return like.lower() in str(type(Display.get_default())).lower() + + +def parse_ip_port(text): + """Return an IP and port from text. + + Parses both IPv4 and IPv6. + + Params: + text (str): Text to be parsed for IP and port. + + Returns: + tuple: (ip (str), port (int)) + + """ + if '.' in text: + # ipv4 + ip, __, port = text.rpartition(':') + elif '[' in text: + # ipv6 + ip, __, port = text.partition('[')[2].partition(']:') + else: + return None, None + + if ip and is_ip(ip) and port.isdigit(): + return ip, int(port) + else: + return None, None diff --git a/deluge/ui/gtk3/connectionmanager.py b/deluge/ui/gtk3/connectionmanager.py index d5883c4..b53dd8e 100644 --- a/deluge/ui/gtk3/connectionmanager.py +++ b/deluge/ui/gtk3/connectionmanager.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,11 +6,10 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os -from socket import gaierror, gethostbyname +from socket import gaierror, getaddrinfo +from urllib.parse import urlparse from gi.repository import Gtk from twisted.internet import defer, reactor @@ -26,12 +24,6 @@ from deluge.ui.hostlist import DEFAULT_PORT, LOCALHOST, HostList from .common import get_clipboard_text from .dialogs import AuthenticationDialog, ErrorDialog -try: - from urllib.parse import urlparse -except ImportError: - # PY2 fallback - from urlparse import urlparse # pylint: disable=ungrouped-imports - log = logging.getLogger(__name__) HOSTLIST_COL_ID = 0 @@ -230,7 +222,7 @@ class ConnectionManager(component.Component): __, host, port, __, __, status, __, __ = model[row] try: - gethostbyname(host) + getaddrinfo(host, None) except gaierror as ex: log.error( 'Error resolving host %s to ip: %s', row[HOSTLIST_COL_HOST], ex.args[1] diff --git a/deluge/ui/gtk3/createtorrentdialog.py b/deluge/ui/gtk3/createtorrentdialog.py index 1e5e73c..e9f1690 100644 --- a/deluge/ui/gtk3/createtorrentdialog.py +++ b/deluge/ui/gtk3/createtorrentdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging import os.path from base64 import b64encode @@ -31,7 +28,7 @@ from .torrentview_data_funcs import cell_data_size log = logging.getLogger(__name__) -class CreateTorrentDialog(object): +class CreateTorrentDialog: def __init__(self): pass diff --git a/deluge/ui/gtk3/details_tab.py b/deluge/ui/gtk3/details_tab.py index 2431e08..04a5eab 100644 --- a/deluge/ui/gtk3/details_tab.py +++ b/deluge/ui/gtk3/details_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from xml.sax.saxutils import escape as xml_escape @@ -23,7 +20,7 @@ log = logging.getLogger(__name__) class DetailsTab(Tab): def __init__(self): - super(DetailsTab, self).__init__('Details', 'details_tab', 'details_tab_label') + super().__init__('Details', 'details_tab', 'details_tab_label') self.add_tab_widget('summary_name', None, ('name',)) self.add_tab_widget('summary_total_size', fsize, ('total_size',)) @@ -65,7 +62,7 @@ class DetailsTab(Tab): txt = xml_escape(self.widget_status_as_fstr(widget, status)) if decode_bytes(widget.obj.get_text()) != txt: if 'comment' in widget.status_keys and is_url(txt): - widget.obj.set_markup('<a href="%s">%s</a>' % (txt, txt)) + widget.obj.set_markup(f'<a href="{txt}">{txt}</a>') else: widget.obj.set_markup(txt) diff --git a/deluge/ui/gtk3/dialogs.py b/deluge/ui/gtk3/dialogs.py index 5169ab4..db337d3 100644 --- a/deluge/ui/gtk3/dialogs.py +++ b/deluge/ui/gtk3/dialogs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # @@ -9,7 +8,7 @@ # pylint: disable=super-on-old-class -from __future__ import unicode_literals +from collections import namedtuple from gi.repository import Gtk from twisted.internet import defer @@ -17,7 +16,9 @@ from twisted.internet import defer import deluge.component as component from deluge.common import windows_check -from .common import get_deluge_icon, get_pixbuf_at_size +from .common import get_deluge_icon, get_pixbuf + +Account = namedtuple('Account', 'username password authlevel') class BaseDialog(Gtk.Dialog): @@ -34,7 +35,7 @@ class BaseDialog(Gtk.Dialog): :param parent: gtkWindow, the parent window, if None it will default to the MainWindow """ - super(BaseDialog, self).__init__( + super().__init__( title=header, parent=parent if parent else component.get('MainWindow').window, flags=Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, @@ -55,7 +56,7 @@ class BaseDialog(Gtk.Dialog): # Hack for Windows since it doesn't support svg if icon.endswith('.svg') and windows_check(): icon = icon.rpartition('.svg')[0] + '16.png' - image.set_from_pixbuf(get_pixbuf_at_size(icon, 24)) + image.set_from_pixbuf(get_pixbuf(icon, 24)) else: image.set_from_icon_name(icon, Gtk.IconSize.LARGE_TOOLBAR) image.set_alignment(0.5, 0.0) @@ -72,12 +73,12 @@ class BaseDialog(Gtk.Dialog): self.vbox.show_all() def _on_delete_event(self, widget, event): - self.deferred.callback(Gtk.ResponseType.DELETE_EVENT) self.destroy() + self.deferred.callback(Gtk.ResponseType.DELETE_EVENT) def _on_response(self, widget, response): - self.deferred.callback(response) self.destroy() + self.deferred.callback(response) def run(self): """ @@ -103,13 +104,15 @@ class YesNoDialog(BaseDialog): :param text: see `:class:BaseDialog` :param parent: see `:class:BaseDialog` """ - super(YesNoDialog, self).__init__( + super().__init__( header, text, 'dialog-question', (_('_No'), Gtk.ResponseType.NO, _('_Yes'), Gtk.ResponseType.YES), parent, ) + # Use the preferred size calculated from the content + self.set_default_size(-1, -1) class InformationDialog(BaseDialog): @@ -125,7 +128,7 @@ class InformationDialog(BaseDialog): :param text: see `:class:BaseDialog` :param parent: see `:class:BaseDialog` """ - super(InformationDialog, self).__init__( + super().__init__( header, text, 'dialog-information', @@ -152,13 +155,13 @@ class ErrorDialog(BaseDialog): :param traceback: show the traceback information in the details area :type traceback: bool """ - super(ErrorDialog, self).__init__( + super().__init__( header, text, 'dialog-error', (_('_Close'), Gtk.ResponseType.CLOSE), parent ) if traceback: - import traceback import sys + import traceback tb = sys.exc_info() tb = traceback.format_exc(tb[2]) @@ -196,7 +199,7 @@ class AuthenticationDialog(BaseDialog): :param err_msg: the error message we got back from the server :type err_msg: string """ - super(AuthenticationDialog, self).__init__( + super().__init__( _('Authenticate'), err_msg, 'dialog-password', @@ -253,7 +256,7 @@ class AccountDialog(BaseDialog): parent=None, ): if username: - super(AccountDialog, self).__init__( + super().__init__( _('Edit Account'), _('Edit existing account'), 'dialog-information', @@ -266,7 +269,7 @@ class AccountDialog(BaseDialog): parent, ) else: - super(AccountDialog, self).__init__( + super().__init__( _('New Account'), _('Create a new account'), 'dialog-information', @@ -274,21 +277,21 @@ class AccountDialog(BaseDialog): parent, ) - self.levels_mapping = levels_mapping + self.account = None table = Gtk.Table(2, 3, False) - self.username_label = Gtk.Label() - self.username_label.set_markup('<b>' + _('Username:') + '</b>') - self.username_label.set_alignment(1.0, 0.5) - self.username_label.set_padding(5, 5) + username_label = Gtk.Label() + username_label.set_markup('<b>' + _('Username:') + '</b>') + username_label.set_alignment(1.0, 0.5) + username_label.set_padding(5, 5) self.username_entry = Gtk.Entry() - table.attach(self.username_label, 0, 1, 0, 1) + table.attach(username_label, 0, 1, 0, 1) table.attach(self.username_entry, 1, 2, 0, 1) - self.authlevel_label = Gtk.Label() - self.authlevel_label.set_markup('<b>' + _('Authentication Level:') + '</b>') - self.authlevel_label.set_alignment(1.0, 0.5) - self.authlevel_label.set_padding(5, 5) + authlevel_label = Gtk.Label() + authlevel_label.set_markup('<b>' + _('Authentication Level:') + '</b>') + authlevel_label.set_alignment(1.0, 0.5) + authlevel_label.set_padding(5, 5) # combo_box_new_text is deprecated but no other pygtk alternative. self.authlevel_combo = Gtk.ComboBoxText() @@ -303,16 +306,16 @@ class AccountDialog(BaseDialog): if active_idx is not None: self.authlevel_combo.set_active(active_idx) - table.attach(self.authlevel_label, 0, 1, 1, 2) + table.attach(authlevel_label, 0, 1, 1, 2) table.attach(self.authlevel_combo, 1, 2, 1, 2) - self.password_label = Gtk.Label() - self.password_label.set_markup('<b>' + _('Password:') + '</b>') - self.password_label.set_alignment(1.0, 0.5) - self.password_label.set_padding(5, 5) + password_label = Gtk.Label() + password_label.set_markup('<b>' + _('Password:') + '</b>') + password_label.set_alignment(1.0, 0.5) + password_label.set_padding(5, 5) self.password_entry = Gtk.Entry() self.password_entry.set_visibility(False) - table.attach(self.password_label, 0, 1, 2, 3) + table.attach(password_label, 0, 1, 2, 3) table.attach(self.password_entry, 1, 2, 2, 3) self.vbox.pack_start(table, False, False, padding=5) @@ -325,18 +328,17 @@ class AccountDialog(BaseDialog): if password: self.password_entry.set_text(username) - self.show_all() - - def get_username(self): - return self.username_entry.get_text() - - def get_password(self): - return self.password_entry.get_text() + self.vbox.show_all() - def get_authlevel(self): - combobox = self.authlevel_combo - level = combobox.get_model()[combobox.get_active()][0] - return level + def _on_response(self, widget, response): + if response == Gtk.ResponseType.OK: + self.account = Account( + self.username_entry.get_text(), + self.password_entry.get_text(), + self.authlevel_combo.get_active_text(), + ) + self.destroy() + self.deferred.callback(response) class OtherDialog(BaseDialog): @@ -357,7 +359,7 @@ class OtherDialog(BaseDialog): if not icon: icon = 'dialog-information' - super(OtherDialog, self).__init__( + super().__init__( header, text, icon, @@ -419,7 +421,7 @@ class PasswordDialog(BaseDialog): :param password_msg: the error message we got back from the server :type password_msg: string """ - super(PasswordDialog, self).__init__( + super().__init__( header=_('Password Protected'), text=password_msg, icon='dialog-password', @@ -453,3 +455,44 @@ class PasswordDialog(BaseDialog): def on_password_activate(self, widget): self.response(Gtk.ResponseType.OK) + + +class CopyMagnetDialog(BaseDialog): + """ + Displays a dialog with a magnet URI + """ + + def __init__(self, torrent_magnet='', parent=None): + super().__init__( + header=_('Copy Magnet URI'), + text='', + icon='magnet_copy.svg', + buttons=(_('_Close'), Gtk.ResponseType.CLOSE), + parent=parent, + ) + self.copied = False + + table = Gtk.Table(1, 2, False) + self.magnet_entry = Gtk.Entry() + self.magnet_entry.set_text(torrent_magnet) + self.magnet_entry.set_editable(False) + self.magnet_entry.connect('copy-clipboard', self.on_copy_emitted) + table.attach(self.magnet_entry, 0, 1, 0, 1) + + copy_button = Gtk.Button.new_with_label(_('Copy')) + copy_button.connect('clicked', self.on_copy_clicked) + table.attach(copy_button, 1, 2, 0, 1) + + self.vbox.pack_start(table, False, False, padding=5) + self.set_focus(self.magnet_entry) + + self.show_all() + + def on_copy_clicked(self, widget): + self.magnet_entry.select_region(0, -1) + self.magnet_entry.copy_clipboard() + self.magnet_entry.set_position(0) + self.copied = True + + def on_copy_emitted(self, widget): + self.copied = True diff --git a/deluge/ui/gtk3/edittrackersdialog.py b/deluge/ui/gtk3/edittrackersdialog.py index 1dfdd2a..861e392 100644 --- a/deluge/ui/gtk3/edittrackersdialog.py +++ b/deluge/ui/gtk3/edittrackersdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,12 +6,10 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path -from gi.repository import Gtk +from gi.repository import Gdk, Gtk from twisted.internet import defer import deluge.component as component @@ -77,7 +74,7 @@ def trackers_tiers_from_text(text_str=''): return trackers -class EditTrackersDialog(object): +class EditTrackersDialog: def __init__(self, torrent_id, parent=None): self.torrent_id = torrent_id self.builder = Gtk.Builder() @@ -132,6 +129,12 @@ class EditTrackersDialog(object): self.dialog.connect('delete-event', self._on_delete_event) self.dialog.connect('response', self._on_response) + self.treeview.connect('button_press_event', self.on_button_press_event) + + self.add_tracker_dialog.connect('key-press-event', self.on_key_add_press_event) + self.add_tracker_dialog.connect('delete-event', self.on_delete_event_add) + self.edit_tracker_entry.connect('key-press-event', self.on_key_edit_press_event) + self.edit_tracker_entry.connect('delete-event', self.on_delete_event_edit) def run(self): # Make sure we have a torrent_id.. if not just return @@ -191,7 +194,7 @@ class EditTrackersDialog(object): self.old_trackers = list(status['trackers']) for tracker in self.old_trackers: self.add_tracker(tracker['tier'], tracker['url']) - self.treeview.set_cursor((0)) + self.treeview.set_cursor(0) self.dialog.show() def add_tracker(self, tier, url): @@ -207,6 +210,7 @@ class EditTrackersDialog(object): # Show the add tracker dialog self.add_tracker_dialog.show() self.builder.get_object('textview_trackers').grab_focus() + self.dialog.set_sensitive(False) def on_button_remove_clicked(self, widget): log.debug('on_button_remove_clicked') @@ -215,8 +219,18 @@ class EditTrackersDialog(object): self.liststore.remove(selected) def on_button_edit_clicked(self, widget): - """edits an existing tracker""" + """edits an existing tracker on edit button click""" log.debug('on_button_edit_clicked') + self._edit_tracker() + + def on_button_press_event(self, widget, event): + """edits an existing tracker on double click on tracker name""" + if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS: + log.debug('button_press_event double click') + self._edit_tracker() + + def _edit_tracker(self): + """edits an existing tracker""" selected = self.get_selected() if selected: tracker = self.liststore.get_value(selected, 1) @@ -225,11 +239,27 @@ class EditTrackersDialog(object): self.edit_tracker_entry.grab_focus() self.dialog.set_sensitive(False) - def on_button_edit_cancel_clicked(self, widget): - log.debug('on_button_edit_cancel_clicked') + def _close_edit_dialog(self): self.dialog.set_sensitive(True) self.edit_tracker_entry.hide() + def on_button_edit_cancel_clicked(self, widget): + """handles the cancel button""" + log.debug('on_button_edit_cancel_clicked') + self._close_edit_dialog() + + def on_key_edit_press_event(self, widget, event): + """handles Escape key press""" + if event.keyval == Gdk.KEY_Escape: + log.debug('on_key_edit_press_event') + self._close_edit_dialog() + + def on_delete_event_edit(self, widget, event): + """handles the Top-Right X button""" + log.debug('on_delete_event_edit') + self._close_edit_dialog() + return True + def on_button_edit_ok_clicked(self, widget): log.debug('on_button_edit_ok_clicked') selected = self.get_selected() @@ -290,11 +320,29 @@ class EditTrackersDialog(object): # Clear the entry widget and hide the dialog textview_buf.set_text('') + self.dialog.set_sensitive(True) self.add_tracker_dialog.hide() - def on_button_add_cancel_clicked(self, widget): - log.debug('on_button_add_cancel_clicked') + def _discard_and_close_add_dialog(self): # Clear the entry widget and hide the dialog b = Gtk.TextBuffer() self.builder.get_object('textview_trackers').set_buffer(b) + self.dialog.set_sensitive(True) self.add_tracker_dialog.hide() + + def on_button_add_cancel_clicked(self, widget): + """handles the cancel button""" + log.debug('on_button_add_cancel_clicked') + self._discard_and_close_add_dialog() + + def on_key_add_press_event(self, widget, event): + """handles Escape key press""" + if event.keyval == Gdk.KEY_Escape: + log.debug('on_key_add_press_event') + self._discard_and_close_add_dialog() + + def on_delete_event_add(self, widget, event): + """handles the Top-Right X button""" + log.debug('on_delete_event_add') + self._discard_and_close_add_dialog() + return True diff --git a/deluge/ui/gtk3/files_tab.py b/deluge/ui/gtk3/files_tab.py index b3bd5b5..24c1697 100644 --- a/deluge/ui/gtk3/files_tab.py +++ b/deluge/ui/gtk3/files_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,21 +6,26 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import json import logging import os.path +import gi # isort:skip (Required before Gtk import). + +gi.require_version('Gtk', '3.0') + +# isort:imports-thirdparty from gi.repository import Gio, Gtk from gi.repository.Gdk import DragAction, ModifierType, keyval_name from gi.repository.GObject import TYPE_UINT64 +# isort:imports-firstparty import deluge.component as component from deluge.common import open_file, show_file from deluge.ui.client import client from deluge.ui.common import FILE_PRIORITY +# isort:imports-localfolder from .common import ( listview_replace_treestore, load_pickled_state_file, @@ -77,7 +81,7 @@ def cell_progress(column, cell, model, row, data): class FilesTab(Tab): def __init__(self): - super(FilesTab, self).__init__('Files', 'files_tab', 'files_tab_label') + super().__init__('Files', 'files_tab', 'files_tab_label') self.listview = self.main_builder.get_object('files_listview') # filename, size, progress string, progress value, priority, file index, icon id @@ -442,7 +446,7 @@ class FilesTab(Tab): try: value = completed_bytes / self.treestore[parent][1] * 100 except ZeroDivisionError: - # Catch the unusal error found when moving folders around + # Catch the unusual error found when moving folders around value = 0 self.treestore[parent][3] = value self.treestore[parent][2] = '%i%%' % value diff --git a/deluge/ui/gtk3/filtertreeview.py b/deluge/ui/gtk3/filtertreeview.py index bd781e0..40752d7 100644 --- a/deluge/ui/gtk3/filtertreeview.py +++ b/deluge/ui/gtk3/filtertreeview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com> # 2008 Andrew Resch <andrewresch@gmail.com> @@ -9,8 +8,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os import warnings @@ -24,7 +21,7 @@ from deluge.common import TORRENT_STATE, decode_bytes, resource_filename from deluge.configmanager import ConfigManager from deluge.ui.client import client -from .common import get_pixbuf, get_pixbuf_at_size +from .common import get_pixbuf log = logging.getLogger(__name__) @@ -89,10 +86,10 @@ class FilterTreeView(component.Component): self.treeview.set_headers_visible(False) self.treeview.set_level_indentation(-21) # Force theme to use expander-size so we don't cut out entries due to indentation hack. - Gtk.rc_parse_string( - """style "treeview-style" {GtkTreeView::expander-size = 7} - class "GtkTreeView" style "treeview-style" """ - ) + provider = Gtk.CssProvider() + provider.load_from_data(b'* {-GtkTreeView-expander-size: 9;}') + context = self.treeview.get_style_context() + context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) self.treeview.set_model(self.treestore) self.treeview.get_selection().connect('changed', self.on_selection_changed) @@ -100,11 +97,6 @@ class FilterTreeView(component.Component): self.treeview.connect('button-press-event', self.on_button_press_event) - # colors using current theme. - style_ctx = component.get('MainWindow').window.get_style_context() - self.colour_background = style_ctx.get_background_color(Gtk.StateFlags.NORMAL) - self.colour_foreground = style_ctx.get_color(Gtk.StateFlags.NORMAL) - # filtertree menu builder = Gtk.Builder() builder.add_from_file( @@ -229,7 +221,7 @@ class FilterTreeView(component.Component): label = decode_bytes(model.get_value(row, 2)) count = model.get_value(row, 3) - # Supress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed + # Suppress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed original_filters = warnings.filters[:] warnings.simplefilter('ignore') try: @@ -261,7 +253,7 @@ class FilterTreeView(component.Component): return get_pixbuf('%s16.png' % pix) def set_row_image(self, cat, value, filename): - pix = get_pixbuf_at_size(filename, 16) + pix = get_pixbuf(filename, size=16) row = self.filters[(cat, value)] self.treestore.set_value(row, 4, pix) return False diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui index a7a8cae..8adbad3 100644 --- a/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui +++ b/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui @@ -144,8 +144,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.ui index 4d36803..7183272 100644 --- a/deluge/ui/gtk3/glade/add_torrent_dialog.ui +++ b/deluge/ui/gtk3/glade/add_torrent_dialog.ui @@ -727,8 +727,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_maxup"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment2</property> <property name="update_policy">if-valid</property> </object> @@ -741,8 +739,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_maxconnections"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment3</property> </object> <packing> @@ -754,8 +750,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_maxupslots"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment4</property> </object> <packing> @@ -767,8 +761,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_maxdown"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment1</property> </object> <packing> diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui index ecbd0f7..6b75b23 100644 --- a/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui +++ b/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui @@ -143,8 +143,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/connect_peer_dialog.ui b/deluge/ui/gtk3/glade/connect_peer_dialog.ui index f5e9337..4a60751 100644 --- a/deluge/ui/gtk3/glade/connect_peer_dialog.ui +++ b/deluge/ui/gtk3/glade/connect_peer_dialog.ui @@ -128,8 +128,6 @@ <property name="width_chars">39</property> <property name="text" translatable="yes">hostname:port</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/connection_manager.addhost.ui b/deluge/ui/gtk3/glade/connection_manager.addhost.ui index 641a71c..ea5376e 100644 --- a/deluge/ui/gtk3/glade/connection_manager.addhost.ui +++ b/deluge/ui/gtk3/glade/connection_manager.addhost.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.22.1 --> +<!-- Generated with glade 3.22.2 --> <interface> <requires lib="gtk+" version="3.0"/> <object class="GtkAdjustment" id="adjustment_port"> @@ -16,7 +16,7 @@ <property name="window_position">center-on-parent</property> <property name="destroy_with_parent">True</property> <property name="type_hint">dialog</property> - <child> + <child type="titlebar"> <placeholder/> </child> <child internal-child="vbox"> @@ -24,11 +24,12 @@ <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> - <property name="spacing">2</property> + <property name="spacing">5</property> <child internal-child="action_area"> <object class="GtkButtonBox" id="dialog-action_area5"> <property name="visible">True</property> <property name="can_focus">False</property> + <property name="margin_top">15</property> <property name="layout_style">end</property> <child> <object class="GtkButton" id="button_addhost_cancel"> @@ -65,49 +66,41 @@ <property name="expand">False</property> <property name="fill">True</property> <property name="pack_type">end</property> - <property name="position">0</property> + <property name="position">1</property> </packing> </child> <child> - <object class="GtkBox" id="hbox2"> + <object class="GtkGrid"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="spacing">5</property> + <property name="row_spacing">5</property> + <property name="column_spacing">5</property> <child> <object class="GtkLabel" id="label3"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="halign">start</property> <property name="label" translatable="yes">Hostname:</property> + <property name="xalign">0</property> </object> <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> + <property name="left_attach">0</property> + <property name="top_attach">0</property> </packing> </child> <child> - <object class="GtkAlignment" id="alignment4"> + <object class="GtkEntry" id="entry_hostname"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="left_padding">1</property> - <child> - <object class="GtkEntry" id="entry_hostname"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="activates_default">True</property> - <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <signal name="paste-clipboard" handler="on_entry_host_paste_clipboard" swapped="no"/> - </object> - </child> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="invisible_char">•</property> + <property name="activates_default">True</property> + <property name="truncate_multiline">True</property> + <signal name="paste-clipboard" handler="on_entry_host_paste_clipboard" swapped="no"/> </object> <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> + <property name="left_attach">1</property> + <property name="top_attach">0</property> </packing> </child> <child> @@ -115,116 +108,90 @@ <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">Port:</property> + <property name="xalign">0</property> </object> <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> + <property name="left_attach">0</property> + <property name="top_attach">1</property> </packing> </child> <child> <object class="GtkSpinButton" id="spinbutton_port"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="halign">start</property> <property name="max_length">5</property> <property name="invisible_char">•</property> <property name="width_chars">5</property> + <property name="max_width_chars">5</property> <property name="progress_pulse_step">1</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_port</property> <property name="climb_rate">1</property> <property name="numeric">True</property> </object> <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">3</property> + <property name="left_attach">1</property> + <property name="top_attach">1</property> </packing> </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkGrid" id="table1"> - <property name="visible">True</property> - <property name="can_focus">False</property> <child> - <object class="GtkAlignment" id="alignment3"> + <object class="GtkLabel" id="label5"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="left_padding">5</property> - <child> - <object class="GtkEntry" id="entry_password"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="visibility">False</property> - <property name="invisible_char">•</property> - <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - </object> - </child> + <property name="halign">start</property> + <property name="label" translatable="yes">Username:</property> + <property name="xalign">0</property> </object> <packing> - <property name="left_attach">1</property> - <property name="top_attach">1</property> + <property name="left_attach">0</property> + <property name="top_attach">2</property> </packing> </child> <child> - <object class="GtkAlignment" id="alignment2"> + <object class="GtkEntry" id="entry_username"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="left_padding">5</property> - <child> - <object class="GtkEntry" id="entry_username"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - </object> - </child> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="invisible_char">•</property> + <property name="truncate_multiline">True</property> </object> <packing> <property name="left_attach">1</property> - <property name="top_attach">0</property> + <property name="top_attach">2</property> </packing> </child> <child> - <object class="GtkLabel" id="label5"> + <object class="GtkLabel" id="label6"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="halign">start</property> - <property name="label" translatable="yes">Username:</property> + <property name="label" translatable="yes">Password:</property> + <property name="xalign">0</property> </object> <packing> <property name="left_attach">0</property> - <property name="top_attach">0</property> + <property name="top_attach">3</property> </packing> </child> <child> - <object class="GtkLabel" id="label6"> + <object class="GtkEntry" id="entry_password"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">start</property> - <property name="label" translatable="yes">Password:</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="visibility">False</property> + <property name="invisible_char">•</property> + <property name="truncate_multiline">True</property> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> + <property name="left_attach">1</property> + <property name="top_attach">3</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">0</property> </packing> </child> </object> diff --git a/deluge/ui/gtk3/glade/connection_manager.ui b/deluge/ui/gtk3/glade/connection_manager.ui index 11516aa..44f4b34 100644 --- a/deluge/ui/gtk3/glade/connection_manager.ui +++ b/deluge/ui/gtk3/glade/connection_manager.ui @@ -30,7 +30,7 @@ <property name="modal">True</property> <property name="window_position">center-on-parent</property> <property name="default_width">300</property> - <property name="default_height">250</property> + <property name="default_height">285</property> <property name="destroy_with_parent">True</property> <property name="type_hint">dialog</property> <child> diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui index dc7b7e9..4328330 100644 --- a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui +++ b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui @@ -143,8 +143,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui index a380718..7123054 100644 --- a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui +++ b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui @@ -143,8 +143,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.ui index c27a4b8..0d15940 100644 --- a/deluge/ui/gtk3/glade/create_torrent_dialog.ui +++ b/deluge/ui/gtk3/glade/create_torrent_dialog.ui @@ -356,8 +356,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> @@ -393,8 +391,6 @@ <object class="GtkEntry" id="entry_comments"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> @@ -810,6 +806,7 @@ <property name="receives_default">True</property> <property name="use_underline">True</property> <signal name="clicked" handler="on_button_cancel_clicked" swapped="no"/> + <accelerator key="Escape" signal="clicked"/> </object> <packing> <property name="expand">False</property> diff --git a/deluge/ui/gtk3/glade/edit_trackers.edit.ui b/deluge/ui/gtk3/glade/edit_trackers.edit.ui index 2521e8f..fc3e51b 100644 --- a/deluge/ui/gtk3/glade/edit_trackers.edit.ui +++ b/deluge/ui/gtk3/glade/edit_trackers.edit.ui @@ -144,8 +144,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/main_window.tabs.ui b/deluge/ui/gtk3/glade/main_window.tabs.ui index 30bd395..7ecf618 100644 --- a/deluge/ui/gtk3/glade/main_window.tabs.ui +++ b/deluge/ui/gtk3/glade/main_window.tabs.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.22.1 --> +<!-- Generated with glade 3.22.2 --> <interface> <requires lib="gtk+" version="3.0"/> <object class="GtkAdjustment" id="spin_max_connections_adjustment"> @@ -38,14 +38,14 @@ </object> <object class="GtkWindow" id="tabs"> <property name="can_focus">False</property> - <child> + <child type="titlebar"> <placeholder/> </child> <child> <object class="GtkNotebook" id="dummy_nb_see_main_win_torrent_info"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="tab_pos">left</property> + <property name="enable_popup">True</property> <child> <object class="GtkScrolledWindow" id="status_tab"> <property name="visible">True</property> @@ -105,7 +105,7 @@ <property name="row_spacing">5</property> <property name="column_spacing">10</property> <child> - <object class="GtkLabel" id="summary_total_uploaded"> + <object class="GtkLabel" id="summary_download_speed"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="width_chars">20</property> @@ -305,7 +305,7 @@ </packing> </child> <child> - <object class="GtkLabel" id="summary_download_speed"> + <object class="GtkLabel" id="summary_total_uploaded"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="width_chars">15</property> @@ -490,7 +490,6 @@ <object class="GtkLabel" id="status_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Status</property> <property name="use_underline">True</property> </object> @@ -850,12 +849,6 @@ <child> <placeholder/> </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> </object> </child> </object> @@ -871,7 +864,6 @@ <object class="GtkLabel" id="details_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Details</property> <property name="use_underline">True</property> </object> @@ -902,7 +894,6 @@ <object class="GtkLabel" id="files_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">Fi_les</property> <property name="use_underline">True</property> </object> @@ -933,7 +924,6 @@ <object class="GtkLabel" id="peers_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Peers</property> <property name="use_underline">True</property> </object> @@ -1065,23 +1055,20 @@ </packing> </child> <child> - <placeholder/> - </child> - <child> <object class="GtkCheckButton" id="chk_super_seeding"> <property name="label" translatable="yes">Super Seeding</property> - <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> + <property name="halign">start</property> <property name="draw_indicator">True</property> <signal name="toggled" handler="on_chk_toggled" swapped="no"/> </object> <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">4</property> - </packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> </child> <child> <object class="GtkCheckButton" id="chk_move_completed"> @@ -1113,6 +1100,9 @@ <property name="position">6</property> </packing> </child> + <child> + <placeholder/> + </child> </object> </child> </object> @@ -1176,8 +1166,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_stop_ratio_adjustment</property> <property name="digits">1</property> <property name="numeric">True</property> @@ -1277,8 +1265,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_max_connections_adjustment</property> <property name="numeric">True</property> </object> @@ -1292,8 +1278,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_max_upload_adjustment</property> <property name="digits">1</property> <property name="numeric">True</property> @@ -1308,8 +1292,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_max_download_adjustment</property> <property name="climb_rate">1</property> <property name="digits">1</property> @@ -1366,8 +1348,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_max_upload_slots_adjustment</property> <property name="numeric">True</property> </object> @@ -1453,7 +1433,6 @@ <object class="GtkLabel" id="options_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Options</property> <property name="use_underline">True</property> </object> @@ -1664,7 +1643,6 @@ <object class="GtkLabel" id="trackers_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Trackers</property> <property name="use_underline">True</property> </object> diff --git a/deluge/ui/gtk3/glade/main_window.tabs.ui~ b/deluge/ui/gtk3/glade/main_window.tabs.ui~ new file mode 100644 index 0000000..4362183 --- /dev/null +++ b/deluge/ui/gtk3/glade/main_window.tabs.ui~ @@ -0,0 +1,1507 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.2 --> +<interface> + <requires lib="gtk+" version="3.0"/> + <object class="GtkAdjustment" id="spin_max_connections_adjustment"> + <property name="lower">-1</property> + <property name="upper">999999</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkAdjustment" id="spin_max_download_adjustment"> + <property name="lower">-1</property> + <property name="upper">2097151</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkAdjustment" id="spin_max_upload_adjustment"> + <property name="lower">-1</property> + <property name="upper">2097151</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkAdjustment" id="spin_max_upload_slots_adjustment"> + <property name="lower">-1</property> + <property name="upper">999999</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkAdjustment" id="spin_stop_ratio_adjustment"> + <property name="upper">999999</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkWindow" id="tabs"> + <property name="can_focus">False</property> + <child type="titlebar"> + <placeholder/> + </child> + <child> + <object class="GtkNotebook" id="dummy_nb_see_main_win_torrent_info"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="enable_popup">True</property> + <child> + <object class="GtkScrolledWindow" id="status_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkViewport" id="viewport1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="resize_mode">queue</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment43"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">5</property> + <property name="bottom_padding">2</property> + <property name="left_padding">10</property> + <property name="right_padding">10</property> + <child> + <object class="GtkBox" id="vbox3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">5</property> + <child> + <object class="GtkBox" id="status_progress_vbox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkProgressBar" id="progressbar"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="pulse_step">0.10000000149</property> + <property name="show_text">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkGrid" id="table8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">5</property> + <property name="column_spacing">10</property> + <child> + <object class="GtkLabel" id="summary_download_speed"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">20</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_total_downloaded"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_upload_speed"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">15</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_seed_rank"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_availability"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap">True</property> + <property name="wrap_mode">word-char</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_share_ratio"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_peers"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">10</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_eta"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_active_time"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_seed_time"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_last_transfer"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap">True</property> + <property name="wrap_mode">char</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_last_seen_complete"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap">True</property> + <property name="wrap_mode">char</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label42"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Down Speed:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label43"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Up Speed:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label38"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Downloaded:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label39"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Uploaded:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_seeds"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">10</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_total_uploaded"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">15</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_seeds"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Seeds:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_peers"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Peers:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label41"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Share Ratio:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_availablity"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Availability:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_seed_rank"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Seed Rank:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_eta"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">ETA Time:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_last_transfer"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Last Transfer:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_active_time"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Active Time:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_last_seen_complete"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Complete Seen:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_seed_time"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Seeding Time:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + <child type="tab"> + <object class="GtkLabel" id="status_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Status</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="details_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkViewport" id="viewport2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="resize_mode">queue</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment54"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">5</property> + <property name="bottom_padding">2</property> + <property name="left_padding">10</property> + <property name="right_padding">15</property> + <child> + <object class="GtkGrid" id="table_details"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">5</property> + <property name="column_spacing">10</property> + <child> + <object class="GtkLabel" id="summary_name"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap">True</property> + <property name="wrap_mode">word-char</property> + <property name="selectable">True</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_total_size"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_num_files"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_completed"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_date_added"> + <property name="width_request">100</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_private"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_pieces"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Pieces:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_pieces"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_hash"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap">True</property> + <property name="selectable">True</property> + <property name="width_chars">40</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_torrent_path"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap">True</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + <property name="ellipsize">start</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_comments"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">6</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_creator"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">5</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_name"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Name:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_path"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Download Folder:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_date_added"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Added:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_total_size"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Total Size:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_num_files"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Total Files:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_hash"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Hash:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_creator"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Created By:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">5</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_comments"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Comments:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">6</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_completed"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Completed:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_private"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Private Torrent:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkSeparator" id="vseparator5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + <property name="height">4</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="details_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Details</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">1</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="files_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkTreeView" id="files_listview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeselection1"/> + </child> + </object> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="files_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Fi_les</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">2</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="peers_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkTreeView" id="peers_listview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeselection2"/> + </child> + </object> + </child> + </object> + <packing> + <property name="position">3</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="peers_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Peers</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">3</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="options_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkViewport" id="viewport3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="resize_mode">queue</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">5</property> + <property name="bottom_padding">2</property> + <property name="left_padding">5</property> + <property name="right_padding">15</property> + <child> + <object class="GtkGrid" id="table6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkAlignment" id="alignment8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">2</property> + <property name="left_padding">5</property> + <property name="right_padding">5</property> + <child> + <object class="GtkBox" id="vbox1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="hbox1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel" id="label_owner"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Owner:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_owner"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">10</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_shared"> + <property name="label" translatable="yes">Shared</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_prioritize_first_last"> + <property name="label" translatable="yes">Prioritize First/Last</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_sequential_download"> + <property name="label" translatable="yes">Sequential Download</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_super_seeding"> + <property name="label" translatable="yes">Super Seeding</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_move_completed"> + <property name="label" translatable="yes">Move completed:</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_move_completed_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">5</property> + </packing> + </child> + <child> + <object class="GtkBox" id="hbox_move_completed_path_chooser"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">6</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">10</property> + <child> + <object class="GtkBox" id="vbox6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">15</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkCheckButton" id="chk_auto_managed"> + <property name="label" translatable="yes">Auto Managed</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_stop_at_ratio"> + <property name="label" translatable="yes">Stop seed at ratio:</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_stop_at_ratio_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">10</property> + <property name="right_padding">10</property> + <child> + <object class="GtkSpinButton" id="spin_stop_ratio"> + <property name="width_request">50</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_stop_ratio_adjustment</property> + <property name="digits">1</property> + <property name="numeric">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">10</property> + <child> + <object class="GtkCheckButton" id="chk_remove_at_ratio"> + <property name="label" translatable="yes">Remove at ratio</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkSeparator" id="hseparator1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">7</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkButton" id="button_apply"> + <property name="label" translatable="yes">_Apply</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + <signal name="clicked" handler="on_button_apply_clicked" swapped="no"/> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkGrid" id="table1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="border_width">5</property> + <property name="row_spacing">2</property> + <property name="column_spacing">4</property> + <child> + <object class="GtkSpinButton" id="spin_max_connections"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_max_connections_adjustment</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spin_max_upload"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_max_upload_adjustment</property> + <property name="digits">1</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spin_max_download"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_max_download_adjustment</property> + <property name="climb_rate">1</property> + <property name="digits">1</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label11"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Upload Speed:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label13"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">K/s</property> + <property name="ellipsize">start</property> + <attributes> + <attribute name="scale" value="0.90000000000000002"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label14"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">K/s</property> + <attributes> + <attribute name="scale" value="0.90000000000000002"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spin_max_upload_slots"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_max_upload_slots_adjustment</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Download Speed:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label12"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Connections:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label15"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Upload Slots:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Bandwidth Limits</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">4</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="options_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Options</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">4</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="trackers_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkTreeView" id="trackers_listview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeselection3"/> + </child> + </object> + </child> + </object> + <packing> + <property name="position">5</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="trackers_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Trackers</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">5</property> + <property name="tab_fill">False</property> + </packing> + </child> + </object> + </child> + </object> +</interface> diff --git a/deluge/ui/gtk3/glade/main_window.ui b/deluge/ui/gtk3/glade/main_window.ui index 43d8bf0..ecbb8f6 100644 --- a/deluge/ui/gtk3/glade/main_window.ui +++ b/deluge/ui/gtk3/glade/main_window.ui @@ -174,6 +174,7 @@ <property name="use_stock">False</property> <property name="accel_group">accelgroup1</property> <signal name="activate" handler="on_menuitem_quit_activate" swapped="no"/> + <accelerator key="Q" signal="activate" modifiers="GDK_CONTROL_MASK"/> </object> </child> </object> diff --git a/deluge/ui/gtk3/glade/other_dialog.ui b/deluge/ui/gtk3/glade/other_dialog.ui index 26d3d08..01a5597 100644 --- a/deluge/ui/gtk3/glade/other_dialog.ui +++ b/deluge/ui/gtk3/glade/other_dialog.ui @@ -148,8 +148,6 @@ <property name="max_length">6</property> <property name="activates_default">True</property> <property name="width_chars">6</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment1</property> </object> </child> diff --git a/deluge/ui/gtk3/glade/path_combo_chooser.ui b/deluge/ui/gtk3/glade/path_combo_chooser.ui index f79685d..871bac0 100644 --- a/deluge/ui/gtk3/glade/path_combo_chooser.ui +++ b/deluge/ui/gtk3/glade/path_combo_chooser.ui @@ -98,8 +98,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment3</property> <property name="climb_rate">1</property> <property name="numeric">True</property> @@ -571,6 +569,7 @@ <object class="GtkEntry" id="entry_text"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="hexpand">True</property> <property name="invisible_char">•</property> <signal name="changed" handler="on_entry_text_changed" swapped="no"/> <signal name="delete-text" handler="on_entry_text_delete_text" swapped="no"/> diff --git a/deluge/ui/gtk3/glade/preferences_dialog.ui b/deluge/ui/gtk3/glade/preferences_dialog.ui index e1bbc74..aa1531d 100644 --- a/deluge/ui/gtk3/glade/preferences_dialog.ui +++ b/deluge/ui/gtk3/glade/preferences_dialog.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.22.1 --> +<!-- Generated with glade 3.22.2 --> <interface> <requires lib="gtk+" version="3.0"/> <object class="GtkAdjustment" id="adjustment_cache_expiry"> @@ -245,7 +245,7 @@ <property name="type_hint">dialog</property> <signal name="configure-event" handler="on_pref_dialog_configure_event" swapped="no"/> <signal name="delete-event" handler="on_pref_dialog_delete_event" swapped="no"/> - <child> + <child type="titlebar"> <placeholder/> </child> <child internal-child="vbox"> @@ -479,6 +479,23 @@ </packing> </child> <child> + <object class="GtkCheckButton" id="urldetect_toggle"> + <property name="label" translatable="yes">Detect torrent URLs from clipboard</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="tooltip_text" translatable="yes">Automatically open Add Torrent dialog when clipboard contains a torrent URL</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_urldetect_toggle_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> <object class="GtkCheckButton" id="piecesbar_toggle"> <property name="visible">True</property> <property name="can_focus">True</property> @@ -501,7 +518,7 @@ and daemon (does not apply in Standalone mode).</property> <packing> <property name="expand">True</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">3</property> </packing> </child> <child> @@ -697,7 +714,7 @@ and daemon (does not apply in Standalone mode).</property> <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">3</property> + <property name="position">4</property> </packing> </child> </object> @@ -912,8 +929,6 @@ and daemon (does not apply in Standalone mode).</property> <property name="width_chars">16</property> <property name="text">********</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">False</property> @@ -1078,7 +1093,7 @@ and daemon (does not apply in Standalone mode).</property> <object class="GtkLabel" id="label5"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes"><b>Languge</b></property> + <property name="label" translatable="yes"><b>Language</b></property> <property name="use_markup">True</property> </object> </child> @@ -1133,6 +1148,7 @@ and daemon (does not apply in Standalone mode).</property> <property name="top_padding">2</property> <property name="bottom_padding">2</property> <property name="left_padding">12</property> + <property name="right_padding">12</property> <child> <object class="GtkGrid" id="table9"> <property name="visible">True</property> @@ -1528,8 +1544,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_max_connections_per_second"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_conn_per_sec</property> <property name="numeric">True</property> </object> @@ -1542,8 +1556,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_max_half_open_connections"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_half_open_conn</property> <property name="numeric">True</property> </object> @@ -1607,8 +1619,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum number of connections allowed. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_conn_global</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -1638,8 +1648,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum download speed for all torrents. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_download</property> <property name="climb_rate">1</property> <property name="digits">1</property> @@ -1655,8 +1663,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum upload speed for all torrents. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_upload</property> <property name="climb_rate">1</property> <property name="digits">1</property> @@ -1672,8 +1678,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum upload slots for all torrents. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_upload_slots_global</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -1832,8 +1836,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum upload slots per torrent. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_upload_slots_per_torrent</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -1849,8 +1851,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum number of connections per torrent. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_conn_per_torrent</property> <property name="snap_to_ticks">True</property> <property name="numeric">True</property> @@ -1913,8 +1913,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum number download speed per torrent. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_download_per_torrent</property> <property name="digits">1</property> <property name="numeric">True</property> @@ -1929,8 +1927,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum upload speed per torrent. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_upload_per_torrent</property> <property name="digits">1</property> <property name="numeric">True</property> @@ -2114,8 +2110,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_active"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_active</property> <property name="snap_to_ticks">True</property> <property name="numeric">True</property> @@ -2129,8 +2123,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_seeding"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_seeding</property> <property name="snap_to_ticks">True</property> <property name="numeric">True</property> @@ -2168,8 +2160,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_downloading"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_downloading</property> <property name="snap_to_ticks">True</property> <property name="numeric">True</property> @@ -2204,7 +2194,7 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> - <property name="tooltip_text" translatable="yes">Torrents not transfering any data do not count towards download/seeding active count.</property> + <property name="tooltip_text" translatable="yes">Torrents not transferring any data do not count towards download/seeding active count.</property> <property name="halign">start</property> <property name="draw_indicator">True</property> </object> @@ -2306,8 +2296,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_seed_time_limit</property> </object> <packing> @@ -2320,8 +2308,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_time_ratio_limit</property> <property name="digits">2</property> </object> @@ -2335,8 +2321,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_share_ratio_limit</property> <property name="digits">2</property> </object> @@ -2408,8 +2392,6 @@ used sparingly.</property> <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_share_ratio</property> <property name="digits">2</property> <property name="numeric">True</property> @@ -2555,12 +2537,10 @@ used sparingly.</property> <object class="GtkEntry" id="entry_interface"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="tooltip_text" translatable="yes">The IP address of the interface to listen for incoming bittorrent connections on. Leave this empty if you want to use the default.</property> - <property name="max_length">15</property> + <property name="tooltip_text" translatable="yes">IP address or network interface name to listen for incoming BitTorrent connections. Leave empty to use system default.</property> + <property name="max_length">40</property> <property name="width_chars">15</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> </child> </object> @@ -2569,7 +2549,7 @@ used sparingly.</property> <object class="GtkLabel" id="label110"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes">Incoming Address</property> + <property name="label" translatable="yes">Incoming Interface</property> <attributes> <attribute name="weight" value="bold"/> </attributes> @@ -2611,8 +2591,6 @@ used sparingly.</property> <property name="can_focus">True</property> <property name="max_length">5</property> <property name="max_width_chars">6</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_incoming_port</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -2711,6 +2689,24 @@ used sparingly.</property> </packing> </child> <child> + <object class="GtkAlignment" id="alignment31"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">10</property> + <child> + <object class="GtkSpinner" id="port_spinner"> + <property name="visible">False</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> <object class="GtkAlignment" id="alignment48"> <property name="visible">True</property> <property name="can_focus">False</property> @@ -2725,7 +2721,7 @@ used sparingly.</property> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">3</property> </packing> </child> </object> @@ -2776,14 +2772,12 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes"> -The network interface name or IP address for outgoing BitTorrent connections. (Leave empty for default.) + IP address or network interface name for outgoing BitTorrent connections. Leave empty to use system default. </property> - <property name="max_length">15</property> + <property name="max_length">40</property> <property name="invisible_char">●</property> <property name="width_chars">15</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> </child> </object> @@ -2862,8 +2856,7 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="max_length">5</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> + <property name="width_chars">7</property> <property name="adjustment">adjustment_spin_outgoing_port_min</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -2894,8 +2887,7 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="max_length">5</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> + <property name="width_chars">7</property> <property name="adjustment">adjustment_spin_outgoing_port_max</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -3217,8 +3209,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="width_chars">1</property> <property name="text">0x00</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">False</property> @@ -3326,8 +3316,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="can_focus">True</property> <property name="visibility">False</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="left_attach">1</property> @@ -3351,8 +3339,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="visible">True</property> <property name="can_focus">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <signal name="paste-clipboard" handler="on_entry_proxy_host_paste_clipboard" swapped="no"/> </object> <packing> @@ -3380,8 +3366,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <object class="GtkSpinButton" id="spin_proxy_port"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_proxy_port</property> <property name="numeric">True</property> </object> @@ -3397,8 +3381,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="visible">True</property> <property name="can_focus">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="left_attach">1</property> @@ -3677,8 +3659,6 @@ the proxy instead of using the local DNS service</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">●</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_cache_size</property> <property name="numeric">True</property> <property name="update_policy">if-valid</property> @@ -3694,8 +3674,6 @@ the proxy instead of using the local DNS service</property> <property name="can_focus">True</property> <property name="max_length">5</property> <property name="invisible_char">●</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_cache_expiry</property> </object> <packing> @@ -3878,28 +3856,16 @@ the proxy instead of using the local DNS service</property> </packing> </child> <child> - <object class="GtkLabel" id="label119"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="tooltip_text" translatable="yes">The number of blocks that were served from cache.</property> - <property name="halign">start</property> - <property name="label" translatable="yes">Blocks Read Hit:</property> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> - </packing> - </child> - <child> <object class="GtkLabel" id="label122"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">The cache hit ratio for the read cache.</property> + <property name="halign">start</property> <property name="label" translatable="yes">Read Cache Hit Ratio:</property> </object> <packing> <property name="left_attach">0</property> - <property name="top_attach">3</property> + <property name="top_attach">2</property> </packing> </child> <child> @@ -3913,23 +3879,13 @@ the proxy instead of using the local DNS service</property> </packing> </child> <child> - <object class="GtkLabel" id="label_cache_num_blocks_cache_hits"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">1</property> - </packing> - </child> - <child> <object class="GtkLabel" id="label_cache_read_hit_ratio"> <property name="visible">True</property> <property name="can_focus">False</property> </object> <packing> <property name="left_attach">1</property> - <property name="top_attach">3</property> + <property name="top_attach">2</property> </packing> </child> <child> @@ -3943,7 +3899,7 @@ the proxy instead of using the local DNS service</property> </object> <packing> <property name="left_attach">0</property> - <property name="top_attach">2</property> + <property name="top_attach">1</property> </packing> </child> <child> @@ -3953,7 +3909,7 @@ the proxy instead of using the local DNS service</property> </object> <packing> <property name="left_attach">1</property> - <property name="top_attach">2</property> + <property name="top_attach">1</property> </packing> </child> </object> @@ -4251,8 +4207,6 @@ the proxy instead of using the local DNS service</property> <property name="tooltip_text" translatable="yes">If Deluge cannot find the database file at this location it will fallback to using DNS to resolve the peer's country.</property> <property name="invisible_char">●</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">False</property> @@ -4430,8 +4384,6 @@ the proxy instead of using the local DNS service</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="max_width_chars">6</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_daemon_port</property> </object> <packing> diff --git a/deluge/ui/gtk3/glade/torrent_menu.ui b/deluge/ui/gtk3/glade/torrent_menu.ui index c1b77b4..c9ee289 100644 --- a/deluge/ui/gtk3/glade/torrent_menu.ui +++ b/deluge/ui/gtk3/glade/torrent_menu.ui @@ -31,6 +31,11 @@ <property name="icon_name">media-playback-pause-symbolic</property> <property name="icon_size">1</property> </object> + <object class="GtkImage" id="menu-item-image15"> + <property name="can_focus">False</property> + <property name="icon_name">edit-copy-symbolic</property> + <property name="icon_size">1</property> + </object> <object class="GtkImage" id="menu-item-image19"> <property name="visible">True</property> <property name="can_focus">False</property> @@ -155,6 +160,17 @@ </object> </child> <child> + <object class="GtkImageMenuItem" id="menuitem_copymagnet"> + <property name="label" translatable="yes">_Copy Magnet URI</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_underline">True</property> + <property name="image">menu-item-image15</property> + <property name="use_stock">False</property> + <signal name="activate" handler="on_menuitem_copymagnet_activate" swapped="no"/> + </object> + </child> + <child> <object class="GtkImageMenuItem" id="menuitem_updatetracker"> <property name="label" translatable="yes">_Update Tracker</property> <property name="visible">True</property> diff --git a/deluge/ui/gtk3/gtkui.py b/deluge/ui/gtk3/gtkui.py index d93bd2e..ddb2eb5 100644 --- a/deluge/ui/gtk3/gtkui.py +++ b/deluge/ui/gtk3/gtkui.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -8,8 +7,6 @@ # # pylint: disable=wrong-import-position -from __future__ import division, unicode_literals - import logging import os import signal @@ -18,8 +15,8 @@ import time import gi # isort:skip (Required before Gtk import). -gi.require_version('Gtk', '3.0') # NOQA: E402 -gi.require_version('Gdk', '3.0') # NOQA: E402 +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') # isort:imports-thirdparty from gi.repository.GLib import set_prgname @@ -32,7 +29,7 @@ try: # Install twisted reactor, before any other modules import reactor. reactor = gtk3reactor.install() except ReactorAlreadyInstalledError: - # Running unit tests so trial already installed a rector + # Running unit tests so already installed a rector from twisted.internet import reactor # isort:imports-firstparty @@ -45,7 +42,7 @@ from deluge.common import ( windows_check, ) from deluge.configmanager import ConfigManager, get_config_dir -from deluge.error import DaemonRunningError +from deluge.error import DaemonRunningError, LibtorrentImportError from deluge.i18n import I18N_DOMAIN, set_language, setup_translation from deluge.ui.client import client from deluge.ui.hostlist import LOCALHOST @@ -75,7 +72,7 @@ set_prgname('deluge') log = logging.getLogger(__name__) try: - from setproctitle import setproctitle, getproctitle + from setproctitle import getproctitle, setproctitle except ImportError: def setproctitle(title): @@ -121,6 +118,7 @@ DEFAULT_PREFS = { 'show_toolbar': True, 'show_statusbar': True, 'show_tabsbar': True, + 'tabsbar_tab_pos': 'top', 'tabsbar_position': 235, 'sidebar_show_zero': False, 'sidebar_show_trackers': True, @@ -129,6 +127,7 @@ DEFAULT_PREFS = { 'show_rate_in_title': False, 'createtorrent.trackers': [], 'show_piecesbar': False, + 'detect_urls': True, 'pieces_color_missing': [65535, 0, 0], 'pieces_color_waiting': [4874, 56494, 0], 'pieces_color_downloading': [65535, 55255, 0], @@ -138,7 +137,7 @@ DEFAULT_PREFS = { } -class GtkUI(object): +class GtkUI: def __init__(self, args): # Setup gtkbuilder/glade translation setup_translation() @@ -220,7 +219,7 @@ class GtkUI(object): menubar_osx(self, self.osxapp) self.osxapp.ready() - # Initalize the plugins + # Initialize the plugins self.plugins = PluginManager() # Show the connection manager @@ -313,8 +312,8 @@ class GtkUI(object): 'A Deluge daemon (deluged) is already running.\n' 'To use Standalone mode, stop local daemon and restart Deluge.' ) - except ImportError as ex: - if 'No module named libtorrent' in str(ex): + except LibtorrentImportError as ex: + if 'libtorrent library not found' in str(ex): err_msg = _( 'Only Thin Client mode is available because libtorrent is not installed.\n' 'To use Standalone mode, please install libtorrent package.' @@ -322,9 +321,17 @@ class GtkUI(object): else: log.exception(ex) err_msg = _( - 'Only Thin Client mode is available due to unknown Import Error.\n' + 'Only Thin Client mode is available due to libtorrent import error: %s\n' 'To use Standalone mode, please see logs for error details.' + % (str(ex)) ) + + except ImportError as ex: + log.exception(ex) + err_msg = _( + 'Only Thin Client mode is available due to unknown Import Error.\n' + 'To use Standalone mode, please see logs for error details.' + ) except Exception as ex: log.exception(ex) err_msg = _( diff --git a/deluge/ui/gtk3/ipcinterface.py b/deluge/ui/gtk3/ipcinterface.py index dc51a87..0ef28d8 100644 --- a/deluge/ui/gtk3/ipcinterface.py +++ b/deluge/ui/gtk3/ipcinterface.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,14 +6,14 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os import sys from base64 import b64encode from glob import glob from tempfile import mkstemp +from urllib.parse import urlparse +from urllib.request import url2pathname import rencode import twisted.internet.error @@ -26,14 +25,6 @@ from deluge.common import decode_bytes, is_magnet, is_url, windows_check from deluge.configmanager import ConfigManager, get_config_dir from deluge.ui.client import client -try: - from urllib.parse import urlparse - from urllib.request import url2pathname -except ImportError: - # PY2 fallback - from urlparse import urlparse # pylint: disable=ungrouped-imports - from urllib import url2pathname # pylint: disable=ungrouped-imports - log = logging.getLogger(__name__) @@ -84,8 +75,8 @@ class IPCInterface(component.Component): if windows_check(): # If we're on windows we need to check the global mutex to see if deluge is # already running. - import win32event import win32api + import win32event import winerror self.mutex = win32event.CreateMutex(None, False, 'deluge') diff --git a/deluge/ui/gtk3/listview.py b/deluge/ui/gtk3/listview.py index 666bb67..e9f6b10 100644 --- a/deluge/ui/gtk3/listview.py +++ b/deluge/ui/gtk3/listview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,20 +6,18 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from gi.repository import GObject, Gtk -from deluge.common import PY2, decode_bytes +from deluge.common import decode_bytes from .common import cmp, load_pickled_state_file, save_pickled_state_file log = logging.getLogger(__name__) -class ListViewColumnState(object): +class ListViewColumnState: """Class used for saving/loading column state.""" def __init__(self, name, position, width, visible, sort, sort_order): @@ -32,13 +29,13 @@ class ListViewColumnState(object): self.sort_order = sort_order -class ListView(object): +class ListView: """ListView is used to make custom GtkTreeViews. It supports the adding and removing of columns, creating a menu for a column toggle list and support for 'status_field's which are used while updating the columns data. """ - class ListViewColumn(object): + class ListViewColumn: """Holds information regarding a column in the ListView""" def __init__(self, name, column_indices): @@ -66,22 +63,20 @@ class ListView(object): self.pixbuf_index = 0 self.data_func = None - class TreeviewColumn(Gtk.TreeViewColumn, object): + class TreeviewColumn(Gtk.TreeViewColumn): """ - TreeViewColumn does not signal right-click events, and we need them - This subclass is equivalent to TreeViewColumn, but it signals these events + TreeViewColumn does not signal right-click events, and we need them + This subclass is equivalent to TreeViewColumn, but it signals these events - Most of the code of this class comes from Quod Libet (http://www.sacredchao.net/quodlibet) + Most of the code of this class comes from Quod Libet (http://www.sacredchao.net/quodlibet) """ __gsignals__ = { - 'button-press-event' - if not PY2 - else b'button-press-event': (GObject.SIGNAL_RUN_LAST, None, (object,)) + 'button-press-event': (GObject.SIGNAL_RUN_LAST, None, (object,)) } def __init__(self, title=None, cell_renderer=None, **args): - """ Constructor, see Gtk.TreeViewColumn """ + """Constructor, see Gtk.TreeViewColumn""" Gtk.TreeViewColumn.__init__(self, title, cell_renderer, **args) label = Gtk.Label(label=title) self.set_widget(label) @@ -112,7 +107,7 @@ class ListView(object): Gtk.TreeViewColumn.set_visible(self, visible) if self.data_func: if not visible: - # Set data function to None to prevent unecessary calls when column is hidden + # Set data function to None to prevent unnecessary calls when column is hidden self.set_cell_data_func(self.cell_renderer, None, func_data=None) else: self.set_cell_data_func( @@ -144,7 +139,6 @@ class ListView(object): self.liststore = None self.model_filter = None - self.treeview.set_rules_hint(True) self.treeview.set_reorderable(False) self.treeview.set_rubber_banding(True) # Enable mouse multi-row selection. self.treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) @@ -281,7 +275,7 @@ class ListView(object): def save_state(self, filename): """Saves the listview state (column positions and visibility) to - filename.""" + filename.""" # A list of ListViewColumnStates state = [] @@ -627,8 +621,7 @@ class ListView(object): unique=False, default_sort=False, ): - """Add a text column to the listview. Only the header name is required. - """ + """Add a text column to the listview. Only the header name is required.""" render = Gtk.CellRendererText() self.add_column( header, diff --git a/deluge/ui/gtk3/mainwindow.py b/deluge/ui/gtk3/mainwindow.py index 5b4240c..d11ff31 100644 --- a/deluge/ui/gtk3/mainwindow.py +++ b/deluge/ui/gtk3/mainwindow.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path from hashlib import sha1 as sha @@ -20,11 +17,11 @@ from twisted.internet import reactor from twisted.internet.error import ReactorNotRunning import deluge.component as component -from deluge.common import decode_bytes, fspeed, resource_filename +from deluge.common import decode_bytes, fspeed, is_magnet, is_url, resource_filename from deluge.configmanager import ConfigManager from deluge.ui.client import client -from .common import get_deluge_icon, windowing +from .common import get_clipboard_text, get_deluge_icon, windowing from .dialogs import PasswordDialog from .ipcinterface import process_args @@ -45,7 +42,7 @@ if windowing('X11'): log = logging.getLogger(__name__) -class _GtkBuilderSignalsHolder(object): +class _GtkBuilderSignalsHolder: def connect_signals(self, mapping_or_class): if isinstance(mapping_or_class, dict): @@ -108,6 +105,7 @@ class MainWindow(component.Component): self.window = self.main_builder.get_object('main_window') self.window.set_icon(get_deluge_icon()) self.tabsbar_pane = self.main_builder.get_object('tabsbar_pane') + self.tabsbar_torrent_info = self.main_builder.get_object('torrent_info') self.sidebar_pane = self.main_builder.get_object('sidebar_pane') # Keep a list of components to pause and resume when changing window state. @@ -131,6 +129,7 @@ class MainWindow(component.Component): self.window.connect('configure-event', self.on_window_configure_event) self.window.connect('delete-event', self.on_window_delete_event) self.window.connect('drag-data-received', self.on_drag_data_received_event) + self.window.connect('notify::is-active', self.on_focus) self.tabsbar_pane.connect( 'notify::position', self.on_tabsbar_pane_position_event ) @@ -147,6 +146,9 @@ class MainWindow(component.Component): 'NewVersionAvailableEvent', self.on_newversionavailable_event ) + self.previous_clipboard_text = '' + self.first_run = True + def connect_signals(self, mapping_or_class): self.gtk_builder_signals_holder.connect_signals(mapping_or_class) @@ -277,12 +279,14 @@ class MainWindow(component.Component): def save_position(self): self.config['window_maximized'] = self.window.props.is_maximized if not self.config['window_maximized'] and self.visible(): - self.config['window_x_pos'], self.config[ - 'window_y_pos' - ] = self.window.get_position() - self.config['window_width'], self.config[ - 'window_height' - ] = self.window.get_size() + ( + self.config['window_x_pos'], + self.config['window_y_pos'], + ) = self.window.get_position() + ( + self.config['window_width'], + self.config['window_height'], + ) = self.window.get_size() def on_window_configure_event(self, widget, event): self.save_position() @@ -327,6 +331,21 @@ class MainWindow(component.Component): def on_expose_event(self, widget, event): component.get('SystemTray').blink(False) + def on_focus(self, window, param): + if window.props.is_active and not self.first_run and self.config['detect_urls']: + text = get_clipboard_text() + if text == self.previous_clipboard_text: + return + self.previous_clipboard_text = text + if text and ( + (is_url(text) and text.endswith('.torrent')) + or is_magnet(text) + and not component.get('MenuBar').magnet_copied() + ): + component.get('AddTorrentDialog').show() + component.get('AddTorrentDialog').on_button_url_clicked(window) + self.first_run = False + def stop(self): self.window.set_title('Deluge') diff --git a/deluge/ui/gtk3/menubar.py b/deluge/ui/gtk3/menubar.py index e09f394..a812a8c 100644 --- a/deluge/ui/gtk3/menubar.py +++ b/deluge/ui/gtk3/menubar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> @@ -9,8 +8,6 @@ # -from __future__ import unicode_literals - import logging import os.path @@ -21,7 +18,7 @@ import deluge.component as component from deluge.configmanager import ConfigManager from deluge.ui.client import client -from .dialogs import ErrorDialog, OtherDialog +from .dialogs import CopyMagnetDialog, ErrorDialog, OtherDialog from .path_chooser import PathChooser log = logging.getLogger(__name__) @@ -34,6 +31,7 @@ class MenuBar(component.Component): self.mainwindow = component.get('MainWindow') self.main_builder = self.mainwindow.get_builder() self.config = ConfigManager('gtk3ui.conf') + self._magnet_copied = False self.builder = Gtk.Builder() # Get the torrent menu from the gtk builder file @@ -142,6 +140,19 @@ class MenuBar(component.Component): self.change_sensitivity = ['menuitem_addtorrent'] + def magnet_copied(self): + """ + lets the caller know whether a magnet was copied internally + + the `mainwindow` checks every time the data in the clipboard, + so it will automatically open the AddTorrentURL dialog in case it + contains a valid link (URL to a torrent or a magnet URI). + + """ + val = self._magnet_copied + self._magnet_copied = False + return val + def start(self): for widget in self.change_sensitivity: self.main_builder.get_object(widget).set_sensitive(True) @@ -282,6 +293,21 @@ class MenuBar(component.Component): component.get('TorrentView').get_selected_torrents() ) + def on_menuitem_copymagnet_activate(self, data=None): + log.debug('on_menuitem_copymagnet_activate') + torrent_ids = component.get('TorrentView').get_selected_torrents() + if torrent_ids: + + def _on_magnet_uri(magnet_uri): + def update_copied(response_id): + if dialog.copied: + self._magnet_copied = True + + dialog = CopyMagnetDialog(magnet_uri) + dialog.run().addCallback(update_copied) + + client.core.get_magnet_uri(torrent_ids[0]).addCallback(_on_magnet_uri) + def on_menuitem_updatetracker_activate(self, data=None): log.debug('on_menuitem_updatetracker_activate') client.core.force_reannounce( @@ -541,7 +567,7 @@ class MenuBar(component.Component): account_to_log = {} for key, value in account.copy().items(): if key == 'password': - value = '*' * len(value) + value = '*' * 10 account_to_log[key] = value known_accounts_to_log.append(account_to_log) log.debug('_on_known_accounts: %s', known_accounts_to_log) diff --git a/deluge/ui/gtk3/menubar_osx.py b/deluge/ui/gtk3/menubar_osx.py index 1df6fab..53150fb 100644 --- a/deluge/ui/gtk3/menubar_osx.py +++ b/deluge/ui/gtk3/menubar_osx.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from gi.repository.Gdk import ModifierType from gi.repository.Gtk import SeparatorMenuItem, accel_groups_from_object from gi.repository.Gtk.AccelFlags import VISIBLE diff --git a/deluge/ui/gtk3/new_release_dialog.py b/deluge/ui/gtk3/new_release_dialog.py index 6aa3282..a635bd2 100644 --- a/deluge/ui/gtk3/new_release_dialog.py +++ b/deluge/ui/gtk3/new_release_dialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from gi.repository.Gtk import IconSize import deluge.common @@ -17,7 +14,7 @@ from deluge.configmanager import ConfigManager from deluge.ui.client import client -class NewReleaseDialog(object): +class NewReleaseDialog: def __init__(self): pass diff --git a/deluge/ui/gtk3/options_tab.py b/deluge/ui/gtk3/options_tab.py index 6a25fd1..b0411a8 100644 --- a/deluge/ui/gtk3/options_tab.py +++ b/deluge/ui/gtk3/options_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # 2017 Calum Lind <calumlind+deluge@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from gi.repository.Gdk import keyval_name import deluge.component as component @@ -21,7 +18,7 @@ from .torrentdetails import Tab class OptionsTab(Tab): def __init__(self): - super(OptionsTab, self).__init__('Options', 'options_tab', 'options_tab_label') + super().__init__('Options', 'options_tab', 'options_tab_label') self.prev_torrent_ids = None self.prev_status = None @@ -191,8 +188,9 @@ class OptionsTab(Tab): ): options[status_key] = widget_value - if options.get('move_completed', False): - options['move_completed_path'] = self.move_completed_path_chooser.get_text() + move_completed_path = self.move_completed_path_chooser.get_text() + if move_completed_path != self.prev_status['move_completed_path']: + options['move_completed_path'] = move_completed_path client.core.set_torrent_options(self.prev_torrent_ids, options) self.button_apply.set_sensitive(False) diff --git a/deluge/ui/gtk3/path_chooser.py b/deluge/ui/gtk3/path_chooser.py index b722841..8058196 100644 --- a/deluge/ui/gtk3/path_chooser.py +++ b/deluge/ui/gtk3/path_chooser.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2013 Bro <bro.development@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -125,7 +122,7 @@ class PathChoosersHandler(component.Component): class PathChooser(PathChooserComboBox): def __init__(self, paths_config_key=None, parent=None): self.paths_config_key = paths_config_key - super(PathChooser, self).__init__(parent=parent) + super().__init__(parent=parent) self.chooser_handler = PathChoosersHandler() self.chooser_handler.register_chooser(self) self.set_auto_completer_func(self.on_completion) diff --git a/deluge/ui/gtk3/path_combo_chooser.py b/deluge/ui/gtk3/path_combo_chooser.py index c26289d..74d9055 100755 --- a/deluge/ui/gtk3/path_combo_chooser.py +++ b/deluge/ui/gtk3/path_combo_chooser.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Copyright (C) 2013 Bro <bro.development@gmail.com> # @@ -8,15 +7,13 @@ # See LICENSE for more details. # -from __future__ import division, print_function, unicode_literals - import os import warnings from gi.repository import Gdk, GObject, Gtk from gi.repository.GObject import SignalFlags -from deluge.common import PY2, resource_filename +from deluge.common import resource_filename from deluge.path_chooser_common import get_completion_paths # Filter the pygobject signal warning: @@ -64,7 +61,7 @@ def path_without_trailing_path_sep(path): return path -class ValueList(object): +class ValueList: paths_without_trailing_path_sep = False @@ -176,7 +173,7 @@ class ValueList(object): """ for i, row in enumerate(self.tree_store): if row[0] == value: - self.treeview.set_cursor((i)) + self.treeview.set_cursor(i) return # The value was not found if select_first: @@ -374,7 +371,7 @@ class StoredValuesList(ValueList): """ # This is left click if event.button != 3: - super(StoredValuesList, self).on_treeview_mouse_button_press_event( + super().on_treeview_mouse_button_press_event( treeview, event, double_click=True ) return False @@ -390,9 +387,9 @@ class StoredValuesList(ValueList): treeview.set_cursor(path, col, 0) self.path_list_popup = Gtk.Menu() - menuitem_edit = Gtk.MenuItem.new_with_label('Edit path') + menuitem_edit = Gtk.MenuItem.new_with_label(_('Edit path')) self.path_list_popup.append(menuitem_edit) - menuitem_remove = Gtk.MenuItem.new_with_label('Remove path') + menuitem_remove = Gtk.MenuItem.new_with_label(_('Remove path')) self.path_list_popup.append(menuitem_remove) def on_edit_clicked(widget, path): @@ -412,9 +409,7 @@ class StoredValuesList(ValueList): PathChooserPopup.popup(self) def on_stored_values_treeview_key_press_event(self, widget, event): - super(StoredValuesList, self).on_value_list_treeview_key_press_event( - widget, event - ) + super().on_value_list_treeview_key_press_event(widget, event) # Prevent the default event handler to move the cursor in the list if key_is_up_or_down(event.keyval): return True @@ -479,9 +474,9 @@ class CompletionList(ValueList): ] = self.on_completion_treeview_motion_notify_event # Add super class signal handler - self.signal_handlers['on_completion_treeview_mouse_button_press_event'] = super( - CompletionList, self - ).on_treeview_mouse_button_press_event + self.signal_handlers[ + 'on_completion_treeview_mouse_button_press_event' + ] = super().on_treeview_mouse_button_press_event def reduce_values(self, prefix): """ @@ -499,9 +494,7 @@ class CompletionList(ValueList): self.add_values(matching_values, clear=True) def on_completion_treeview_key_press_event(self, widget, event): - ret = super(CompletionList, self).on_value_list_treeview_key_press_event( - widget, event - ) + ret = super().on_value_list_treeview_key_press_event(widget, event) if ret: return ret keyval = event.keyval @@ -529,7 +522,7 @@ class CompletionList(ValueList): self.handle_list_scroll(path=path[0], _next=None) -class PathChooserPopup(object): +class PathChooserPopup: """This creates the popop window for the ComboEntry.""" def __init__(self, min_visible_rows, max_visible_rows, popup_alignment_widget): @@ -538,9 +531,8 @@ class PathChooserPopup(object): self.set_max_popup_rows(max_visible_rows) self.popup_window.realize() self.alignment_widget = popup_alignment_widget - self.popup_buttonbox = ( - None - ) # If set, the height of this widget is the minimum height + # If set, the height of this widget is the minimum height + self.popup_buttonbox = None def popup(self): """Make the popup visible.""" @@ -984,7 +976,7 @@ class PathCompletionPopup(CompletionList, PathChooserPopup): return True -class PathAutoCompleter(object): +class PathAutoCompleter: def __init__(self, builder, path_entry, max_visible_rows): self.completion_popup = PathCompletionPopup( builder, path_entry, max_visible_rows @@ -1107,9 +1099,7 @@ class PathAutoCompleter(object): class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject): __gsignals__ = { - signal - if not PY2 - else signal.encode(): (SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object,)) + signal: (SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object,)) for signal in [ 'text-changed', 'accelerator-set', @@ -1415,7 +1405,7 @@ class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject): self.set_text(self.get_text()) def _on_entry_combobox_hbox_realize(self, widget): - """ Must do this when the widget is realized """ + """Must do this when the widget is realized""" self.set_filechooser_button_visible(self.filechooser_visible) self.set_path_entry_visible(self.path_entry_visible) @@ -1467,7 +1457,7 @@ class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject): ) return True elif is_ascii_value(keyval, 's'): - super(PathChooserComboBox, self).add_current_value_to_saved_list() + super().add_current_value_to_saved_list() return True elif is_ascii_value(keyval, 'd'): # Set the default value in the text entry @@ -1697,7 +1687,7 @@ if __name__ == '__main__': box1 = Gtk.Box.new(Gtk.Orientation.VERTICAL, spacing=0) def get_resource2(filename): - return '%s/glade/%s' % (os.path.abspath(os.path.dirname(sys.argv[0])), filename) + return f'{os.path.abspath(os.path.dirname(sys.argv[0]))}/glade/{filename}' # Override get_resource which fetches from deluge install # get_resource = get_resource2 diff --git a/deluge/ui/gtk3/peers_tab.py b/deluge/ui/gtk3/peers_tab.py index 33395b9..b458f7a 100644 --- a/deluge/ui/gtk3/peers_tab.py +++ b/deluge/ui/gtk3/peers_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path @@ -32,6 +29,7 @@ from .common import ( icon_downloading, icon_seeding, load_pickled_state_file, + parse_ip_port, save_pickled_state_file, ) from .torrentdetails import Tab @@ -41,18 +39,12 @@ from .torrentview_data_funcs import ( cell_data_speed_up, ) -try: - from future_builtins import zip -except ImportError: - # Ignore on Py3. - pass - log = logging.getLogger(__name__) class PeersTab(Tab): def __init__(self): - super(PeersTab, self).__init__('Peers', 'peers_tab', 'peers_tab_label') + super().__init__('Peers', 'peers_tab', 'peers_tab_label') self.peer_menu = self.main_builder.get_object('menu_peer_tab') component.get('MainWindow').connect_signals(self) @@ -304,15 +296,15 @@ class PeersTab(Tab): peer_ip = peer['ip'] else: # This is an IPv6 address - import socket import binascii + import socket # Split out the :port ip = ':'.join(peer['ip'].split(':')[:-1]) ip_int = int( binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip)), 16 ) - peer_ip = '[%s]:%s' % (ip, peer['ip'].split(':')[-1]) + peer_ip = '[{}]:{}'.format(ip, peer['ip'].split(':')[-1]) if peer['seed']: icon = self.seed_pixbuf @@ -376,19 +368,15 @@ class PeersTab(Tab): peer_dialog = builder.get_object('connect_peer_dialog') txt_ip = builder.get_object('txt_ip') response = peer_dialog.run() + if response: value = txt_ip.get_text() - if value and ':' in value: - if ']' in value: - # ipv6 - ip = value.split(']')[0][1:] - port = value.split(']')[1][1:] - else: - # ipv4 - ip = value.split(':')[0] - port = value.split(':')[1] - if deluge.common.is_ip(ip): - log.debug('adding peer %s to %s', value, self.torrent_id) - client.core.connect_peer(self.torrent_id, ip, port) + ip, port = parse_ip_port(value) + if ip and port: + log.info('Adding peer IP: %s port: %s to %s', ip, port, self.torrent_id) + client.core.connect_peer(self.torrent_id, ip, port) + else: + log.error('Error parsing peer "%s"', value) + peer_dialog.destroy() return True diff --git a/deluge/ui/gtk3/piecesbar.py b/deluge/ui/gtk3/piecesbar.py index ba03e55..8665328 100644 --- a/deluge/ui/gtk3/piecesbar.py +++ b/deluge/ui/gtk3/piecesbar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> # @@ -7,14 +6,13 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - from math import pi import gi # isort:skip (Version check required before import). -gi.require_version('PangoCairo', '1.0') # NOQA: E402 -gi.require_version('cairo', '1.0') # NOQA: E402 +gi.require_version('PangoCairo', '1.0') +gi.require_foreign('cairo') +gi.require_version('cairo', '1.0') # isort:imports-thirdparty import cairo # Backward compat cairo <= 1.15 @@ -23,7 +21,6 @@ from gi.repository.Gtk import DrawingArea, ProgressBar, StateFlags from gi.repository.Pango import SCALE, Weight # isort:imports-firstparty -from deluge.common import PY2 from deluge.configmanager import ConfigManager COLOR_STATES = ['missing', 'waiting', 'downloading', 'completed'] @@ -31,14 +28,15 @@ COLOR_STATES = ['missing', 'waiting', 'downloading', 'completed'] class PiecesBar(DrawingArea): # Draw in response to an draw - __gsignals__ = {'draw': 'override'} if not PY2 else {b'draw': b'override'} + __gsignals__ = {'draw': 'override'} def __init__(self): - super(PiecesBar, self).__init__() + super().__init__() # Get progress bar styles, in order to keep font consistency pb = ProgressBar() pb_style = pb.get_style_context() - self.text_font = pb_style.get_property('font', StateFlags.NORMAL) + # Get a copy of Pango.FontDescription since original needs freed. + self.text_font = pb_style.get_property('font', StateFlags.NORMAL).copy() self.text_font.set_weight(Weight.BOLD) # Done with the ProgressBar styles, don't keep refs of it del pb, pb_style diff --git a/deluge/ui/gtk3/pluginmanager.py b/deluge/ui/gtk3/pluginmanager.py index d60f8d3..63353c0 100644 --- a/deluge/ui/gtk3/pluginmanager.py +++ b/deluge/ui/gtk3/pluginmanager.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component diff --git a/deluge/ui/gtk3/preferences.py b/deluge/ui/gtk3/preferences.py index b196128..a008a95 100644 --- a/deluge/ui/gtk3/preferences.py +++ b/deluge/ui/gtk3/preferences.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> @@ -8,11 +7,10 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os from hashlib import sha1 as sha +from urllib.parse import urlparse from gi import require_version from gi.repository import Gtk @@ -21,6 +19,7 @@ from gi.repository.Gdk import Color import deluge.common import deluge.component as component from deluge.configmanager import ConfigManager, get_config_dir +from deluge.decorators import maybe_coroutine from deluge.error import AuthManagerError, NotAuthorizedError from deluge.i18n import get_languages from deluge.ui.client import client @@ -31,12 +30,6 @@ from .dialogs import AccountDialog, ErrorDialog, InformationDialog, YesNoDialog from .path_chooser import PathChooser try: - from urllib.parse import urlparse -except ImportError: - # PY2 fallback - from urlparse import urlparse # pylint: disable=ungrouped-imports - -try: require_version('AppIndicator3', '0.1') from gi.repository import AppIndicator3 # noqa: F401 except (ImportError, ValueError): @@ -77,7 +70,7 @@ class Preferences(component.Component): self.load_pref_dialog_state() self.builder.get_object('image_magnet').set_from_file( - deluge.common.get_pixmap('magnet.png') + deluge.common.get_pixmap('magnet16.png') ) # Hide the unused associate magnet button on OSX see: #2420 @@ -255,11 +248,10 @@ class Preferences(component.Component): vbox.pack_start(label, False, True, 0) sep = Gtk.HSeparator() vbox.pack_start(sep, False, True, 0) - align = Gtk.Alignment() - align.set_padding(5, 0, 0, 0) - align.set(0, 0, 1, 1) - align.add(widget) - vbox.pack_start(align, True, True, 0) + widget.set_margin_top(7) + widget.set_vexpand(True) + widget.set_hexpand(True) + vbox.pack_start(widget, True, True, 0) scrolled = Gtk.ScrolledWindow() viewport = Gtk.Viewport() viewport.set_shadow_type(Gtk.ShadowType.NONE) @@ -571,6 +563,9 @@ class Preferences(component.Component): self.builder.get_object('piecesbar_toggle').set_active( self.gtkui_config['show_piecesbar'] ) + self.builder.get_object('urldetect_toggle').set_active( + self.gtkui_config['detect_urls'] + ) self.__set_color('completed', from_config=True) self.__set_color('downloading', from_config=True) self.__set_color('waiting', from_config=True) @@ -677,11 +672,15 @@ class Preferences(component.Component): 'chk_random_outgoing_ports' ).get_active() incoming_address = self.builder.get_object('entry_interface').get_text().strip() - if deluge.common.is_ip(incoming_address) or not incoming_address: + if deluge.common.is_interface(incoming_address) or not incoming_address: new_core_config['listen_interface'] = incoming_address - new_core_config['outgoing_interface'] = ( + outgoing_address = ( self.builder.get_object('entry_outgoing_interface').get_text().strip() ) + if deluge.common.is_interface(outgoing_address) or not outgoing_address: + new_core_config['outgoing_interface'] = ( + self.builder.get_object('entry_outgoing_interface').get_text().strip() + ) new_core_config['peer_tos'] = self.builder.get_object( 'entry_peer_tos' ).get_text() @@ -937,7 +936,7 @@ class Preferences(component.Component): not self.gtkui_config['standalone'] ) - mode = 'Thinclient' if was_standalone else 'Standalone' + mode = _('Thinclient') if was_standalone else _('Standalone') dialog = YesNoDialog( _('Switching Deluge Client Mode...'), _('Do you want to restart to use %s mode?' % mode), @@ -946,6 +945,7 @@ class Preferences(component.Component): def hide(self): self.window_open = False + self.builder.get_object('port_spinner').stop() self.builder.get_object('port_img').hide() self.pref_dialog.hide() @@ -958,7 +958,6 @@ class Preferences(component.Component): 'label_cache_num_blocks_written', 'label_cache_read_hit_ratio', 'label_cache_write_hit_ratio', - 'label_cache_num_blocks_cache_hits', 'label_cache_disk_blocks_in_use', 'label_cache_read_cache_blocks', ) @@ -1091,6 +1090,8 @@ class Preferences(component.Component): log.debug('on_test_port_clicked') def on_get_test(status): + self.builder.get_object('port_spinner').stop() + self.builder.get_object('port_spinner').hide() if status: self.builder.get_object('port_img').set_from_icon_name( 'emblem-ok-symbolic', Gtk.IconSize.MENU @@ -1103,12 +1104,9 @@ class Preferences(component.Component): self.builder.get_object('port_img').show() client.core.test_listen_port().addCallback(on_get_test) - # XXX: Consider using gtk.Spinner() instead of the loading gif - # It requires gtk.ver > 2.12 - self.builder.get_object('port_img').set_from_file( - deluge.common.get_pixmap('loading.gif') - ) - self.builder.get_object('port_img').show() + self.builder.get_object('port_spinner').start() + self.builder.get_object('port_spinner').show() + self.builder.get_object('port_img').hide() client.force_call() def on_plugin_toggled(self, renderer, path): @@ -1180,8 +1178,8 @@ class Preferences(component.Component): chooser.destroy() return - from base64 import b64encode import shutil + from base64 import b64encode filename = os.path.split(filepath)[1] shutil.copyfile(filepath, os.path.join(get_config_dir(), 'plugins', filename)) @@ -1332,58 +1330,46 @@ class Preferences(component.Component): (model, itr) = treeselection.get_selected() if not itr: return - username = model[itr][0] - if username: + level = model[itr][1] + if level: self.builder.get_object('accounts_edit').set_sensitive(True) self.builder.get_object('accounts_delete').set_sensitive(True) else: self.builder.get_object('accounts_edit').set_sensitive(False) self.builder.get_object('accounts_delete').set_sensitive(False) - def on_accounts_add_clicked(self, widget): + @maybe_coroutine + async def on_accounts_add_clicked(self, widget): dialog = AccountDialog( levels_mapping=client.auth_levels_mapping, parent=self.pref_dialog ) + response = await dialog.run() + if response != Gtk.ResponseType.OK: + return - def dialog_finished(response_id): - username = dialog.get_username() - password = dialog.get_password() - authlevel = dialog.get_authlevel() - - def add_ok(rv): - accounts_iter = self.accounts_liststore.append() - self.accounts_liststore.set_value( - accounts_iter, ACCOUNTS_USERNAME, username - ) - self.accounts_liststore.set_value( - accounts_iter, ACCOUNTS_LEVEL, authlevel - ) - self.accounts_liststore.set_value( - accounts_iter, ACCOUNTS_PASSWORD, password - ) - - def add_fail(failure): - if failure.type == AuthManagerError: - ErrorDialog( - _('Error Adding Account'), - _('Authentication failed'), - parent=self.pref_dialog, - details=failure.getErrorMessage(), - ).run() - else: - ErrorDialog( - _('Error Adding Account'), - _('An error occurred while adding account'), - parent=self.pref_dialog, - details=failure.getErrorMessage(), - ).run() - - if response_id == Gtk.ResponseType.OK: - client.core.create_account(username, password, authlevel).addCallback( - add_ok - ).addErrback(add_fail) - - dialog.run().addCallback(dialog_finished) + account = dialog.account + try: + await client.core.create_account(*account) + except AuthManagerError as ex: + return ErrorDialog( + _('Error Adding Account'), + _('Authentication failed'), + parent=self.pref_dialog, + details=ex, + ).run() + except Exception as ex: + return ErrorDialog( + _('Error Adding Account'), + _(f'An error occurred while adding account: {account}'), + parent=self.pref_dialog, + details=ex, + ).run() + + self.accounts_liststore.set( + self.accounts_liststore.append(), + [ACCOUNTS_USERNAME, ACCOUNTS_LEVEL, ACCOUNTS_PASSWORD], + [account.username, account.authlevel, account.password], + ) def on_accounts_edit_clicked(self, widget): (model, itr) = self.accounts_listview.get_selection().get_selected() @@ -1463,6 +1449,9 @@ class Preferences(component.Component): colors_widget = self.builder.get_object('piecebar_colors_expander') colors_widget.set_visible(widget.get_active()) + def on_urldetect_toggle_toggled(self, widget): + self.gtkui_config['detect_urls'] = widget.get_active() + def on_checkbutton_language_toggled(self, widget): self.language_combo.set_visible(not self.language_checkbox.get_active()) diff --git a/deluge/ui/gtk3/queuedtorrents.py b/deluge/ui/gtk3/queuedtorrents.py index 0f08c24..6fdecec 100644 --- a/deluge/ui/gtk3/queuedtorrents.py +++ b/deluge/ui/gtk3/queuedtorrents.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path diff --git a/deluge/ui/gtk3/removetorrentdialog.py b/deluge/ui/gtk3/removetorrentdialog.py index 48806a5..06fca77 100644 --- a/deluge/ui/gtk3/removetorrentdialog.py +++ b/deluge/ui/gtk3/removetorrentdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os @@ -21,7 +18,7 @@ from deluge.ui.client import client log = logging.getLogger(__name__) -class RemoveTorrentDialog(object): +class RemoveTorrentDialog: """ This class is used to create and show a Remove Torrent Dialog. diff --git a/deluge/ui/gtk3/sidebar.py b/deluge/ui/gtk3/sidebar.py index 1d75191..5a2b154 100644 --- a/deluge/ui/gtk3/sidebar.py +++ b/deluge/ui/gtk3/sidebar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from gi.repository.Gtk import Label, PolicyType, ScrolledWindow diff --git a/deluge/ui/gtk3/status_tab.py b/deluge/ui/gtk3/status_tab.py index fab6719..6a9010b 100644 --- a/deluge/ui/gtk3/status_tab.py +++ b/deluge/ui/gtk3/status_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging import deluge.component as component @@ -32,7 +29,7 @@ log = logging.getLogger(__name__) class StatusTab(Tab): def __init__(self): - super(StatusTab, self).__init__('Status', 'status_tab', 'status_tab_label') + super().__init__('Status', 'status_tab', 'status_tab_label') self.config = ConfigManager('gtk3ui.conf') @@ -110,7 +107,7 @@ class StatusTab(Tab): if decode_bytes(widget[0].get_text()) != txt: widget[0].set_text(txt) - # Update progress bar seperately as it's a special case (not a label). + # Update progress bar separately as it's a special case (not a label). fraction = status['progress'] / 100 if self.config['show_piecesbar']: diff --git a/deluge/ui/gtk3/statusbar.py b/deluge/ui/gtk3/statusbar.py index 265e7c8..0a2e800 100644 --- a/deluge/ui/gtk3/statusbar.py +++ b/deluge/ui/gtk3/statusbar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging from gi.repository import Gtk @@ -25,7 +22,7 @@ from .dialogs import OtherDialog log = logging.getLogger(__name__) -class StatusBarItem(object): +class StatusBarItem: def __init__( self, image=None, @@ -142,12 +139,11 @@ class StatusBar(component.Component): self.current_warnings = [] # Add hbox to the statusbar after removing the initial label widget self.hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, spacing=10) - align = Gtk.Alignment() - align.set_padding(2, 0, 3, 0) - align.add(self.hbox) + self.hbox.set_margin_top(2) + self.hbox.set_margin_bottom(3) frame = self.statusbar.get_children()[0] frame.remove(frame.get_children()[0]) - frame.add(align) + frame.add(self.hbox) self.statusbar.show_all() # Create the not connected item self.not_connected_item = StatusBarItem( @@ -321,18 +317,22 @@ class StatusBar(component.Component): def send_status_request(self): # Sends an async request for data from the core keys = [ - 'num_peers', + 'peer.num_peers_connected', 'upload_rate', 'download_rate', 'payload_upload_rate', 'payload_download_rate', + 'net.sent_bytes', + 'net.recv_bytes', + 'net.sent_payload_bytes', + 'net.recv_payload_bytes', ] if self.dht_status: - keys.append('dht_nodes') + keys.append('dht.dht_nodes') if not self.health: - keys.append('has_incoming_connections') + keys.append('net.has_incoming_connections') client.core.get_session_status(keys).addCallback(self._on_get_session_status) client.core.get_free_space().addCallback(self._on_get_free_space) @@ -371,18 +371,18 @@ class StatusBar(component.Component): self.upload_protocol_rate = ( status['upload_rate'] - status['payload_upload_rate'] ) // 1024 - self.num_connections = status['num_peers'] + self.num_connections = status['peer.num_peers_connected'] self.update_download_label() self.update_upload_label() self.update_traffic_label() self.update_connections_label() - if 'dht_nodes' in status: - self.dht_nodes = status['dht_nodes'] + if 'dht.dht_nodes' in status: + self.dht_nodes = status['dht.dht_nodes'] self.update_dht_label() - if 'has_incoming_connections' in status: - self.health = status['has_incoming_connections'] + if 'net.has_incoming_connections' in status: + self.health = status['net.has_incoming_connections'] if self.health: self.remove_item(self.health_item) @@ -413,7 +413,7 @@ class StatusBar(component.Component): if self.max_connections_global < 0: label_string = '%s' % self.num_connections else: - label_string = '%s <small>(%s)</small>' % ( + label_string = '{} <small>({})</small>'.format( self.num_connections, self.max_connections_global, ) diff --git a/deluge/ui/gtk3/systemtray.py b/deluge/ui/gtk3/systemtray.py index f851f32..f65fde5 100644 --- a/deluge/ui/gtk3/systemtray.py +++ b/deluge/ui/gtk3/systemtray.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os @@ -30,8 +27,12 @@ from .common import build_menu_radio_list, get_logo from .dialogs import OtherDialog try: - require_version('AppIndicator3', '0.1') - from gi.repository import AppIndicator3 + try: + require_version('AyatanaAppIndicator3', '0.1') + from gi.repository import AyatanaAppIndicator3 as AppIndicator3 + except (ValueError, ImportError): + require_version('AppIndicator3', '0.1') + from gi.repository import AppIndicator3 except (ValueError, ImportError): AppIndicator3 = None @@ -234,13 +235,13 @@ class SystemTray(component.Component): if max_download_speed == -1: max_download_speed = _('Unlimited') else: - max_download_speed = '%s %s' % (max_download_speed, _('K/s')) + max_download_speed = '{} {}'.format(max_download_speed, _('K/s')) if max_upload_speed == -1: max_upload_speed = _('Unlimited') else: - max_upload_speed = '%s %s' % (max_upload_speed, _('K/s')) + max_upload_speed = '{} {}'.format(max_upload_speed, _('K/s')) - msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % ( + msg = '{}\n{}: {} ({})\n{}: {} ({})'.format( _('Deluge'), _('Down'), self.download_rate, diff --git a/deluge/ui/gtk3/tab_data_funcs.py b/deluge/ui/gtk3/tab_data_funcs.py index 6fa0ba5..a78994f 100644 --- a/deluge/ui/gtk3/tab_data_funcs.py +++ b/deluge/ui/gtk3/tab_data_funcs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,14 +6,12 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from deluge.common import fdate, fsize, fspeed, ftime from deluge.ui.common import TRACKER_STATUS_TRANSLATION def ftotal_sized(first, second): - return '%s (%s)' % (fsize(first, shortform=True), fsize(second, shortform=True)) + return f'{fsize(first, shortform=True)} ({fsize(second, shortform=True)})' def fratio(value): @@ -24,7 +21,7 @@ def fratio(value): def fpcnt(value, state, message): state_i18n = _(state) if state not in ('Error', 'Seeding') and value < 100: - percent = '{:.2f}'.format(value).rstrip('0').rstrip('.') + percent = f'{value:.2f}'.rstrip('0').rstrip('.') return _('{state} {percent}%').format(state=state_i18n, percent=percent) elif state == 'Error': return _('{state}: {err_msg}').format(state=state_i18n, err_msg=message) @@ -34,7 +31,7 @@ def fpcnt(value, state, message): def fspeed_max(value, max_value=-1): value = fspeed(value, shortform=True) - return '%s (%s %s)' % (value, max_value, _('K/s')) if max_value > -1 else value + return '{} ({} {})'.format(value, max_value, _('K/s')) if max_value > -1 else value def fdate_or_never(value): @@ -73,7 +70,7 @@ def fseed_rank_or_dash(seed_rank, seeding_time): def fpieces_num_size(num_pieces, piece_size): - return '%s (%s)' % (num_pieces, fsize(piece_size, precision=0)) + return f'{num_pieces} ({fsize(piece_size, precision=0)})' def fcount(value): diff --git a/deluge/ui/gtk3/toolbar.py b/deluge/ui/gtk3/toolbar.py index 7bc029e..1b6952e 100644 --- a/deluge/ui/gtk3/toolbar.py +++ b/deluge/ui/gtk3/toolbar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from gi.repository.Gtk import SeparatorToolItem, ToolButton diff --git a/deluge/ui/gtk3/torrentdetails.py b/deluge/ui/gtk3/torrentdetails.py index 29e0193..08c37a1 100644 --- a/deluge/ui/gtk3/torrentdetails.py +++ b/deluge/ui/gtk3/torrentdetails.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # @@ -9,12 +8,17 @@ """The torrent details component shows info about the selected torrent.""" -from __future__ import unicode_literals - import logging from collections import namedtuple -from gi.repository.Gtk import CheckMenuItem, Menu, SeparatorMenuItem +from gi.repository.Gtk import ( + CheckMenuItem, + Menu, + MenuItem, + PositionType, + RadioMenuItem, + SeparatorMenuItem, +) import deluge.component as component from deluge.ui.client import client @@ -26,7 +30,7 @@ log = logging.getLogger(__name__) TabWidget = namedtuple('TabWidget', ('obj', 'func', 'status_keys')) -class Tab(object): +class Tab: def __init__(self, name=None, child_widget=None, tab_label=None): self._name = name self.is_visible = True @@ -100,8 +104,12 @@ class TorrentDetails(component.Component): def __init__(self): component.Component.__init__(self, 'TorrentDetails', interval=2) main_builder = component.get('MainWindow').get_builder() + self.config = component.get('MainWindow').config self.notebook = main_builder.get_object('torrent_info') + self.notebook.set_tab_pos( + getattr(PositionType, self.config['tabsbar_tab_pos'].upper()) + ) # This is the menu item we'll attach the tabs checklist menu to self.menu_tabs = main_builder.get_object('menu_tabs') @@ -112,11 +120,11 @@ class TorrentDetails(component.Component): self.tabs = {} # Add the default tabs - from .status_tab import StatusTab from .details_tab import DetailsTab from .files_tab import FilesTab - from .peers_tab import PeersTab from .options_tab import OptionsTab + from .peers_tab import PeersTab + from .status_tab import StatusTab from .trackers_tab import TrackersTab default_tabs = { @@ -175,7 +183,6 @@ class TorrentDetails(component.Component): # Generate the checklist menu self.generate_menu() - self.config = component.get('MainWindow').config self.visible(self.config['show_tabsbar']) def tab_insert_position(self, weight): @@ -314,9 +321,24 @@ class TorrentDetails(component.Component): self.generate_menu() self.visible(True) + def create_tab_pos_menuitem(self): + """Returns a menu to select which side of the notebook the tabs should be shown""" + tab_pos_menu = Menu() + tab_pos_menuitem = MenuItem.new_with_label(_('Position')) + group = [] + for pos in ('top', 'right', 'bottom', 'left'): + menuitem = RadioMenuItem.new_with_mnemonic(group, _(pos.capitalize())) + group = menuitem.get_group() + menuitem.connect('toggled', self._on_tabs_pos_toggled, pos) + menuitem.set_active(pos == self.notebook.get_tab_pos().value_nick) + tab_pos_menu.append(menuitem) + tab_pos_menuitem.set_submenu(tab_pos_menu) + return tab_pos_menuitem + def generate_menu(self): """Generates the checklist menu for all the tabs and attaches it""" menu = Menu() + # Create 'All' menuitem and a separator menuitem = CheckMenuItem.new_with_mnemonic(self.translate_tabs['All']) menuitem.set_name('All') @@ -347,6 +369,9 @@ class TorrentDetails(component.Component): menuitem.connect('toggled', self._on_menuitem_toggled) menu.append(menuitem) + menu.append(SeparatorMenuItem()) + menu.append(self.create_tab_pos_menuitem()) + self.menu_tabs.set_submenu(menu) self.menu_tabs.show_all() @@ -440,6 +465,10 @@ class TorrentDetails(component.Component): self.set_tab_visible(name, widget.get_active()) + def _on_tabs_pos_toggled(self, widget, position): + self.config['tabsbar_tab_pos'] = position + self.notebook.set_tab_pos(getattr(PositionType, position.upper())) + def save_state(self): """We save the state, which is basically the tab_index list""" # Update the visiblity status of all tabs diff --git a/deluge/ui/gtk3/torrentview.py b/deluge/ui/gtk3/torrentview.py index fcc6edf..16de16e 100644 --- a/deluge/ui/gtk3/torrentview.py +++ b/deluge/ui/gtk3/torrentview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -8,8 +7,6 @@ # """The torrent view component that lists all torrents in the session.""" -from __future__ import unicode_literals - import logging from locale import strcoll @@ -77,13 +74,13 @@ def eta_column_sort(model, iter1, iter2, data): if v1 == v2: return 0 if v1 == 0: - return 1 - if v2 == 0: return -1 - if v1 > v2: + if v2 == 0: return 1 - if v2 > v1: + if v1 > v2: return -1 + if v2 > v1: + return 1 def seed_peer_column_sort(model, iter1, iter2, data): @@ -107,7 +104,7 @@ def progress_sort(model, iter1, iter2, sort_column_id): return cmp(progress1, progress2) -class SearchBox(object): +class SearchBox: def __init__(self, torrentview): self.torrentview = torrentview mainwindow = component.get('MainWindow') @@ -404,6 +401,13 @@ class TorrentView(ListView, component.Component): status_field=['last_seen_complete'], default=False, ) + self.add_func_column( + _('Last Transfer'), + funcs.cell_data_time, + [int], + status_field=['time_since_transfer'], + default=False, + ) self.add_texticon_column( _('Tracker'), function=funcs.cell_data_trackericon, diff --git a/deluge/ui/gtk3/torrentview_data_funcs.py b/deluge/ui/gtk3/torrentview_data_funcs.py index 8bd1f9c..0b2545d 100644 --- a/deluge/ui/gtk3/torrentview_data_funcs.py +++ b/deluge/ui/gtk3/torrentview_data_funcs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import print_function, unicode_literals - import warnings from functools import partial @@ -17,7 +14,7 @@ import deluge.component as component from .common import ( create_blank_pixbuf, - get_pixbuf_at_size, + get_pixbuf, icon_alert, icon_checking, icon_downloading, @@ -42,7 +39,6 @@ ICON_STATE = { # renderer. This is much cheaper than fetch the current value and test if # it's equal. func_last_value = { - 'cell_data_time': None, 'cell_data_ratio_seeds_peers': None, 'cell_data_ratio_ratio': None, 'cell_data_ratio_avail': None, @@ -86,7 +82,7 @@ def set_tracker_icon(tracker_icon, cell): if tracker_icon: pixbuf = tracker_icon.get_cached_icon() if pixbuf is None: - pixbuf = get_pixbuf_at_size(tracker_icon.get_filename(), 16) + pixbuf = get_pixbuf(tracker_icon.get_filename(), 16) tracker_icon.set_cached_icon(pixbuf) else: pixbuf = create_blank_pixbuf() @@ -162,7 +158,7 @@ def cell_data_speed(cell, model, row, data): if speed > 0: speed_str = common.fspeed(speed, shortform=True) cell.set_property( - 'markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split())) + 'markup', '{} <small>{}</small>'.format(*tuple(speed_str.split())) ) else: cell.set_property('text', '') @@ -189,7 +185,7 @@ def cell_data_speed_limit(cell, model, row, data, cache_key): if speed > 0: speed_str = common.fspeed(speed * 1024, shortform=True) cell.set_property( - 'markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split())) + 'markup', '{} <small>{}</small>'.format(*tuple(speed_str.split())) ) else: cell.set_property('text', '') @@ -222,10 +218,6 @@ def cell_data_peer(column, cell, model, row, data): def cell_data_time(column, cell, model, row, data): """Display value as time, eg 1m10s""" time = model.get_value(row, data) - if func_last_value['cell_data_time'] == time: - return - func_last_value['cell_data_time'] = time - if time <= 0: time_str = '' else: diff --git a/deluge/ui/gtk3/trackers_tab.py b/deluge/ui/gtk3/trackers_tab.py index d83b995..d671471 100644 --- a/deluge/ui/gtk3/trackers_tab.py +++ b/deluge/ui/gtk3/trackers_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -22,9 +19,7 @@ log = logging.getLogger(__name__) class TrackersTab(Tab): def __init__(self): - super(TrackersTab, self).__init__( - 'Trackers', 'trackers_tab', 'trackers_tab_label' - ) + super().__init__('Trackers', 'trackers_tab', 'trackers_tab_label') self.add_tab_widget('summary_next_announce', ftime, ('next_announce',)) self.add_tab_widget('summary_tracker', None, ('tracker_host',)) diff --git a/deluge/ui/hostlist.py b/deluge/ui/hostlist.py index ee4c7df..0fc3eab 100644 --- a/deluge/ui/hostlist.py +++ b/deluge/ui/hostlist.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) Calum Lind 2017 <calumlind+deluge@gmail.com> # @@ -7,12 +6,10 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os import uuid -from socket import gaierror, gethostbyname +from socket import gaierror, getaddrinfo from twisted.internet import defer @@ -25,7 +22,7 @@ log = logging.getLogger(__name__) DEFAULT_HOST = '127.0.0.1' DEFAULT_PORT = 58846 -LOCALHOST = ('127.0.0.1', 'localhost') +LOCALHOST = ('127.0.0.1', 'localhost', '::1') def default_hostlist(): @@ -47,7 +44,7 @@ def validate_host_info(hostname, port): """ try: - gethostbyname(hostname) + getaddrinfo(hostname, None) except gaierror as ex: raise ValueError('Host %s: %s', hostname, ex.args[1]) @@ -87,7 +84,15 @@ def migrate_config_2_to_3(config): return config -class HostList(object): +def mask_hosts_password(hosts): + """Replace passwords in hosts list with *'s for log output""" + if not hosts: + return hosts + + return [list(host)[:-1] + ['*' * 10] for host in hosts] + + +class HostList: """This class contains methods for adding, removing and looking up hosts in hostlist.conf.""" def __init__(self): @@ -97,12 +102,13 @@ class HostList(object): default_hostlist(), config_dir=get_config_dir(), file_version=3, + log_mask_funcs={'hosts': mask_hosts_password}, ) self.config.run_converter((1, 2), 3, migrate_config_2_to_3) self.config.save() def check_info_exists(self, hostname, port, username, skip_host_id=None): - """Check for exising host entries with the same details. + """Check for existing host entries with the same details. Args: hostname (str): The IP or hostname of the deluge daemon. @@ -207,33 +213,38 @@ class HostList(object): host_id, host, port, user = self.get_host_info(host_id) except ValueError: log.warning('Problem getting host_id info from hostlist') - return status_offline + return defer.succeed(status_offline) try: - ip = gethostbyname(host) - except gaierror as ex: - log.error('Error resolving host %s to ip: %s', host, ex.args[1]) - return status_offline - - host_conn_info = ( - ip, - port, - 'localclient' if not user and host in LOCALHOST else user, - ) - if client.connected() and host_conn_info == client.connection_info(): - # Currently connected to host_id daemon. - def on_info(info, host_id): - log.debug('Client connected, query info: %s', info) - return host_id, 'Connected', info - - return client.daemon.info().addCallback(on_info, host_id) - else: - # Attempt to connect to daemon with host_id details. - c = Client() - d = c.connect(host, port, skip_authentication=True) - d.addCallback(on_connect, c, host_id) - d.addErrback(on_connect_failed, host_id) - return d + ips = list({addrinfo[4][0] for addrinfo in getaddrinfo(host, None)}) + except (gaierror, IndexError) as ex: + log.warning('Unable to resolve host %s to IP: %s', host, ex.args[1]) + return defer.succeed(status_offline) + + host_conn_list = [ + ( + host_ip, + port, + 'localclient' if not user and host_ip in LOCALHOST else user, + ) + for host_ip in ips + ] + + for host_conn_info in host_conn_list: + if client.connected() and host_conn_info == client.connection_info(): + # Currently connected to host_id daemon. + def on_info(info, host_id): + log.debug('Client connected, query info: %s', info) + return host_id, 'Connected', info + + return client.daemon.info().addCallback(on_info, host_id) + else: + # Attempt to connect to daemon with host_id details. + c = Client() + d = c.connect(host, port, skip_authentication=True) + d.addCallback(on_connect, c, host_id) + d.addErrback(on_connect_failed, host_id) + return d def update_host(self, host_id, hostname, port, username, password): """Update the supplied host id with new connection details. diff --git a/deluge/ui/sessionproxy.py b/deluge/ui/sessionproxy.py index 5af8e79..b50ba6c 100644 --- a/deluge/ui/sessionproxy.py +++ b/deluge/ui/sessionproxy.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2010 Andrew Resch <andrewresch@gmail.com> # @@ -6,8 +5,6 @@ # the additional special exception to link portions of this program with the OpenSSL library. # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from time import time @@ -148,11 +145,17 @@ class SessionProxy(component.Component): def on_status(result, torrent_id): t = time() - self.torrents[torrent_id][0] = t - self.torrents[torrent_id][1].update(result) - for key in keys_to_get: - self.cache_times[torrent_id][key] = t - return self.create_status_dict([torrent_id], keys)[torrent_id] + try: + self.torrents[torrent_id][0] = t + self.torrents[torrent_id][1].update(result) + for key in keys_to_get: + self.cache_times[torrent_id][key] = t + return self.create_status_dict([torrent_id], keys)[torrent_id] + except KeyError: + log.debug( + f'Status missing for torrent (removed?): {torrent_id}' + ) + return {} return d.addCallback(on_status, torrent_id) else: diff --git a/deluge/ui/tracker_icons.py b/deluge/ui/tracker_icons.py index c10cd2f..5f619af 100644 --- a/deluge/ui/tracker_icons.py +++ b/deluge/ui/tracker_icons.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com> # @@ -7,14 +6,14 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os -from tempfile import mkstemp +import tempfile +from html.parser import HTMLParser +from urllib.parse import urljoin, urlparse from twisted.internet import defer, threads -from twisted.web.error import PageRedirect +from twisted.python.failure import Failure from twisted.web.resource import ForbiddenResource, NoResource from deluge.component import Component @@ -23,12 +22,9 @@ from deluge.decorators import proxy from deluge.httpdownloader import download_file try: - from html.parser import HTMLParser - from urllib.parse import urljoin, urlparse + import chardet except ImportError: - # PY2 fallback - from HTMLParser import HTMLParser - from urlparse import urljoin, urlparse # pylint: disable=ungrouped-imports + chardet = None try: from PIL import Image @@ -38,7 +34,7 @@ except ImportError: log = logging.getLogger(__name__) -class TrackerIcon(object): +class TrackerIcon: """ Represents a tracker's icon """ @@ -207,17 +203,19 @@ class TrackerIcons(Component): else: # We need to fetch it self.pending[host] = [] + tmp_file = tempfile.mkstemp(prefix='deluge_trackericon_html.') + filename = tmp_file[1] # Start callback chain - d = self.download_page(host) + d = self.download_page(host, filename) d.addCallbacks( self.on_download_page_complete, self.on_download_page_fail, - errbackArgs=(host,), ) d.addCallback(self.parse_html_page) d.addCallbacks( self.on_parse_complete, self.on_parse_fail, callbackArgs=(host,) ) + d.addBoth(self.del_tmp_file, tmp_file) d.addCallback(self.download_icon, host) d.addCallbacks( self.on_download_icon_complete, @@ -229,24 +227,38 @@ class TrackerIcons(Component): d.addCallback(self.store_icon, host) return d - def download_page(self, host, url=None): - """ - Downloads a tracker host's page + @staticmethod + def del_tmp_file(result, tmp_file): + """Remove tmp_file created when downloading tracker page""" + fd, filename = tmp_file + try: + os.close(fd) + os.remove(filename) + except OSError: + log.debug(f'Unable to delete temporary file: {filename}') + + return result + + def download_page( + self, host: str, filename: str, url: str = None + ) -> 'defer.Deferred[str]': + """Downloads a tracker host's page + If no url is provided, it bases the url on the host - :param host: the tracker host - :type host: string - :param url: the (optional) url of the host - :type url: string - :returns: the filename of the tracker host's page - :rtype: Deferred + Args: + host: The tracker host + filename: Location to download page + url: The url of the host + + Returns: + The filename of the tracker host's page """ if not url: url = self.host_to_url(host) - log.debug('Downloading %s %s', host, url) - tmp_fd, tmp_file = mkstemp(prefix='deluge_ticon.') - os.close(tmp_fd) - return download_file(url, tmp_file, force_filename=True, handle_redirects=False) + + log.debug(f'Downloading {host} {url} to {filename}') + return download_file(url, filename, force_filename=True) def on_download_page_complete(self, page): """ @@ -260,33 +272,18 @@ class TrackerIcons(Component): log.debug('Finished downloading %s', page) return page - def on_download_page_fail(self, f, host): - """ - Recovers from download error + def on_download_page_fail(self, failure: 'Failure') -> 'Failure': + """Runs any download failure clean-up functions - :param f: the failure that occurred - :type f: Failure - :param host: the name of the host whose page failed to download - :type host: string - :returns: a Deferred if recovery was possible - else the original failure - :rtype: Deferred or Failure - """ - error_msg = f.getErrorMessage() - log.debug('Error downloading page: %s', error_msg) - d = f - if f.check(PageRedirect): - # Handle redirect errors - location = urljoin(self.host_to_url(host), error_msg.split(' to ')[1]) - self.redirects[host] = url_to_host(location) - d = self.download_page(host, url=location) - d.addCallbacks( - self.on_download_page_complete, - self.on_download_page_fail, - errbackArgs=(host,), - ) + Args: + failure: The failure that occurred. - return d + Returns: + The original failure. + + """ + log.debug(f'Error downloading page: {failure.getErrorMessage()}') + return failure @proxy(threads.deferToThread) def parse_html_page(self, page): @@ -298,17 +295,19 @@ class TrackerIcons(Component): :returns: a Deferred which callbacks a list of available favicons (url, type) :rtype: Deferred """ - with open(page, 'r') as _file: + encoding = 'UTF-8' + if chardet: + with open(page, 'rb') as _file: + result = chardet.detect(_file.read()) + encoding = result['encoding'] + + with open(page, encoding=encoding) as _file: parser = FaviconParser() for line in _file: parser.feed(line) if parser.left_head: break parser.close() - try: - os.remove(page) - except OSError as ex: - log.warning('Could not remove temp file: %s', ex) return parser.get_icons() @@ -382,7 +381,7 @@ class TrackerIcons(Component): try: with Image.open(icon_name): pass - except IOError as ex: + except OSError as ex: raise InvalidIconError(ex) else: if not os.path.getsize(icon_name): @@ -423,22 +422,7 @@ class TrackerIcons(Component): error_msg = f.getErrorMessage() log.debug('Error downloading icon from %s: %s', host, error_msg) d = f - if f.check(PageRedirect): - # Handle redirect errors - location = urljoin(self.host_to_url(host), error_msg.split(' to ')[1]) - d = self.download_icon( - [(location, extension_to_mimetype(location.rpartition('.')[2]))] - + icons, - host, - ) - if not icons: - d.addCallbacks( - self.on_download_icon_complete, - self.on_download_icon_fail, - callbackArgs=(host,), - errbackArgs=(host,), - ) - elif f.check(NoResource, ForbiddenResource) and icons: + if f.check(NoResource, ForbiddenResource) and icons: d = self.download_icon(icons, host) elif f.check(NoIconsError): # No icons, try favicon.ico as an act of desperation @@ -477,14 +461,17 @@ class TrackerIcons(Component): # Requires Pillow(PIL) to resize. if icon and Image: filename = icon.get_filename() + remove_old = False with Image.open(filename) as img: if img.size > (16, 16): new_filename = filename.rpartition('.')[0] + '.png' img = img.resize((16, 16), Image.ANTIALIAS) img.save(new_filename) if new_filename != filename: - os.remove(filename) - icon = TrackerIcon(new_filename) + remove_old = True + if remove_old: + os.remove(filename) + icon = TrackerIcon(new_filename) return icon def store_icon(self, icon, host): @@ -617,11 +604,13 @@ MIME_MAP = { 'image/png': 'png', 'image/vnd.microsoft.icon': 'ico', 'image/x-icon': 'ico', + 'image/svg+xml': 'svg', 'gif': 'image/gif', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'png': 'image/png', 'ico': 'image/vnd.microsoft.icon', + 'svg': 'image/svg+xml', } diff --git a/deluge/ui/ui.py b/deluge/ui/ui.py index 0986ec7..338f8a8 100644 --- a/deluge/ui/ui.py +++ b/deluge/ui/ui.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # @@ -7,9 +6,8 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging +import sys import deluge.common import deluge.configmanager @@ -27,7 +25,7 @@ except ImportError: return -class UI(object): +class UI: """ Base class for UI implementations. @@ -60,7 +58,7 @@ class UI(object): return self.__options def start(self, parser=None): - args = deluge.common.unicode_argv()[1:] + args = sys.argv[1:] if parser is None: parser = self.parser self.__options = self.parse_args(parser, args) diff --git a/deluge/ui/ui_entry.py b/deluge/ui/ui_entry.py index 71ce837..e185fda 100644 --- a/deluge/ui/ui_entry.py +++ b/deluge/ui/ui_entry.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2010 Pedro Algarvio <pedro@algarvio.me> @@ -12,8 +11,6 @@ # user runs the command 'deluge'. """Main starting point for Deluge""" -from __future__ import unicode_literals - import argparse import logging import os @@ -100,7 +97,7 @@ def start_ui(): # If the UI is set as default, indicate this in help by prefixing with a star. subactions = subparsers._get_subactions() prefix = '*' if ui == default_ui else ' ' - subactions[-1].metavar = '%s %s' % (prefix, ui) + subactions[-1].metavar = f'{prefix} {ui}' # Insert a default UI subcommand unless one of the ambiguous_args are specified parser.set_default_subparser(default_ui, abort_opts=AMBIGUOUS_CMD_ARGS) @@ -115,7 +112,7 @@ def start_ui(): try: ui = ui_entrypoints[selected_ui]( - prog='%s %s' % (os.path.basename(sys.argv[0]), selected_ui), ui_args=ui_args + prog=f'{os.path.basename(sys.argv[0])} {selected_ui}', ui_args=ui_args ) except KeyError: log.error( diff --git a/deluge/ui/web/__init__.py b/deluge/ui/web/__init__.py index 0be7eed..3757e0b 100644 --- a/deluge/ui/web/__init__.py +++ b/deluge/ui/web/__init__.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from deluge.ui.web.web import Web diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py index fa95049..eacbbf5 100644 --- a/deluge/ui/web/auth.py +++ b/deluge/ui/web/auth.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Damien Churchill <damoxc@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import hashlib import logging import os @@ -60,11 +57,11 @@ def make_expires(timeout): class Auth(JSONComponent): """ - The component that implements authentification into the JSON interface. + The component that implements authentication into the JSON interface. """ def __init__(self, config): - super(Auth, self).__init__('Auth') + super().__init__('Auth') self.worker = LoopingCall(self._clean_sessions) self.config = config diff --git a/deluge/ui/web/common.py b/deluge/ui/web/common.py index 475f335..32c29c8 100644 --- a/deluge/ui/web/common.py +++ b/deluge/ui/web/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Damien Churchill <damoxc@gmail.com> # @@ -7,19 +6,15 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import gettext from mako.template import Template as MakoTemplate -from deluge.common import PY2, get_version +from deluge.common import get_version def _(text): text_local = gettext.gettext(text) - if PY2: - return text_local.decode('utf-8') return text_local diff --git a/deluge/ui/web/css/deluge.css b/deluge/ui/web/css/deluge.css index b9fa03e..9460286 100644 --- a/deluge/ui/web/css/deluge.css +++ b/deluge/ui/web/css/deluge.css @@ -6,6 +6,8 @@ body { border: 0 none; overflow: hidden; height: 100%; + color: black; + background: white; } input { @@ -458,8 +460,16 @@ dl.singleline dd { background-image: url('../icons/add_url.png') !important; } -.icon-add-magnet { - background-image: url('../icons/add_magnet.png') !important; +.icon-magnet-add { + background-image: url('../icons/magnet_add.png') !important; +} + +.icon-magnet-copy { + background-image: url('../icons/magnet_copy.png') !important; +} + +.icon-magnet { + background-image: url('../icons/magnet.png') !important; } .icon-pause { diff --git a/deluge/ui/web/icons/active.png b/deluge/ui/web/icons/active.png Binary files differindex daa4f64..c9af82a 100644 --- a/deluge/ui/web/icons/active.png +++ b/deluge/ui/web/icons/active.png diff --git a/deluge/ui/web/icons/add_magnet.png b/deluge/ui/web/icons/add_magnet.png Binary files differdeleted file mode 100644 index c015b18..0000000 --- a/deluge/ui/web/icons/add_magnet.png +++ /dev/null diff --git a/deluge/ui/web/icons/checking.png b/deluge/ui/web/icons/checking.png Binary files differindex 7487352..6758e36 100644 --- a/deluge/ui/web/icons/checking.png +++ b/deluge/ui/web/icons/checking.png diff --git a/deluge/ui/web/icons/deluge.png b/deluge/ui/web/icons/deluge.png Binary files differindex 2f4ae4c..5afdbe4 100644 --- a/deluge/ui/web/icons/deluge.png +++ b/deluge/ui/web/icons/deluge.png diff --git a/deluge/ui/web/icons/downloading.png b/deluge/ui/web/icons/downloading.png Binary files differindex ec58cb5..24d6ffa 100644 --- a/deluge/ui/web/icons/downloading.png +++ b/deluge/ui/web/icons/downloading.png diff --git a/deluge/ui/web/icons/inactive.png b/deluge/ui/web/icons/inactive.png Binary files differindex b56213e..cae8b2c 100644 --- a/deluge/ui/web/icons/inactive.png +++ b/deluge/ui/web/icons/inactive.png diff --git a/deluge/ui/web/icons/magnet.png b/deluge/ui/web/icons/magnet.png Binary files differnew file mode 100644 index 0000000..61d6dab --- /dev/null +++ b/deluge/ui/web/icons/magnet.png diff --git a/deluge/ui/web/icons/magnet_add.png b/deluge/ui/web/icons/magnet_add.png Binary files differnew file mode 100644 index 0000000..37c1c36 --- /dev/null +++ b/deluge/ui/web/icons/magnet_add.png diff --git a/deluge/ui/web/icons/magnet_copy.png b/deluge/ui/web/icons/magnet_copy.png Binary files differnew file mode 100644 index 0000000..a4be9d2 --- /dev/null +++ b/deluge/ui/web/icons/magnet_copy.png diff --git a/deluge/ui/web/js/deluge-all-debug.js b/deluge/ui/web/js/deluge-all-debug.js index afbbabe..67bb83a 100644 --- a/deluge/ui/web/js/deluge-all-debug.js +++ b/deluge/ui/web/js/deluge-all-debug.js @@ -15,15 +15,15 @@ Ext.ns('Deluge.add'); * Base class for an add Window */ Deluge.add.Window = Ext.extend(Ext.Window, { - initComponent: function() { + initComponent: function () { Deluge.add.Window.superclass.initComponent.call(this); - this.addEvents('beforeadd', 'add'); + this.addEvents('beforeadd', 'add', 'addfailed'); }, /** * Create an id for the torrent before we have any info about it. */ - createTorrentId: function() { + createTorrentId: function () { return new Date().getTime().toString(); }, }); @@ -41,7 +41,7 @@ Ext.namespace('Deluge.add'); // This override allows file upload buttons to contain icons Ext.override(Ext.ux.form.FileUploadField, { - onRender: function(ct, position) { + onRender: function (ct, position) { Ext.ux.form.FileUploadField.superclass.onRender.call( this, ct, @@ -87,26 +87,12 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { plain: true, iconCls: 'x-deluge-add-window-icon', - initComponent: function() { + initComponent: function () { Deluge.add.AddWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancelClick, this); this.addButton(_('Add'), this.onAddClick, this); - function torrentRenderer(value, p, r) { - if (r.data['info_hash']) { - return String.format( - '<div class="x-deluge-add-torrent-name">{0}</div>', - value - ); - } else { - return String.format( - '<div class="x-deluge-add-torrent-name-loading">{0}</div>', - value - ); - } - } - this.list = new Ext.list.ListView({ store: new Ext.data.SimpleStore({ fields: [ @@ -120,8 +106,10 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { id: 'torrent', width: 150, sortable: true, - renderer: torrentRenderer, dataIndex: 'text', + tpl: new Ext.XTemplate( + '<div class="x-deluge-add-torrent-name">{text:htmlEncode}</div>' + ), }, ], stripeRows: true, @@ -176,7 +164,7 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { }, { text: _('Infohash'), - iconCls: 'icon-add-magnet', + iconCls: 'icon-magnet-add', hidden: true, disabled: true, }, @@ -197,17 +185,17 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { this.on('show', this.onShow, this); }, - clear: function() { + clear: function () { this.list.getStore().removeAll(); this.optionsPanel.clear(); // Reset upload form so handler fires when a canceled file is reselected this.fileUploadForm.reset(); }, - onAddClick: function() { + onAddClick: function () { var torrents = []; if (!this.list) return; - this.list.getStore().each(function(r) { + this.list.getStore().each(function (r) { var id = r.get('info_hash'); torrents.push({ path: this.optionsPanel.getFilename(id), @@ -216,29 +204,29 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { }, this); deluge.client.web.add_torrents(torrents, { - success: function(result) {}, + success: function (result) {}, }); this.clear(); this.hide(); }, - onCancelClick: function() { + onCancelClick: function () { this.clear(); this.hide(); }, - onFile: function() { + onFile: function () { if (!this.file) this.file = new Deluge.add.FileWindow(); this.file.show(); }, - onHide: function() { + onHide: function () { this.optionsPanel.setActiveTab(0); this.optionsPanel.files.setDisabled(true); this.optionsPanel.form.setDisabled(true); }, - onRemove: function() { + onRemove: function () { if (!this.list.getSelectionCount()) return; var torrent = this.list.getSelectedRecords()[0]; if (!torrent) return; @@ -249,7 +237,7 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { delete this.torrents[torrent.id]; }, - onSelect: function(list, selections) { + onSelect: function (list, selections) { if (selections.length) { var record = this.list.getRecord(selections[0]); this.optionsPanel.setTorrent(record.get('info_hash')); @@ -259,24 +247,25 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { } }, - onShow: function() { + onShow: function () { if (!this.url) { this.url = new Deluge.add.UrlWindow(); this.url.on('beforeadd', this.onTorrentBeforeAdd, this); this.url.on('add', this.onTorrentAdd, this); + this.url.on('addfailed', this.onTorrentAddFailed, this); } this.optionsPanel.form.getDefaults(); }, - onFileSelected: function() { + onFileSelected: function () { if (this.fileUploadForm.isValid()) { var torrentIds = []; var files = this.fileUploadForm.findField('torrentFile').value; var randomId = this.createTorrentId(); Array.prototype.forEach.call( files, - function(file, i) { + function (file, i) { // Append index for batch of unique torrentIds. var torrentId = randomId + i.toString(); torrentIds.push(torrentId); @@ -287,20 +276,21 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { url: deluge.config.base + 'upload', waitMsg: _('Uploading your torrent...'), success: this.onUploadSuccess, + failure: this.onUploadFailure, scope: this, torrentIds: torrentIds, }); } }, - onUploadSuccess: function(fp, upload) { + onUploadSuccess: function (fp, upload) { if (!upload.result.success) { this.clear(); return; } upload.result.files.forEach( - function(filename, i) { + function (filename, i) { deluge.client.web.get_torrent_info(filename, { success: this.onGotInfo, scope: this, @@ -312,18 +302,31 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { this.fileUploadForm.reset(); }, - onGotInfo: function(info, obj, response, request) { + onUploadFailure: function (form, action) { + this.hide(); + Ext.MessageBox.show({ + title: _('Error'), + msg: _('Failed to upload torrent'), + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error', + }); + this.fireEvent('addfailed', this.torrentId); + }, + + onGotInfo: function (info, obj, response, request) { info.filename = request.options.filename; torrentId = request.options.torrentId; this.onTorrentAdd(torrentId, info); }, - onTorrentBeforeAdd: function(torrentId, text) { + onTorrentBeforeAdd: function (torrentId, text) { var store = this.list.getStore(); store.loadData([[torrentId, null, text]], true); }, - onTorrentAdd: function(torrentId, info) { + onTorrentAdd: function (torrentId, info) { var r = this.list.getStore().getById(torrentId); if (!info) { Ext.MessageBox.show({ @@ -344,7 +347,15 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { } }, - onUrl: function(button, event) { + onTorrentAddFailed: function (torrentId) { + var store = this.list.getStore(); + var torrentRecord = store.getById(torrentId); + if (torrentRecord) { + store.remove(torrentRecord); + } + }, + + onUrl: function (button, event) { this.url.show(); }, }); @@ -378,13 +389,14 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { header: _('Filename'), width: 295, dataIndex: 'filename', + tpl: new Ext.XTemplate('{filename:htmlEncode}'), }, { header: _('Size'), width: 60, dataIndex: 'size', tpl: new Ext.XTemplate('{size:this.fsize}', { - fsize: function(v) { + fsize: function (v) { return fsize(v); }, }), @@ -394,7 +406,7 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { width: 65, dataIndex: 'download', tpl: new Ext.XTemplate('{download:this.format}', { - format: function(v) { + format: function (v) { return ( '<div rel="chkbox" class="x-grid3-check-col' + (v ? '-on' : '') + @@ -405,21 +417,21 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { }, ], - initComponent: function() { + initComponent: function () { Deluge.add.FilesTab.superclass.initComponent.call(this); this.on('click', this.onNodeClick, this); }, - clearFiles: function() { + clearFiles: function () { var root = this.getRootNode(); if (!root.hasChildNodes()) return; - root.cascade(function(node) { + root.cascade(function (node) { if (!node.parentNode || !node.getOwnerTree()) return; node.remove(); }); }, - setDownload: function(node, value, suppress) { + setDownload: function (node, value, suppress) { node.attributes.download = value; node.ui.updateColumns(); @@ -429,7 +441,7 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { } } else { var nodes = [node]; - node.cascade(function(n) { + node.cascade(function (n) { n.attributes.download = value; n.ui.updateColumns(); nodes.push(n); @@ -440,7 +452,7 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { } }, - onNodeClick: function(node, e) { + onNodeClick: function (node, e) { var el = new Ext.Element(e.target); if (el.getAttribute('rel') == 'chkbox') { this.setDownload(node, !node.attributes.download); @@ -477,7 +489,7 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { activeTab: 0, height: 265, - initComponent: function() { + initComponent: function () { Deluge.add.OptionsPanel.superclass.initComponent.call(this); this.files = this.add(new Deluge.add.FilesTab()); this.form = this.add(new Deluge.add.OptionsTab()); @@ -485,12 +497,12 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { this.files.on('fileschecked', this.onFilesChecked, this); }, - addTorrent: function(torrent) { + addTorrent: function (torrent) { this.torrents[torrent['info_hash']] = torrent; var fileIndexes = {}; this.walkFileTree( torrent['files_tree'], - function(filename, type, entry, parent) { + function (filename, type, entry, parent) { if (type != 'file') return; fileIndexes[entry.index] = entry.download; }, @@ -498,7 +510,7 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { ); var priorities = []; - Ext.each(Ext.keys(fileIndexes), function(index) { + Ext.each(Ext.keys(fileIndexes), function (index) { priorities[index] = fileIndexes[index]; }); @@ -510,26 +522,26 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { this.form.optionsManager.changeId(oldId, true); }, - clear: function() { + clear: function () { this.files.clearFiles(); this.form.optionsManager.resetAll(); }, - getFilename: function(torrentId) { + getFilename: function (torrentId) { return this.torrents[torrentId]['filename']; }, - getOptions: function(torrentId) { + getOptions: function (torrentId) { var oldId = this.form.optionsManager.changeId(torrentId, true); var options = this.form.optionsManager.get(); this.form.optionsManager.changeId(oldId, true); - Ext.each(options['file_priorities'], function(priority, index) { + Ext.each(options['file_priorities'], function (priority, index) { options['file_priorities'][index] = priority ? 1 : 0; }); return options; }, - setTorrent: function(torrentId) { + setTorrent: function (torrentId) { if (!torrentId) return; this.torrentId = torrentId; @@ -544,7 +556,7 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { if (this.torrents[torrentId]['files_tree']) { this.walkFileTree( this.torrents[torrentId]['files_tree'], - function(filename, type, entry, parentNode) { + function (filename, type, entry, parentNode) { var node = new Ext.tree.TreeNode({ download: entry.index ? priorities[entry.index] : true, filename: filename, @@ -568,7 +580,7 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { } }, - walkFileTree: function(files, callback, scope, parentNode) { + walkFileTree: function (files, callback, scope, parentNode) { for (var filename in files.contents) { var entry = files.contents[filename]; var type = entry.type; @@ -588,14 +600,13 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { } }, - onFilesChecked: function(nodes, newValue, oldValue) { + onFilesChecked: function (nodes, newValue, oldValue) { Ext.each( nodes, - function(node) { + function (node) { if (node.attributes.fileindex < 0) return; - var priorities = this.form.optionsManager.get( - 'file_priorities' - ); + var priorities = + this.form.optionsManager.get('file_priorities'); priorities[node.attributes.fileindex] = newValue; this.form.optionsManager.update('file_priorities', priorities); }, @@ -626,7 +637,7 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { disabled: true, labelWidth: 1, - initComponent: function() { + initComponent: function () { Deluge.add.OptionsTab.superclass.initComponent.call(this); this.optionsManager = new Deluge.MultiOptionsManager(); @@ -779,7 +790,7 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { ); }, - getDefaults: function() { + getDefaults: function () { var keys = [ 'add_paused', 'pre_allocate_storage', @@ -795,7 +806,7 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { ]; deluge.client.core.get_config_values(keys, { - success: function(config) { + success: function (config) { var options = { file_priorities: [], add_paused: config.add_paused, @@ -844,7 +855,7 @@ Deluge.add.UrlWindow = Ext.extend(Deluge.add.Window, { bodyStyle: 'padding: 10px 5px;', iconCls: 'x-deluge-add-url-window-icon', - initComponent: function() { + initComponent: function () { Deluge.add.UrlWindow.superclass.initComponent.call(this); this.addButton(_('Add'), this.onAddClick, this); @@ -872,7 +883,7 @@ Deluge.add.UrlWindow = Ext.extend(Deluge.add.Window, { this.cookieField.on('specialkey', this.onAdd, this); }, - onAddClick: function(field, e) { + onAddClick: function (field, e) { if ( (field.id == 'url' || field.id == 'cookies') && e.getKey() != e.ENTER @@ -894,6 +905,7 @@ Deluge.add.UrlWindow = Ext.extend(Deluge.add.Window, { } else { deluge.client.web.download_torrent_from_url(url, cookies, { success: this.onDownload, + failure: this.onDownloadFailed, scope: this, torrentId: torrentId, }); @@ -904,16 +916,29 @@ Deluge.add.UrlWindow = Ext.extend(Deluge.add.Window, { this.fireEvent('beforeadd', torrentId, url); }, - onDownload: function(filename, obj, resp, req) { + onDownload: function (filename, obj, resp, req) { deluge.client.web.get_torrent_info(filename, { success: this.onGotInfo, + failure: this.onDownloadFailed, scope: this, filename: filename, torrentId: req.options.torrentId, }); }, - onGotInfo: function(info, obj, response, request) { + onDownloadFailed: function (obj, resp, req) { + Ext.MessageBox.show({ + title: _('Error'), + msg: _('Failed to download torrent'), + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error', + }); + this.fireEvent('addfailed', req.options.torrentId); + }, + + onGotInfo: function (info, obj, response, request) { info['filename'] = request.options.filename; this.fireEvent('add', request.options.torrentId, info); }, @@ -947,11 +972,11 @@ Deluge.data.SortTypes = { return ((+d[1] * 256 + (+d[2])) * 256 + (+d[3])) * 256 + (+d[4]); }, - asQueuePosition: function(value) { + asQueuePosition: function (value) { return value > -1 ? value : Number.MAX_VALUE; }, - asName: function(value) { + asName: function (value) { return String(value).toLowerCase(); }, }; @@ -1147,7 +1172,7 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { id: 'torrentDetails', activeTab: 0, - initComponent: function() { + initComponent: function () { Deluge.details.DetailsPanel.superclass.initComponent.call(this); this.add(new Deluge.details.StatusTab()); this.add(new Deluge.details.DetailsTab()); @@ -1156,8 +1181,8 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { this.add(new Deluge.details.OptionsTab()); }, - clear: function() { - this.items.each(function(panel) { + clear: function () { + this.items.each(function (panel) { if (panel.clear) { panel.clear.defer(100, panel); panel.disable(); @@ -1165,14 +1190,14 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { }); }, - update: function(tab) { + update: function (tab) { var torrent = deluge.torrents.getSelected(); if (!torrent) { this.clear(); return; } - this.items.each(function(tab) { + this.items.each(function (tab) { if (tab.disabled) tab.enable(); }); @@ -1183,7 +1208,7 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { /* Event Handlers */ // We need to add the events in onRender since Deluge.Torrents has not been created yet. - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.DetailsPanel.superclass.onRender.call( this, ct, @@ -1195,18 +1220,18 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { deluge.torrents.getSelectionModel().on( 'selectionchange', - function(selModel) { + function (selModel) { if (!selModel.hasSelection()) this.clear(); }, this ); }, - onTabChange: function(panel, tab) { + onTabChange: function (panel, tab) { this.update(tab); }, - onTorrentsClick: function(grid, rowIndex, e) { + onTorrentsClick: function (grid, rowIndex, e) { this.update(); }, }); @@ -1230,7 +1255,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { oldData: {}, - initComponent: function() { + initComponent: function () { Deluge.details.DetailsTab.superclass.initComponent.call(this); this.addItem('torrent_name', _('Name:')); this.addItem('hash', _('Hash:')); @@ -1243,7 +1268,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { this.addItem('creator', _('Created By:')); }, - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.DetailsTab.superclass.onRender.call(this, ct, position); this.body.setStyle('padding', '10px'); this.dl = Ext.DomHelper.append(this.body, { tag: 'dl' }, true); @@ -1253,7 +1278,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { } }, - addItem: function(id, label) { + addItem: function (id, label) { if (!this.rendered) { this.queuedItems[id] = label; } else { @@ -1262,7 +1287,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { }, // private - doAddItem: function(id, label) { + doAddItem: function (id, label) { Ext.DomHelper.append(this.dl, { tag: 'dt', cls: id, html: label }); this.fields[id] = Ext.DomHelper.append( this.dl, @@ -1271,7 +1296,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { ); }, - clear: function() { + clear: function () { if (!this.fields) return; for (var k in this.fields) { this.fields[k].dom.innerHTML = ''; @@ -1279,7 +1304,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { this.oldData = {}; }, - update: function(torrentId) { + update: function (torrentId) { deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Details, { success: this.onRequestComplete, scope: this, @@ -1287,7 +1312,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { }); }, - onRequestComplete: function(torrent, request, response, options) { + onRequestComplete: function (torrent, request, response, options) { var data = { torrent_name: torrent.name, hash: options.options.torrentId, @@ -1303,7 +1328,9 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { for (var field in this.fields) { if (!Ext.isDefined(data[field])) continue; // This is a field we are not responsible for. if (data[field] == this.oldData[field]) continue; - this.fields[field].dom.innerHTML = Ext.escapeHTML(data[field]); + this.fields[field].dom.innerHTML = Ext.util.Format.htmlEncode( + data[field] + ); } this.oldData = data; }, @@ -1328,13 +1355,14 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { header: _('Filename'), width: 330, dataIndex: 'filename', + tpl: new Ext.XTemplate('{filename:htmlEncode}'), }, { header: _('Size'), width: 150, dataIndex: 'size', tpl: new Ext.XTemplate('{size:this.fsize}', { - fsize: function(v) { + fsize: function (v) { return fsize(v); }, }), @@ -1344,7 +1372,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { header: _('Progress'), width: 150, dataIndex: 'progress', - renderer: function(v) { + renderer: function (v) { var progress = v * 100; return Deluge.progressBar( progress, @@ -1364,11 +1392,11 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { '{priority:this.getName}' + '</div></tpl>', { - getClass: function(v) { + getClass: function (v) { return FILE_PRIORITY_CSS[v]; }, - getName: function(v) { + getName: function (v) { return _(FILE_PRIORITY[v]); }, } @@ -1378,15 +1406,15 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { selModel: new Ext.tree.MultiSelectionModel(), - initComponent: function() { + initComponent: function () { Deluge.details.FilesTab.superclass.initComponent.call(this); this.setRootNode(new Ext.tree.TreeNode({ text: _('Files') })); }, - clear: function() { + clear: function () { var root = this.getRootNode(); if (!root.hasChildNodes()) return; - root.cascade(function(node) { + root.cascade(function (node) { var parentNode = node.parentNode; if (!parentNode) return; if (!parentNode.ownerTree) return; @@ -1394,7 +1422,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { }); }, - createFileTree: function(files) { + createFileTree: function (files) { function walk(files, parentNode) { for (var file in files.contents) { var item = files.contents[file]; @@ -1433,7 +1461,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { root.firstChild.expand(); }, - update: function(torrentId) { + update: function (torrentId) { if (this.torrentId != torrentId) { this.clear(); this.torrentId = torrentId; @@ -1446,7 +1474,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { }); }, - updateFileTree: function(files) { + updateFileTree: function (files) { function walk(files, parentNode) { for (var file in files.contents) { var item = files.contents[file]; @@ -1463,7 +1491,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { walk(files, this.getRootNode()); }, - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.FilesTab.superclass.onRender.call(this, ct, position); deluge.menus.filePriorities.on('itemclick', this.onItemClick, this); this.on('contextmenu', this.onContextMenu, this); @@ -1472,7 +1500,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { }); }, - onContextMenu: function(node, e) { + onContextMenu: function (node, e) { e.stopEvent(); var selModel = this.getSelectionModel(); if (selModel.getSelectedNodes().length < 2) { @@ -1482,14 +1510,14 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { deluge.menus.filePriorities.showAt(e.getPoint()); }, - onItemClick: function(baseItem, e) { + onItemClick: function (baseItem, e) { switch (baseItem.id) { case 'expandAll': this.expandAll(); break; default: var indexes = {}; - var walk = function(node) { + var walk = function (node) { if (Ext.isEmpty(node.attributes.fileIndex)) return; indexes[node.attributes.fileIndex] = node.attributes.priority; @@ -1497,9 +1525,9 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { this.getRootNode().cascade(walk); var nodes = this.getSelectionModel().getSelectedNodes(); - Ext.each(nodes, function(node) { + Ext.each(nodes, function (node) { if (!node.isLeaf()) { - var setPriorities = function(node) { + var setPriorities = function (node) { if (Ext.isEmpty(node.attributes.fileIndex)) return; indexes[node.attributes.fileIndex] = baseItem.filePriority; @@ -1521,8 +1549,8 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { [this.torrentId], { file_priorities: priorities }, { - success: function() { - Ext.each(nodes, function(node) { + success: function () { + Ext.each(nodes, function (node) { node.setColumnValue(3, baseItem.filePriority); }); }, @@ -1533,7 +1561,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { } }, - onRequestComplete: function(files, options) { + onRequestComplete: function (files, options) { if (!this.getRootNode().hasChildNodes()) { this.createFileTree(files); } else { @@ -1552,7 +1580,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { */ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { autoScroll: true, @@ -1573,7 +1601,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { Deluge.details.OptionsTab.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.details.OptionsTab.superclass.initComponent.call(this); (this.fieldsets = {}), (this.fields = {}); @@ -1882,7 +1910,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { }); }, - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.OptionsTab.superclass.onRender.call(this, ct, position); // This is another hack I think, so keep an eye out here when upgrading. @@ -1891,17 +1919,17 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { this.doLayout(); }, - clear: function() { + clear: function () { if (this.torrentId == null) return; this.torrentId = null; this.optionsManager.changeId(null); }, - reset: function() { + reset: function () { if (this.torrentId) this.optionsManager.reset(); }, - update: function(torrentId) { + update: function (torrentId) { if (this.torrentId && !torrentId) this.clear(); // we want to clear the pane if we get a null torrent torrentIds if (!torrentId) return; // We do not care about null torrentIds. @@ -1916,33 +1944,33 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { }); }, - onApply: function() { + onApply: function () { var changed = this.optionsManager.getDirty(); deluge.client.core.set_torrent_options([this.torrentId], changed, { - success: function() { + success: function () { this.optionsManager.commit(); }, scope: this, }); }, - onEditTrackers: function() { + onEditTrackers: function () { deluge.editTrackers.show(); }, - onMoveCompletedChecked: function(checkbox, checked) { + onMoveCompletedChecked: function (checkbox, checked) { this.fields.move_completed_path.setDisabled(!checked); if (!checked) return; this.fields.move_completed_path.focus(); }, - onStopRatioChecked: function(checkbox, checked) { + onStopRatioChecked: function (checkbox, checked) { this.fields.remove_at_ratio.setDisabled(!checked); this.fields.stop_ratio.setDisabled(!checked); }, - onRequestComplete: function(torrent, options) { + onRequestComplete: function (torrent, options) { this.fields['private'].setValue(torrent['private']); this.fields['private'].setDisabled(true); delete torrent['private']; @@ -1968,13 +1996,13 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { * See LICENSE for more details. */ -(function() { +(function () { function flagRenderer(value) { if (!value.replace(' ', '').replace(' ', '')) { return ''; } return String.format( - '<img src="{0}flag/{1}" />', + '<img alt="{1}" title="{1}" src="{0}flag/{1}" />', deluge.config.base, value ); @@ -2000,7 +2028,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { // fast way to figure out if we have a peer already. peers: {}, - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { title: _('Peers'), @@ -2033,7 +2061,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { header: _('Client'), width: 125, sortable: true, - renderer: fplain, + renderer: 'htmlEncode', dataIndex: 'client', }, { @@ -2067,19 +2095,19 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { Deluge.details.PeersTab.superclass.constructor.call(this, config); }, - clear: function() { + clear: function () { this.getStore().removeAll(); this.peers = {}; }, - update: function(torrentId) { + update: function (torrentId) { deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Peers, { success: this.onRequestComplete, scope: this, }); }, - onRequestComplete: function(torrent, options) { + onRequestComplete: function (torrent, options) { if (!torrent) return; var store = this.getStore(); @@ -2089,7 +2117,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { // Go through the peers updating and creating peer records Ext.each( torrent.peers, - function(peer) { + function (peer) { if (this.peers[peer.ip]) { var record = store.getById(peer.ip); record.beginEdit(); @@ -2110,7 +2138,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { store.add(newPeers); // Remove any peers that should not be left in the store. - store.each(function(record) { + store.each(function (record) { if (!addresses[record.id]) { store.remove(record); delete this.peers[record.id]; @@ -2143,7 +2171,7 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { title: _('Status'), autoScroll: true, - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.StatusTab.superclass.onRender.call(this, ct, position); this.progressBar = this.add({ @@ -2159,7 +2187,7 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { width: 1000, listeners: { render: { - fn: function(panel) { + fn: function (panel) { panel.load({ url: deluge.config.base + 'render/tab_status.html', text: _('Loading') + '...', @@ -2174,14 +2202,14 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { }); }, - clear: function() { + clear: function () { this.progressBar.updateProgress(0, ' '); for (var k in this.fields) { this.fields[k].innerHTML = ''; } }, - update: function(torrentId) { + update: function (torrentId) { if (!this.fields) this.getFields(); deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Status, { success: this.onRequestComplete, @@ -2189,18 +2217,18 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { }); }, - onPanelUpdate: function(el, response) { + onPanelUpdate: function (el, response) { this.fields = {}; Ext.each( Ext.query('dd', this.status.body.dom), - function(field) { + function (field) { this.fields[field.className] = field; }, this ); }, - onRequestComplete: function(status) { + onRequestComplete: function (status) { seeds = status.total_seeds > -1 ? status.num_seeds + ' (' + status.total_seeds + ')' @@ -2295,7 +2323,7 @@ Ext.namespace('Deluge.preferences'); * @extends Ext.form.FormPanel */ Deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { border: false, @@ -2309,7 +2337,7 @@ Deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, { Deluge.preferences.Bandwidth.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.preferences.Bandwidth.superclass.initComponent.call(this); var om = deluge.preferences.getOptionsManager(); @@ -2398,8 +2426,7 @@ Deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, { border: false, title: '', defaultType: 'checkbox', - style: - 'padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;', + style: 'padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;', autoHeight: true, }); om.bind( @@ -2503,7 +2530,7 @@ Deluge.preferences.Cache = Ext.extend(Ext.form.FormPanel, { header: false, layout: 'form', - initComponent: function() { + initComponent: function () { Deluge.preferences.Cache.superclass.initComponent.call(this); var om = deluge.preferences.getOptionsManager(); @@ -2564,7 +2591,7 @@ Deluge.preferences.Daemon = Ext.extend(Ext.form.FormPanel, { header: false, layout: 'form', - initComponent: function() { + initComponent: function () { Deluge.preferences.Daemon.superclass.initComponent.call(this); var om = deluge.preferences.getOptionsManager(); @@ -2644,7 +2671,7 @@ Ext.namespace('Deluge.preferences'); * @extends Ext.form.FormPanel */ Deluge.preferences.Downloads = Ext.extend(Ext.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { border: false, @@ -2659,7 +2686,7 @@ Deluge.preferences.Downloads = Ext.extend(Ext.FormPanel, { Deluge.preferences.Downloads.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.preferences.Downloads.superclass.initComponent.call(this); var optMan = deluge.preferences.getOptionsManager(); @@ -2772,7 +2799,7 @@ Deluge.preferences.Encryption = Ext.extend(Ext.form.FormPanel, { title: _('Encryption'), header: false, - initComponent: function() { + initComponent: function () { Deluge.preferences.Encryption.superclass.initComponent.call(this); var optMan = deluge.preferences.getOptionsManager(); @@ -2879,7 +2906,7 @@ Deluge.preferences.InstallPluginWindow = Ext.extend(Ext.Window, { modal: true, plain: true, - initComponent: function() { + initComponent: function () { Deluge.preferences.InstallPluginWindow.superclass.initComponent.call( this ); @@ -2906,7 +2933,7 @@ Deluge.preferences.InstallPluginWindow = Ext.extend(Ext.Window, { }); }, - onInstall: function(field, e) { + onInstall: function (field, e) { this.form.getForm().submit({ url: deluge.config.base + 'upload', waitMsg: _('Uploading your plugin...'), @@ -2915,11 +2942,11 @@ Deluge.preferences.InstallPluginWindow = Ext.extend(Ext.Window, { }); }, - onUploadPlugin: function(info, obj, response, request) { + onUploadPlugin: function (info, obj, response, request) { this.fireEvent('pluginadded'); }, - onUploadSuccess: function(fp, upload) { + onUploadSuccess: function (fp, upload) { this.hide(); if (upload.result.success) { var filename = this.form.getForm().getFieldValues().file; @@ -2955,7 +2982,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { header: false, layout: 'form', - initComponent: function() { + initComponent: function () { Deluge.preferences.Interface.superclass.initComponent.call(this); var om = (this.optionsManager = new Deluge.OptionsManager()); @@ -3125,7 +3152,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { ); }, - onApply: function() { + onApply: function () { var changed = this.optionsManager.getDirty(); if (!Ext.isObjectEmpty(changed)) { deluge.client.web.set_config(changed, { @@ -3147,7 +3174,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { no: _('Close'), }, multiline: false, - fn: function(btnText) { + fn: function (btnText) { if (btnText === 'yes') location.reload(); }, icon: Ext.MessageBox.QUESTION, @@ -3159,21 +3186,21 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { } }, - onOk: function() { + onOk: function () { this.onApply(); }, - onGotConfig: function(config) { + onGotConfig: function (config) { this.optionsManager.set(config); }, - onGotLanguages: function(info, obj, response, request) { + onGotLanguages: function (info, obj, response, request) { info.unshift(['', _('System Default')]); this.language.store.loadData(info); this.language.setValue(this.optionsManager.get('language')); }, - onPasswordChange: function() { + onPasswordChange: function () { var newPassword = this.newPassword.getValue(); if (newPassword != this.confirmPassword.getValue()) { Ext.MessageBox.show({ @@ -3189,7 +3216,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { var oldPassword = this.oldPassword.getValue(); deluge.client.auth.change_password(oldPassword, newPassword, { - success: function(result) { + success: function (result) { if (!result) { Ext.MessageBox.show({ title: _('Password'), @@ -3218,11 +3245,11 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { }); }, - onSetConfig: function() { + onSetConfig: function () { this.optionsManager.commit(); }, - onPageShow: function() { + onPageShow: function () { deluge.client.web.get_config({ success: this.onGotConfig, scope: this, @@ -3233,7 +3260,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { }); }, - onSSLCheck: function(e, checked) { + onSSLCheck: function (e, checked) { this.pkeyField.setDisabled(!checked); this.certField.setDisabled(!checked); }, @@ -3251,7 +3278,7 @@ Ext.namespace('Deluge.preferences'); // custom Vtype for vtype:'IPAddress' Ext.apply(Ext.form.VTypes, { - IPAddress: function(v) { + IPAddress: function (v) { return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v); }, IPAddressText: 'Must be a numeric IP address', @@ -3268,7 +3295,7 @@ Deluge.preferences.Network = Ext.extend(Ext.form.FormPanel, { title: _('Network'), header: false, - initComponent: function() { + initComponent: function () { Deluge.preferences.Network.superclass.initComponent.call(this); var optMan = deluge.preferences.getOptionsManager(); @@ -3311,7 +3338,7 @@ Deluge.preferences.Network = Ext.extend(Ext.form.FormPanel, { height: 22, listeners: { check: { - fn: function(e, checked) { + fn: function (e, checked) { this.listenPort.setDisabled(checked); }, scope: this, @@ -3373,7 +3400,7 @@ Deluge.preferences.Network = Ext.extend(Ext.form.FormPanel, { height: 22, listeners: { check: { - fn: function(e, checked) { + fn: function (e, checked) { this.outgoingPorts.setDisabled(checked); }, scope: this, @@ -3511,7 +3538,7 @@ Ext.namespace('Deluge.preferences'); * @extends Ext.form.FormPanel */ Deluge.preferences.Other = Ext.extend(Ext.form.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { border: false, @@ -3524,7 +3551,7 @@ Deluge.preferences.Other = Ext.extend(Ext.form.FormPanel, { Deluge.preferences.Other.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.preferences.Other.superclass.initComponent.call(this); var optMan = deluge.preferences.getOptionsManager(); @@ -3637,7 +3664,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { '</dl>' ), - initComponent: function() { + initComponent: function () { Deluge.preferences.Plugins.superclass.initComponent.call(this); this.defaultValues = { version: '', @@ -3647,7 +3674,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { }; this.pluginTemplate.compile(); - var checkboxRenderer = function(v, p, record) { + var checkboxRenderer = function (v, p, record) { p.css += ' x-grid3-check-col-td'; return ( '<div class="x-grid3-check-col' + (v ? '-on' : '') + '"> </div>' @@ -3669,7 +3696,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { width: 0.2, sortable: true, tpl: new Ext.XTemplate('{enabled:this.getCheckbox}', { - getCheckbox: function(v) { + getCheckbox: function (v) { return ( '<div class="x-grid3-check-col' + (v ? '-on' : '') + @@ -3738,23 +3765,23 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { deluge.events.on('PluginEnabledEvent', this.onPluginEnabled, this); }, - disablePlugin: function(plugin) { + disablePlugin: function (plugin) { deluge.client.core.disable_plugin(plugin); }, - enablePlugin: function(plugin) { + enablePlugin: function (plugin) { deluge.client.core.enable_plugin(plugin); }, - setInfo: function(plugin) { + setInfo: function (plugin) { if (!this.pluginInfo.rendered) return; var values = plugin || this.defaultValues; this.pluginInfo.body.dom.innerHTML = this.pluginTemplate.apply(values); }, - updatePlugins: function() { - var onGotAvailablePlugins = function(plugins) { - this.availablePlugins = plugins.sort(function(a, b) { + updatePlugins: function () { + var onGotAvailablePlugins = function (plugins) { + this.availablePlugins = plugins.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); @@ -3764,7 +3791,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { }); }; - var onGotEnabledPlugins = function(plugins) { + var onGotEnabledPlugins = function (plugins) { this.enabledPlugins = plugins; this.onGotPlugins(); }; @@ -3775,11 +3802,11 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { }); }, - updatePluginsGrid: function() { + updatePluginsGrid: function () { var plugins = []; Ext.each( this.availablePlugins, - function(plugin) { + function (plugin) { if (this.enabledPlugins.indexOf(plugin) > -1) { plugins.push([true, plugin]); } else { @@ -3791,7 +3818,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { this.list.getStore().loadData(plugins); }, - onNodeClick: function(dv, index, node, e) { + onNodeClick: function (dv, index, node, e) { var el = new Ext.Element(e.target); if (el.getAttribute('rel') != 'chkbox') return; @@ -3806,16 +3833,16 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { } }, - onFindMorePlugins: function() { + onFindMorePlugins: function () { window.open('http://dev.deluge-torrent.org/wiki/Plugins'); }, - onGotPlugins: function() { + onGotPlugins: function () { this.setInfo(); this.updatePluginsGrid(); }, - onGotPluginInfo: function(info) { + onGotPluginInfo: function (info) { var values = { author: info['Author'], version: info['Version'], @@ -3827,7 +3854,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { delete info; }, - onInstallPluginWindow: function() { + onInstallPluginWindow: function () { if (!this.installWindow) { this.installWindow = new Deluge.preferences.InstallPluginWindow(); this.installWindow.on('pluginadded', this.onPluginInstall, this); @@ -3835,7 +3862,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { this.installWindow.show(); }, - onPluginEnabled: function(pluginName) { + onPluginEnabled: function (pluginName) { var index = this.list.getStore().find('plugin', pluginName); if (index == -1) return; var plugin = this.list.getStore().getAt(index); @@ -3843,7 +3870,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { plugin.commit(); }, - onPluginDisabled: function(pluginName) { + onPluginDisabled: function (pluginName) { var index = this.list.getStore().find('plugin', pluginName); if (index == -1) return; var plugin = this.list.getStore().getAt(index); @@ -3851,11 +3878,11 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { plugin.commit(); }, - onPluginInstall: function() { + onPluginInstall: function () { this.updatePlugins(); }, - onPluginSelect: function(dv, selections) { + onPluginSelect: function (dv, selections) { if (selections.length == 0) return; var r = dv.getRecords(selections)[0]; deluge.client.web.get_plugin_info(r.get('plugin'), { @@ -3864,11 +3891,11 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { }); }, - onPreferencesShow: function() { + onPreferencesShow: function () { this.updatePlugins(); }, - onPluginInfoRender: function(ct, position) { + onPluginInfoRender: function (ct, position) { this.setInfo(); }, }); @@ -3910,7 +3937,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { pages: {}, - initComponent: function() { + initComponent: function () { Deluge.preferences.PreferencesWindow.superclass.initComponent.call( this ); @@ -3920,7 +3947,6 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { columns: [ { id: 'name', - renderer: fplain, dataIndex: 'name', }, ], @@ -3968,7 +3994,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { this.initPages(); }, - initPages: function() { + initPages: function () { deluge.preferences = this; this.addPage(new Deluge.preferences.Downloads()); this.addPage(new Deluge.preferences.Network()); @@ -3983,7 +4009,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { this.addPage(new Deluge.preferences.Plugins()); }, - onApply: function(e) { + onApply: function (e) { var changed = this.optionsManager.getDirty(); if (!Ext.isObjectEmpty(changed)) { // Workaround for only displaying single listen port but still pass array to core. @@ -4008,7 +4034,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { * Return the options manager for the preferences window. * @returns {Deluge.OptionsManager} the options manager */ - getOptionsManager: function() { + getOptionsManager: function () { return this.optionsManager; }, @@ -4016,7 +4042,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { * Adds a page to the preferences window. * @param {Mixed} page */ - addPage: function(page) { + addPage: function (page) { var store = this.list.getStore(); var name = page.title; store.add([new PreferencesRecord({ name: name })]); @@ -4031,7 +4057,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { * Removes a preferences page from the window. * @param {mixed} name */ - removePage: function(page) { + removePage: function (page) { var name = page.title; var store = this.list.getStore(); store.removeAt(store.find('name', name)); @@ -4043,7 +4069,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { * Select which preferences page is displayed. * @param {String} page The page name to change to */ - selectPage: function(page) { + selectPage: function (page) { if (this.pages[page].index < 0) { this.pages[page].index = this.configPanel.items.indexOf( this.pages[page] @@ -4053,7 +4079,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { }, // private - doSelectPage: function(page) { + doSelectPage: function (page) { if (this.pages[page].index < 0) { this.pages[page].index = this.configPanel.items.indexOf( this.pages[page] @@ -4064,23 +4090,23 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { }, // private - onGotConfig: function(config) { + onGotConfig: function (config) { this.getOptionsManager().set(config); }, // private - onPageSelect: function(list, selections) { + onPageSelect: function (list, selections) { var r = list.getRecord(selections[0]); this.doSelectPage(r.get('name')); }, // private - onSetConfig: function() { + onSetConfig: function () { this.getOptionsManager().commit(); }, // private - onAfterRender: function() { + onAfterRender: function () { if (!this.list.getSelectionCount()) { this.list.select(0); } @@ -4088,7 +4114,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { }, // private - onShow: function() { + onShow: function () { if (!deluge.client.core) return; deluge.client.core.get_config({ success: this.onGotConfig, @@ -4097,12 +4123,12 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { }, // private - onClose: function() { + onClose: function () { this.hide(); }, // private - onOk: function() { + onOk: function () { var changed = this.optionsManager.getDirty(); if (!Ext.isObjectEmpty(changed)) { deluge.client.core.set_config(changed, { @@ -4138,7 +4164,7 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { autoHeight: true, labelWidth: 70, - initComponent: function() { + initComponent: function () { Deluge.preferences.ProxyField.superclass.initComponent.call(this); this.proxyType = this.add({ xtype: 'combo', @@ -4265,11 +4291,11 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { this.setting = false; }, - getName: function() { + getName: function () { return this.initialConfig.name; }, - getValue: function() { + getValue: function () { return { type: this.proxyType.getValue(), hostname: this.hostname.getValue(), @@ -4285,7 +4311,7 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { }, // Set the values of the proxies - setValue: function(value) { + setValue: function (value) { this.setting = true; this.proxyType.setValue(value['type']); var index = this.proxyType.getStore().find('id', value['type']); @@ -4305,7 +4331,7 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { this.setting = false; }, - onFieldChange: function(field, newValue, oldValue) { + onFieldChange: function (field, newValue, oldValue) { if (this.setting) return; var newValues = this.getValue(); var oldValues = Ext.apply({}, newValues); @@ -4314,7 +4340,7 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { this.fireEvent('change', this, newValues, oldValues); }, - onTypeSelect: function(combo, record, index) { + onTypeSelect: function (combo, record, index) { var typeId = record.get('id'); if (typeId > 0) { this.hostname.show(); @@ -4359,7 +4385,7 @@ Ext.namespace('Deluge.preferences'); * @extends Ext.form.FormPanel */ Deluge.preferences.Proxy = Ext.extend(Ext.form.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { border: false, @@ -4373,7 +4399,7 @@ Deluge.preferences.Proxy = Ext.extend(Ext.form.FormPanel, { Deluge.preferences.Proxy.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.preferences.Proxy.superclass.initComponent.call(this); this.proxy = this.add( new Deluge.preferences.ProxyField({ @@ -4385,19 +4411,19 @@ Deluge.preferences.Proxy = Ext.extend(Ext.form.FormPanel, { deluge.preferences.getOptionsManager().bind('proxy', this.proxy); }, - getValue: function() { + getValue: function () { return { proxy: this.proxy.getValue(), }; }, - setValue: function(value) { + setValue: function (value) { for (var proxy in value) { this[proxy].setValue(value[proxy]); } }, - onProxyChange: function(field, newValue, oldValue) { + onProxyChange: function (field, newValue, oldValue) { var newValues = this.getValue(); var oldValues = Ext.apply({}, newValues); oldValues[field.getName()] = oldValue; @@ -4426,7 +4452,7 @@ Deluge.preferences.Queue = Ext.extend(Ext.form.FormPanel, { header: false, layout: 'form', - initComponent: function() { + initComponent: function () { Deluge.preferences.Queue.superclass.initComponent.call(this); var om = deluge.preferences.getOptionsManager(); @@ -4634,7 +4660,7 @@ Deluge.preferences.Queue = Ext.extend(Ext.form.FormPanel, { om.bind('remove_seed_at_ratio', this.removeAtRatio); }, - onStopRatioCheck: function(e, checked) { + onStopRatioCheck: function (e, checked) { this.stopRatio.setDisabled(!checked); this.removeAtRatio.setDisabled(!checked); }, @@ -4656,13 +4682,13 @@ Ext.ns('Deluge'); * @extends Ext.menu.Menu */ Deluge.StatusbarMenu = Ext.extend(Ext.menu.Menu, { - initComponent: function() { + initComponent: function () { Deluge.StatusbarMenu.superclass.initComponent.call(this); this.otherWin = new Deluge.OtherLimitWindow( this.initialConfig.otherWin || {} ); - this.items.each(function(item) { + this.items.each(function (item) { if (item.getXType() != 'menucheckitem') return; if (item.value == 'other') { item.on('click', this.onOtherClicked, this); @@ -4672,14 +4698,14 @@ Deluge.StatusbarMenu = Ext.extend(Ext.menu.Menu, { }, this); }, - setValue: function(value) { + setValue: function (value) { var beenSet = false; // set the new value this.value = value = value == 0 ? -1 : value; var other = null; // uncheck all items - this.items.each(function(item) { + this.items.each(function (item) { if (item.setChecked) { item.suspendEvents(); if (item.value == value) { @@ -4701,18 +4727,18 @@ Deluge.StatusbarMenu = Ext.extend(Ext.menu.Menu, { other.resumeEvents(); }, - onLimitChanged: function(item, checked) { + onLimitChanged: function (item, checked) { if (!checked || item.value == 'other') return; // We do not care about unchecked or other. var config = {}; config[item.group] = item.value; deluge.client.core.set_config(config, { - success: function() { + success: function () { deluge.ui.update(); }, }); }, - onOtherClicked: function(item, e) { + onOtherClicked: function (item, e) { this.otherWin.group = item.group; this.otherWin.setValue(this.value); this.otherWin.show(); @@ -4738,7 +4764,7 @@ Ext.namespace('Deluge'); * @param {Object} config Configuration options */ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { - constructor: function(config) { + constructor: function (config) { config = config || {}; this.binds = {}; this.changed = {}; @@ -4776,7 +4802,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * Add a set of default options and values to the options manager * @param {Object} options The default options. */ - addOptions: function(options) { + addOptions: function (options) { this.options = Ext.applyIf(this.options, options); }, @@ -4785,7 +4811,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} option * @param {Ext.form.Field} field */ - bind: function(option, field) { + bind: function (option, field) { this.binds[option] = this.binds[option] || []; this.binds[option].push(field); field._doption = option; @@ -4801,7 +4827,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { /** * Changes all the changed values to be the default values */ - commit: function() { + commit: function () { this.options = Ext.apply(this.options, this.changed); this.reset(); }, @@ -4811,7 +4837,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {Mixed} oldValue The original value * @param {Mixed} value The new value to convert */ - convertValueType: function(oldValue, value) { + convertValueType: function (oldValue, value) { if (Ext.type(oldValue) != Ext.type(value)) { switch (Ext.type(oldValue)) { case 'string': @@ -4841,7 +4867,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} [option] A single option or an array of options to return. * @returns {Object} the options value. */ - get: function() { + get: function () { if (arguments.length == 1) { var option = arguments[0]; return this.isDirty(option) @@ -4851,7 +4877,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { var options = {}; Ext.each( arguments, - function(option) { + function (option) { if (!this.has(option)) return; options[option] = this.isDirty(option) ? this.changed[option] @@ -4868,7 +4894,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String|Array} [option] A single option or an array of options to return. * @returns {Object} the value of the option */ - getDefault: function(option) { + getDefault: function (option) { return this.options[option]; }, @@ -4876,7 +4902,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * Returns the dirty (changed) values. * @returns {Object} the changed options */ - getDirty: function() { + getDirty: function () { return this.changed; }, @@ -4884,7 +4910,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} [option] The option to check * @returns {Boolean} true if the option has been changed from the default. */ - isDirty: function(option) { + isDirty: function (option) { return !Ext.isEmpty(this.changed[option]); }, @@ -4893,14 +4919,14 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} option * @returns {Boolean} true if the option exists, else false. */ - has: function(option) { + has: function (option) { return this.options[option]; }, /** * Reset the options back to the default values. */ - reset: function() { + reset: function () { this.changed = {}; }, @@ -4909,7 +4935,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} option * @param {Object} value The value for the option */ - set: function(option, value) { + set: function (option, value) { if (option === undefined) { return; } else if (typeof option == 'object') { @@ -4929,7 +4955,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String/Object} option or options to update * @param {Object} [value]; */ - update: function(option, value) { + update: function (option, value) { if (option === undefined) { return; } else if (value === undefined) { @@ -4958,7 +4984,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * Lets the option manager know when a field is blurred so if a value * so value changing operations can continue on that field. */ - onFieldBlur: function(field, event) { + onFieldBlur: function (field, event) { if (this.focused == field) { this.focused = null; } @@ -4969,7 +4995,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {Ext.form.Field} field * @private */ - onFieldChange: function(field, event) { + onFieldChange: function (field, event) { if (field.field) field = field.field; // fix for spinners this.update(field._doption, field.getValue()); }, @@ -4978,16 +5004,16 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * Lets the option manager know when a field is focused so if a value changing * operation is performed it will not change the value of the field. */ - onFieldFocus: function(field, event) { + onFieldFocus: function (field, event) { this.focused = field; }, - onChange: function(option, newValue, oldValue) { + onChange: function (option, newValue, oldValue) { // If we don't have a bind there's nothing to do. if (Ext.isEmpty(this.binds[option])) return; Ext.each( this.binds[option], - function(bind) { + function (bind) { // The field is currently focused so we do not want to change it. if (bind == this.focused) return; // Set the form field to the new value. @@ -5027,16 +5053,16 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, { }, buttonAlign: 'center', - initComponent: function() { + initComponent: function () { Deluge.about.AboutWindow.superclass.initComponent.call(this); this.addEvents({ build_ready: true, }); var self = this; - var libtorrent = function() { + var libtorrent = function () { deluge.client.core.get_libtorrent_version({ - success: function(lt_version) { + success: function (lt_version) { comment += '<br/>' + _('libtorrent:') + ' ' + lt_version; Ext.getCmp('about_comment').setText(comment, false); self.fireEvent('build_ready'); @@ -5056,10 +5082,10 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, { client_version + '<br/>'; deluge.client.web.connected({ - success: function(connected) { + success: function (connected) { if (connected) { deluge.client.daemon.get_version({ - success: function(server_version) { + success: function (server_version) { comment += _('Server:') + ' ' + server_version + '<br/>'; libtorrent(); @@ -5069,7 +5095,7 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, { this.fireEvent('build_ready'); } }, - failure: function() { + failure: function () { this.fireEvent('build_ready'); }, scope: this, @@ -5103,27 +5129,26 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, { { xtype: 'label', style: 'padding-top: 5px; font-size: 12px;', - html: - '<a href="https://deluge-torrent.org" target="_blank">deluge-torrent.org</a>', + html: '<a href="https://deluge-torrent.org" target="_blank">deluge-torrent.org</a>', }, ]); this.addButton(_('Close'), this.onCloseClick, this); }, - show: function() { - this.on('build_ready', function() { + show: function () { + this.on('build_ready', function () { Deluge.about.AboutWindow.superclass.show.call(this); }); }, - onCloseClick: function() { + onCloseClick: function () { this.close(); }, }); Ext.namespace('Deluge'); -Deluge.About = function() { +Deluge.About = function () { new Deluge.about.AboutWindow().show(); }; /** @@ -5152,7 +5177,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { bodyStyle: 'padding: 10px 5px;', closeAction: 'hide', - initComponent: function() { + initComponent: function () { Deluge.AddConnectionWindow.superclass.initComponent.call(this); this.addEvents('hostadded'); @@ -5208,7 +5233,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { }); }, - onAddClick: function() { + onAddClick: function () { var values = this.form.getForm().getValues(); deluge.client.web.add_host( values.host, @@ -5216,7 +5241,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { values.username, values.password, { - success: function(result) { + success: function (result) { if (!result[0]) { Ext.MessageBox.show({ title: _('Error'), @@ -5239,7 +5264,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { ); }, - onHide: function() { + onHide: function () { this.form.getForm().reset(); }, }); @@ -5255,9 +5280,10 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { Ext.ns('Deluge'); // Custom VType validator for tracker urls -var trackerUrlTest = /(((^https?)|(^udp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i; +var trackerUrlTest = + /(((^https?)|(^udp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i; Ext.apply(Ext.form.VTypes, { - trackerUrl: function(val, field) { + trackerUrl: function (val, field) { return trackerUrlTest.test(val); }, trackerUrlText: 'Not a valid tracker url', @@ -5281,7 +5307,7 @@ Deluge.AddTrackerWindow = Ext.extend(Ext.Window, { closeAction: 'hide', iconCls: 'x-deluge-edit-trackers', - initComponent: function() { + initComponent: function () { Deluge.AddTrackerWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancelClick, this); @@ -5304,17 +5330,14 @@ Deluge.AddTrackerWindow = Ext.extend(Ext.Window, { }); }, - onAddClick: function() { - var trackers = this.form - .getForm() - .findField('trackers') - .getValue(); + onAddClick: function () { + var trackers = this.form.getForm().findField('trackers').getValue(); trackers = trackers.split('\n'); var cleaned = []; Ext.each( trackers, - function(tracker) { + function (tracker) { if (Ext.form.VTypes.trackerUrl(tracker)) { cleaned.push(tracker); } @@ -5323,17 +5346,11 @@ Deluge.AddTrackerWindow = Ext.extend(Ext.Window, { ); this.fireEvent('add', cleaned); this.hide(); - this.form - .getForm() - .findField('trackers') - .setValue(''); + this.form.getForm().findField('trackers').setValue(''); }, - onCancelClick: function() { - this.form - .getForm() - .findField('trackers') - .setValue(''); + onCancelClick: function () { + this.form.getForm().findField('trackers').setValue(''); this.hide(); }, }); @@ -5370,7 +5387,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { * Fires when the client has retrieved the list of methods from the server. * @param {Ext.ux.util.RpcClient} this */ - constructor: function(config) { + constructor: function (config) { Ext.ux.util.RpcClient.superclass.constructor.call(this, config); this._url = config.url || null; this._id = 0; @@ -5383,14 +5400,14 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { this.reloadMethods(); }, - reloadMethods: function() { + reloadMethods: function () { this._execute('system.listMethods', { success: this._setMethods, scope: this, }); }, - _execute: function(method, options) { + _execute: function (method, options) { options = options || {}; options.params = options.params || []; options.id = this._id; @@ -5413,7 +5430,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { }); }, - _onFailure: function(response, requestOptions) { + _onFailure: function (response, requestOptions) { var options = requestOptions.options; errorObj = { id: options.id, @@ -5439,7 +5456,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { } }, - _onSuccess: function(response, requestOptions) { + _onSuccess: function (response, requestOptions) { var responseObj = Ext.decode(response.responseText); var options = requestOptions.options; if (responseObj.error) { @@ -5477,9 +5494,9 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { } }, - _parseArgs: function(args) { + _parseArgs: function (args) { var params = []; - Ext.each(args, function(arg) { + Ext.each(args, function (arg) { params.push(arg); }); @@ -5488,7 +5505,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { var keys = Ext.keys(options), isOption = false; - Ext.each(this._optionKeys, function(key) { + Ext.each(this._optionKeys, function (key) { if (keys.indexOf(key) > -1) isOption = true; }); @@ -5504,15 +5521,15 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { return options; }, - _setMethods: function(methods) { + _setMethods: function (methods) { var components = {}, self = this; - Ext.each(methods, function(method) { + Ext.each(methods, function (method) { var parts = method.split('.'); var component = components[parts[0]] || {}; - var fn = function() { + var fn = function () { var options = self._parseArgs(arguments); return self._execute(method, options); }; @@ -5525,7 +5542,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { } Ext.each( this._components, - function(component) { + function (component) { if (!component in components) { delete this[component]; } @@ -5559,7 +5576,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { title: _('Connection Manager'), iconCls: 'x-deluge-connect-window-icon', - initComponent: function() { + initComponent: function () { Deluge.ConnectionManager.superclass.initComponent.call(this); this.on('hide', this.onHide, this); this.on('show', this.onShow, this); @@ -5671,9 +5688,9 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { * Check to see if the the web interface is currently connected * to a Deluge Daemon and show the Connection Manager if not. */ - checkConnected: function() { + checkConnected: function () { deluge.client.web.connected({ - success: function(connected) { + success: function (connected) { if (connected) { deluge.events.fire('connect'); } else { @@ -5684,7 +5701,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }); }, - disconnect: function(show) { + disconnect: function (show) { deluge.events.fire('disconnect'); if (show) { if (this.isVisible()) return; @@ -5692,15 +5709,15 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { } }, - loadHosts: function() { + loadHosts: function () { deluge.client.web.get_hosts({ success: this.onGetHosts, scope: this, }); }, - update: function() { - this.list.getStore().each(function(r) { + update: function () { + this.list.getStore().each(function (r) { deluge.client.web.get_host_status(r.id, { success: this.onGetHostStatus, scope: this, @@ -5713,7 +5730,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { * passed in records host state. * @param {Ext.data.Record} record The hosts record to update the UI for */ - updateButtons: function(record) { + updateButtons: function (record) { var button = this.buttons[1], status = record.get('status'); @@ -5747,7 +5764,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onAddClick: function(button, e) { + onAddClick: function (button, e) { if (!this.addWindow) { this.addWindow = new Deluge.AddConnectionWindow(); this.addWindow.on('hostadded', this.onHostChange, this); @@ -5756,7 +5773,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onEditClick: function(button, e) { + onEditClick: function (button, e) { var connection = this.list.getSelectedRecords()[0]; if (!connection) return; @@ -5768,24 +5785,24 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onHostChange: function() { + onHostChange: function () { this.loadHosts(); }, // private - onClose: function(e) { + onClose: function (e) { this.hide(); }, // private - onConnect: function(e) { + onConnect: function (e) { var selected = this.list.getSelectedRecords()[0]; if (!selected) return; var me = this; - var disconnect = function() { + var disconnect = function () { deluge.client.web.disconnect({ - success: function(result) { + success: function (result) { this.update(this); deluge.events.fire('disconnect'); }, @@ -5806,11 +5823,11 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { var id = selected.id; deluge.client.web.connect(id, { - success: function(methods) { + success: function (methods) { deluge.client.reloadMethods(); deluge.client.on( 'connected', - function(e) { + function (e) { deluge.events.fire('connect'); }, this, @@ -5823,11 +5840,11 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onGetHosts: function(hosts) { + onGetHosts: function (hosts) { this.list.getStore().loadData(hosts); Ext.each( hosts, - function(host) { + function (host) { deluge.client.web.get_host_status(host[0], { success: this.onGetHostStatus, scope: this, @@ -5838,7 +5855,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onGetHostStatus: function(host) { + onGetHostStatus: function (host) { var record = this.list.getStore().getById(host[0]); record.set('status', host[1]); record.set('version', host[2]); @@ -5849,19 +5866,19 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onHide: function() { + onHide: function () { if (this.running) window.clearInterval(this.running); }, // private - onLogin: function() { + onLogin: function () { if (deluge.config.first_login) { Ext.MessageBox.confirm( _('Change Default Password'), _( 'We recommend changing the default password.<br><br>Would you like to change it now?' ), - function(res) { + function (res) { this.checkConnected(); if (res == 'yes') { deluge.preferences.show(); @@ -5877,7 +5894,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onLogout: function() { + onLogout: function () { this.disconnect(); if (!this.hidden && this.rendered) { this.hide(); @@ -5885,12 +5902,12 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onRemoveClick: function(button) { + onRemoveClick: function (button) { var connection = this.list.getSelectedRecords()[0]; if (!connection) return; deluge.client.web.remove_host(connection.id, { - success: function(result) { + success: function (result) { if (!result) { Ext.MessageBox.show({ title: _('Error'), @@ -5909,7 +5926,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onSelectionChanged: function(list, selections) { + onSelectionChanged: function (list, selections) { if (selections[0]) { this.editHostButton.enable(); this.removeHostButton.enable(); @@ -5925,7 +5942,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { // FIXME: Find out why this is being fired twice // private - onShow: function() { + onShow: function () { if (!this.addHostButton) { var bbar = this.panel.getBottomToolbar(); this.addHostButton = bbar.items.get('cm-add'); @@ -5939,7 +5956,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onStopClick: function(button, e) { + onStopClick: function (button, e) { var connection = this.list.getSelectedRecords()[0]; if (!connection) return; @@ -5949,7 +5966,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { } else { // This means we need to stop the daemon deluge.client.web.stop_daemon(connection.id, { - success: function(result) { + success: function (result) { if (!result[0]) { Ext.MessageBox.show({ title: _('Error'), @@ -5965,6 +5982,79 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { } }, }); +/* + * Deluge.CopyMagnet.js + * + * Copyright (c) Damien Churchill 2009-2010 <damoxc@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to: + * The Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the OpenSSL + * library. + * You must obey the GNU General Public License in all respects for all of + * the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete + * this exception statement from your version. If you delete this exception + * statement from all source files in the program, then also delete it here. + */ +Deluge.CopyMagnet = Ext.extend(Ext.Window, { + title: _('Copy Magnet URI'), + width: 375, + closeAction: 'hide', + iconCls: 'icon-magnet-copy', + + initComponent: function () { + Deluge.CopyMagnet.superclass.initComponent.call(this); + form = this.add({ + xtype: 'form', + defaultType: 'textfield', + hideLabels: true, + }); + this.magnetURI = form.add({ + name: 'URI', + anchor: '100%', + }); + this.addButton(_('Close'), this.onClose, this); + this.addButton(_('Copy'), this.onCopy, this); + }, + show: function (a) { + Deluge.CopyMagnet.superclass.show.call(this); + var torrent = deluge.torrents.getSelected(); + deluge.client.core.get_magnet_uri(torrent.id, { + success: this.onRequestComplete, + scope: this, + }); + }, + onRequestComplete: function (uri) { + this.magnetURI.setValue(uri); + }, + onCopy: function () { + this.magnetURI.focus(); + this.magnetURI.el.dom.select(); + document.execCommand('copy'); + }, + onClose: function () { + this.hide(); + }, +}); + +deluge.copyMagnetWindow = new Deluge.CopyMagnet(); /** * Deluge.js * @@ -5992,21 +6082,14 @@ Ext.state.Manager.setProvider( // Add some additional functions to ext and setup some of the // configurable parameters Ext.apply(Ext, { - escapeHTML: function(text) { - text = String(text) - .replace('<', '<') - .replace('>', '>'); - return text.replace('&', '&'); - }, - - isObjectEmpty: function(obj) { + isObjectEmpty: function (obj) { for (var i in obj) { return false; } return true; }, - areObjectsEqual: function(obj1, obj2) { + areObjectsEqual: function (obj1, obj2) { var equal = true; if (!obj1 || !obj2) return false; for (var i in obj1) { @@ -6017,7 +6100,7 @@ Ext.apply(Ext, { return equal; }, - keys: function(obj) { + keys: function (obj) { var keys = []; for (var i in obj) if (obj.hasOwnProperty(i)) { @@ -6026,7 +6109,7 @@ Ext.apply(Ext, { return keys; }, - values: function(obj) { + values: function (obj) { var values = []; for (var i in obj) { if (obj.hasOwnProperty(i)) { @@ -6036,7 +6119,7 @@ Ext.apply(Ext, { return values; }, - splat: function(obj) { + splat: function (obj) { var type = Ext.type(obj); return type ? (type != 'array' ? [obj] : obj) : []; }, @@ -6073,7 +6156,7 @@ Ext.apply(Deluge, { * @param {String} text The text to display on the bar * @param {Number} modified Amount to subtract from the width allowing for fixes */ - progressBar: function(progress, width, text, modifier) { + progressBar: function (progress, width, text, modifier) { modifier = Ext.value(modifier, 10); var progressWidth = ((width / 100.0) * progress).toFixed(0); var barWidth = progressWidth - 1; @@ -6092,7 +6175,7 @@ Ext.apply(Deluge, { * Constructs a new instance of the specified plugin. * @param {String} name The plugin name to create */ - createPlugin: function(name) { + createPlugin: function (name) { return new Deluge.pluginStore[name](); }, @@ -6100,7 +6183,7 @@ Ext.apply(Deluge, { * Check to see if a plugin has been registered. * @param {String} name The plugin name to check */ - hasPlugin: function(name) { + hasPlugin: function (name) { return Deluge.pluginStore[name] ? true : false; }, @@ -6109,7 +6192,7 @@ Ext.apply(Deluge, { * @param {String} name The plugin name to register * @param {Plugin} plugin The plugin to register */ - registerPlugin: function(name, plugin) { + registerPlugin: function (name, plugin) { Deluge.pluginStore[name] = plugin; }, }); @@ -6177,7 +6260,7 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { bodyStyle: 'padding: 10px 5px;', closeAction: 'hide', - initComponent: function() { + initComponent: function () { Deluge.EditConnectionWindow.superclass.initComponent.call(this); this.addEvents('hostedited'); @@ -6233,17 +6316,11 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { }); }, - show: function(connection) { + show: function (connection) { Deluge.EditConnectionWindow.superclass.show.call(this); - this.form - .getForm() - .findField('host') - .setValue(connection.get('host')); - this.form - .getForm() - .findField('port') - .setValue(connection.get('port')); + this.form.getForm().findField('host').setValue(connection.get('host')); + this.form.getForm().findField('port').setValue(connection.get('port')); this.form .getForm() .findField('username') @@ -6251,7 +6328,7 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { this.host_id = connection.id; }, - onEditClick: function() { + onEditClick: function () { var values = this.form.getForm().getValues(); deluge.client.web.edit_host( this.host_id, @@ -6260,7 +6337,7 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { values.username, values.password, { - success: function(result) { + success: function (result) { if (!result) { console.log(result); Ext.MessageBox.show({ @@ -6281,7 +6358,7 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { ); }, - onHide: function() { + onHide: function () { this.form.getForm().reset(); }, }); @@ -6314,7 +6391,7 @@ Deluge.EditTrackerWindow = Ext.extend(Ext.Window, { closeAction: 'hide', iconCls: 'x-deluge-edit-trackers', - initComponent: function() { + initComponent: function () { Deluge.EditTrackerWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancelClick, this); @@ -6337,32 +6414,23 @@ Deluge.EditTrackerWindow = Ext.extend(Ext.Window, { }); }, - show: function(record) { + show: function (record) { Deluge.EditTrackerWindow.superclass.show.call(this); this.record = record; - this.form - .getForm() - .findField('tracker') - .setValue(record.data['url']); + this.form.getForm().findField('tracker').setValue(record.data['url']); }, - onCancelClick: function() { + onCancelClick: function () { this.hide(); }, - onHide: function() { - this.form - .getForm() - .findField('tracker') - .setValue(''); + onHide: function () { + this.form.getForm().findField('tracker').setValue(''); }, - onSaveClick: function() { - var url = this.form - .getForm() - .findField('tracker') - .getValue(); + onSaveClick: function () { + var url = this.form.getForm().findField('tracker').getValue(); this.record.set('url', url); this.record.commit(); this.hide(); @@ -6380,7 +6448,7 @@ Deluge.EditTrackerWindow = Ext.extend(Ext.Window, { Ext.ns('Deluge'); /** - * @class Deluge.EditTrackerWindow + * @class Deluge.EditTrackersWindow * @extends Ext.Window */ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { @@ -6398,7 +6466,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { closeAction: 'hide', iconCls: 'x-deluge-edit-trackers', - initComponent: function() { + initComponent: function () { Deluge.EditTrackersWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancelClick, this); @@ -6427,6 +6495,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { header: _('Tracker'), width: 0.9, dataIndex: 'url', + tpl: new Ext.XTemplate('{url:htmlEncode}'), }, ], columnSort: { @@ -6481,18 +6550,18 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { }); }, - onAddClick: function() { + onAddClick: function () { this.addWindow.show(); }, - onAddTrackers: function(trackers) { + onAddTrackers: function (trackers) { var store = this.list.getStore(); Ext.each( trackers, - function(tracker) { + function (tracker) { var duplicate = false, heightestTier = -1; - store.each(function(record) { + store.each(function (record) { if (record.get('tier') > heightestTier) { heightestTier = record.get('tier'); } @@ -6513,27 +6582,27 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { ); }, - onCancelClick: function() { + onCancelClick: function () { this.hide(); }, - onEditClick: function() { + onEditClick: function () { var selected = this.list.getSelectedRecords()[0]; if (!selected) return; this.editWindow.show(selected); }, - onHide: function() { + onHide: function () { this.list.getStore().removeAll(); }, - onListNodeDblClicked: function(list, index, node, e) { + onListNodeDblClicked: function (list, index, node, e) { this.editWindow.show(this.list.getRecord(node)); }, - onOkClick: function() { + onOkClick: function () { var trackers = []; - this.list.getStore().each(function(record) { + this.list.getStore().each(function (record) { trackers.push({ tier: record.get('tier'), url: record.get('url'), @@ -6548,34 +6617,28 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { this.hide(); }, - onRemoveClick: function() { + onRemoveClick: function () { // Remove from the grid var selected = this.list.getSelectedRecords()[0]; if (!selected) return; this.list.getStore().remove(selected); }, - onRequestComplete: function(status) { + onRequestComplete: function (status) { this.list.getStore().loadData(status); this.list.getStore().sort('tier', 'ASC'); }, - onSaveFail: function() {}, + onSaveFail: function () {}, - onSelect: function(list) { + onSelect: function (list) { if (list.getSelectionCount()) { - this.panel - .getBottomToolbar() - .items.get(4) - .enable(); + this.panel.getBottomToolbar().items.get(4).enable(); } }, - onShow: function() { - this.panel - .getBottomToolbar() - .items.get(4) - .disable(); + onShow: function () { + this.panel.getBottomToolbar().items.get(4).disable(); var r = deluge.torrents.getSelected(); this.torrentId = r.id; deluge.client.core.get_torrent_status(r.id, ['trackers'], { @@ -6584,7 +6647,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { }); }, - onDownClick: function() { + onDownClick: function () { var r = this.list.getSelectedRecords()[0]; if (!r) return; @@ -6595,7 +6658,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { this.list.select(r.store.indexOf(r)); }, - onUpClick: function() { + onUpClick: function () { var r = this.list.getSelectedRecords()[0]; if (!r) return; @@ -6624,7 +6687,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { * Class for holding global events that occur within the UI. */ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { - constructor: function() { + constructor: function () { this.toRegister = []; this.on('login', this.onLogin, this); Deluge.EventsManager.superclass.constructor.call(this); @@ -6633,7 +6696,7 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { /** * Append an event handler to this object. */ - addListener: function(eventName, fn, scope, o) { + addListener: function (eventName, fn, scope, o) { this.addEvents(eventName); if (/[A-Z]/.test(eventName.substring(0, 1))) { if (!deluge.client) { @@ -6651,7 +6714,7 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { ); }, - getEvents: function() { + getEvents: function () { deluge.client.web.get_events({ success: this.onGetEventsSuccess, failure: this.onGetEventsFailure, @@ -6662,8 +6725,8 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { /** * Starts the EventsManagerManager checking for events. */ - start: function() { - Ext.each(this.toRegister, function(eventName) { + start: function () { + Ext.each(this.toRegister, function (eventName) { deluge.client.web.register_event_listener(eventName); }); this.running = true; @@ -6674,21 +6737,21 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { /** * Stops the EventsManagerManager checking for events. */ - stop: function() { + stop: function () { this.running = false; }, // private - onLogin: function() { + onLogin: function () { this.start(); }, - onGetEventsSuccess: function(events) { + onGetEventsSuccess: function (events) { if (!this.running) return; if (events) { Ext.each( events, - function(event) { + function (event) { var name = event[0], args = event[1]; args.splice(0, 0, name); @@ -6701,7 +6764,7 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { }, // private - onGetEventsFailure: function(result, error) { + onGetEventsFailure: function (result, error) { // the request timed out or we had a communication failure if (!this.running) return; if (!error.isTimeout && this.errorCount++ >= 3) { @@ -6742,7 +6805,7 @@ Deluge.FileBrowser = Ext.extend(Ext.Window, { width: 500, height: 400, - initComponent: function() { + initComponent: function () { Deluge.FileBrowser.superclass.initComponent.call(this); this.add({ @@ -6790,7 +6853,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { show_zero: null, - initComponent: function() { + initComponent: function () { Deluge.FilterPanel.superclass.initComponent.call(this); this.filterType = this.initialConfig.filter; var title = ''; @@ -6806,7 +6869,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { (title = this.filterType.replace('_', ' ')), (parts = title.split(' ')), (title = ''); - Ext.each(parts, function(p) { + Ext.each(parts, function (p) { fl = p.substring(0, 1).toUpperCase(); title += fl + p.substring(1) + ' '; }); @@ -6845,7 +6908,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { * Return the currently selected filter state * @returns {String} the current filter state */ - getState: function() { + getState: function () { if (!this.list.getSelectionCount()) return; var state = this.list.getSelectedRecords()[0]; @@ -6857,7 +6920,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { /** * Return the current states in the filter */ - getStates: function() { + getStates: function () { return this.states; }, @@ -6865,18 +6928,18 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { * Return the Store for the ListView of the FilterPanel * @returns {Ext.data.Store} the ListView store */ - getStore: function() { + getStore: function () { return this.list.getStore(); }, /** * Update the states in the FilterPanel */ - updateStates: function(states) { + updateStates: function (states) { this.states = {}; Ext.each( states, - function(state) { + function (state) { this.states[state[0]] = state[1]; }, this @@ -6888,7 +6951,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { : this.show_zero; if (!show_zero) { var newStates = []; - Ext.each(states, function(state) { + Ext.each(states, function (state) { if (state[1] > 0 || state[0] == 'All') { newStates.push(state); } @@ -6900,7 +6963,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { var filters = {}; Ext.each( states, - function(s, i) { + function (s, i) { var record = store.getById(s[0]); if (!record) { record = new store.recordType({ @@ -6919,7 +6982,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { this ); - store.each(function(record) { + store.each(function (record) { if (filters[record.id]) return; store.remove(record); var selected = this.list.getSelectedRecords()[0]; @@ -6941,7 +7004,7 @@ Deluge.FilterPanel.templates = { tracker_host: '<div class="x-deluge-filter" style="background-image: url(' + deluge.config.base + - 'tracker/{filter});">{filter} ({count})</div>', + 'tracker/{filter});">{filter:htmlEncode} ({count})</div>', }; /** * Deluge.Formatters.js @@ -6960,7 +7023,23 @@ Deluge.FilterPanel.templates = { * @version 1.3 * @singleton */ -Deluge.Formatters = { +Deluge.Formatters = (function () { + var charToEntity = { + '&': '&', + '>': '>', + '<': '<', + '"': '"', + "'": ''', + }; + + var charToEntityRegex = new RegExp( + '(' + Object.keys(charToEntity).join('|') + ')', + 'g' + ); + var htmlEncodeReplaceFn = function (match, capture) { + return charToEntity[capture]; + }; + /** * Formats a date string in the date representation of the current locale, * based on the systems timezone. @@ -6969,154 +7048,162 @@ Deluge.Formatters = { * @return {String} a string in the date representation of the current locale * or "" if seconds < 0. */ - date: function(timestamp) { - function zeroPad(num, count) { - var numZeropad = num + ''; - while (numZeropad.length < count) { - numZeropad = '0' + numZeropad; + return (Formatters = { + date: function (timestamp) { + function zeroPad(num, count) { + var numZeropad = num + ''; + while (numZeropad.length < count) { + numZeropad = '0' + numZeropad; + } + return numZeropad; } - return numZeropad; - } - timestamp = timestamp * 1000; - var date = new Date(timestamp); - return String.format( - '{0}/{1}/{2} {3}:{4}:{5}', - zeroPad(date.getDate(), 2), - zeroPad(date.getMonth() + 1, 2), - date.getFullYear(), - zeroPad(date.getHours(), 2), - zeroPad(date.getMinutes(), 2), - zeroPad(date.getSeconds(), 2) - ); - }, + timestamp = timestamp * 1000; + var date = new Date(timestamp); + return String.format( + '{0}/{1}/{2} {3}:{4}:{5}', + zeroPad(date.getDate(), 2), + zeroPad(date.getMonth() + 1, 2), + date.getFullYear(), + zeroPad(date.getHours(), 2), + zeroPad(date.getMinutes(), 2), + zeroPad(date.getSeconds(), 2) + ); + }, - /** - * Formats the bytes value into a string with KiB, MiB or GiB units. - * - * @param {Number} bytes the filesize in bytes - * @param {Boolean} showZero pass in true to displays 0 values - * @return {String} formatted string with KiB, MiB or GiB units. - */ - size: function(bytes, showZero) { - if (!bytes && !showZero) return ''; - bytes = bytes / 1024.0; + /** + * Formats the bytes value into a string with KiB, MiB or GiB units. + * + * @param {Number} bytes the filesize in bytes + * @param {Boolean} showZero pass in true to displays 0 values + * @return {String} formatted string with KiB, MiB or GiB units. + */ + size: function (bytes, showZero) { + if (!bytes && !showZero) return ''; + bytes = bytes / 1024.0; - if (bytes < 1024) { - return bytes.toFixed(1) + ' KiB'; - } else { - bytes = bytes / 1024; - } + if (bytes < 1024) { + return bytes.toFixed(1) + ' KiB'; + } else { + bytes = bytes / 1024; + } - if (bytes < 1024) { - return bytes.toFixed(1) + ' MiB'; - } else { - bytes = bytes / 1024; - } + if (bytes < 1024) { + return bytes.toFixed(1) + ' MiB'; + } else { + bytes = bytes / 1024; + } - return bytes.toFixed(1) + ' GiB'; - }, + return bytes.toFixed(1) + ' GiB'; + }, - /** - * Formats the bytes value into a string with K, M or G units. - * - * @param {Number} bytes the filesize in bytes - * @param {Boolean} showZero pass in true to displays 0 values - * @return {String} formatted string with K, M or G units. - */ - sizeShort: function(bytes, showZero) { - if (!bytes && !showZero) return ''; - bytes = bytes / 1024.0; + /** + * Formats the bytes value into a string with K, M or G units. + * + * @param {Number} bytes the filesize in bytes + * @param {Boolean} showZero pass in true to displays 0 values + * @return {String} formatted string with K, M or G units. + */ + sizeShort: function (bytes, showZero) { + if (!bytes && !showZero) return ''; + bytes = bytes / 1024.0; - if (bytes < 1024) { - return bytes.toFixed(1) + ' K'; - } else { - bytes = bytes / 1024; - } + if (bytes < 1024) { + return bytes.toFixed(1) + ' K'; + } else { + bytes = bytes / 1024; + } - if (bytes < 1024) { - return bytes.toFixed(1) + ' M'; - } else { - bytes = bytes / 1024; - } + if (bytes < 1024) { + return bytes.toFixed(1) + ' M'; + } else { + bytes = bytes / 1024; + } - return bytes.toFixed(1) + ' G'; - }, + return bytes.toFixed(1) + ' G'; + }, - /** - * Formats a string to display a transfer speed utilizing {@link #size} - * - * @param {Number} bytes the number of bytes per second - * @param {Boolean} showZero pass in true to displays 0 values - * @return {String} formatted string with KiB, MiB or GiB units. - */ - speed: function(bytes, showZero) { - return !bytes && !showZero ? '' : fsize(bytes, showZero) + '/s'; - }, + /** + * Formats a string to display a transfer speed utilizing {@link #size} + * + * @param {Number} bytes the number of bytes per second + * @param {Boolean} showZero pass in true to displays 0 values + * @return {String} formatted string with KiB, MiB or GiB units. + */ + speed: function (bytes, showZero) { + return !bytes && !showZero ? '' : fsize(bytes, showZero) + '/s'; + }, - /** - * Formats a string to show time in a human readable form. - * - * @param {Number} time the number of seconds - * @return {String} a formatted time string. will return '' if seconds == 0 - */ - timeRemaining: function(time) { - if (time <= 0) { - return '∞'; - } - time = time.toFixed(0); - if (time < 60) { - return time + 's'; - } else { - time = time / 60; - } + /** + * Formats a string to show time in a human readable form. + * + * @param {Number} time the number of seconds + * @return {String} a formatted time string. will return '' if seconds == 0 + */ + timeRemaining: function (time) { + if (time <= 0) { + return '∞'; + } + time = time.toFixed(0); + if (time < 60) { + return time + 's'; + } else { + time = time / 60; + } - if (time < 60) { - var minutes = Math.floor(time); - var seconds = Math.round(60 * (time - minutes)); - if (seconds > 0) { - return minutes + 'm ' + seconds + 's'; + if (time < 60) { + var minutes = Math.floor(time); + var seconds = Math.round(60 * (time - minutes)); + if (seconds > 0) { + return minutes + 'm ' + seconds + 's'; + } else { + return minutes + 'm'; + } } else { - return minutes + 'm'; + time = time / 60; } - } else { - time = time / 60; - } - if (time < 24) { - var hours = Math.floor(time); - var minutes = Math.round(60 * (time - hours)); - if (minutes > 0) { - return hours + 'h ' + minutes + 'm'; + if (time < 24) { + var hours = Math.floor(time); + var minutes = Math.round(60 * (time - hours)); + if (minutes > 0) { + return hours + 'h ' + minutes + 'm'; + } else { + return hours + 'h'; + } } else { - return hours + 'h'; + time = time / 24; } - } else { - time = time / 24; - } - var days = Math.floor(time); - var hours = Math.round(24 * (time - days)); - if (hours > 0) { - return days + 'd ' + hours + 'h'; - } else { - return days + 'd'; - } - }, + var days = Math.floor(time); + var hours = Math.round(24 * (time - days)); + if (hours > 0) { + return days + 'd ' + hours + 'h'; + } else { + return days + 'd'; + } + }, - /** - * Simply returns the value untouched, for when no formatting is required. - * - * @param {Mixed} value the value to be displayed - * @return the untouched value. - */ - plain: function(value) { - return value; - }, + /** + * Simply returns the value untouched, for when no formatting is required. + * + * @param {Mixed} value the value to be displayed + * @return the untouched value. + */ + plain: function (value) { + return value; + }, - cssClassEscape: function(value) { - return value.toLowerCase().replace('.', '_'); - }, -}; + cssClassEscape: function (value) { + return value.toLowerCase().replace('.', '_'); + }, + + htmlEncode: function (value) { + return !value + ? value + : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn); + }, + }); +})(); var fsize = Deluge.Formatters.size; var fsize_short = Deluge.Formatters.sizeShort; var fspeed = Deluge.Formatters.speed; @@ -7124,6 +7211,7 @@ var ftime = Deluge.Formatters.timeRemaining; var fdate = Deluge.Formatters.date; var fplain = Deluge.Formatters.plain; Ext.util.Format.cssClassEscape = Deluge.Formatters.cssClassEscape; +Ext.util.Format.htmlEncode = Deluge.Formatters.htmlEncode; /** * Deluge.Keys.js * @@ -7259,7 +7347,7 @@ Deluge.Keys = { // Merge the grid and status keys together as the status keys contain all the // grid ones. -Ext.each(Deluge.Keys.Grid, function(key) { +Ext.each(Deluge.Keys.Grid, function (key) { Deluge.Keys.Status.push(key); }); /** @@ -7287,7 +7375,7 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { width: 300, height: 120, - initComponent: function() { + initComponent: function () { Deluge.LoginWindow.superclass.initComponent.call(this); this.on('show', this.onShow, this); @@ -7320,17 +7408,17 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { this.passwordField.on('specialkey', this.onSpecialKey, this); }, - logout: function() { + logout: function () { deluge.events.fire('logout'); deluge.client.auth.delete_session({ - success: function(result) { + success: function (result) { this.show(true); }, scope: this, }); }, - show: function(skipCheck) { + show: function (skipCheck) { if (this.firstShow) { deluge.client.on('error', this.onClientError, this); this.firstShow = false; @@ -7341,28 +7429,28 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { } deluge.client.auth.check_session({ - success: function(result) { + success: function (result) { if (result) { deluge.events.fire('login'); } else { this.show(true); } }, - failure: function(result) { + failure: function (result) { this.show(true); }, scope: this, }); }, - onSpecialKey: function(field, e) { + onSpecialKey: function (field, e) { if (e.getKey() == 13) this.onLogin(); }, - onLogin: function() { + onLogin: function () { var passwordField = this.passwordField; deluge.client.auth.login(passwordField.getValue(), { - success: function(result) { + success: function (result) { if (result) { deluge.events.fire('login'); this.hide(); @@ -7373,7 +7461,7 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { msg: _('You entered an incorrect password'), buttons: Ext.MessageBox.OK, modal: false, - fn: function() { + fn: function () { passwordField.focus(true, 10); }, icon: Ext.MessageBox.WARNING, @@ -7385,14 +7473,14 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { }); }, - onClientError: function(errorObj, response, requestOptions) { + onClientError: function (errorObj, response, requestOptions) { if (errorObj.error.code == 1) { deluge.events.fire('logout'); this.show(true); } }, - onShow: function() { + onShow: function () { this.passwordField.focus(true, 300); }, }); @@ -7407,7 +7495,7 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { */ deluge.menus = { - onTorrentActionSetOpt: function(item, e) { + onTorrentActionSetOpt: function (item, e) { var ids = deluge.torrents.getSelectedIds(); var action = item.initialConfig.torrentAction; var opts = {}; @@ -7415,20 +7503,23 @@ deluge.menus = { deluge.client.core.set_torrent_options(ids, opts); }, - onTorrentActionMethod: function(item, e) { + onTorrentActionMethod: function (item, e) { var ids = deluge.torrents.getSelectedIds(); var action = item.initialConfig.torrentAction; deluge.client.core[action](ids, { - success: function() { + success: function () { deluge.ui.update(); }, }); }, - onTorrentActionShow: function(item, e) { + onTorrentActionShow: function (item, e) { var ids = deluge.torrents.getSelectedIds(); var action = item.initialConfig.torrentAction; switch (action) { + case 'copy_magnet': + deluge.copyMagnetWindow.show(); + break; case 'edit_trackers': deluge.editTrackers.show(); break; @@ -7710,6 +7801,13 @@ deluge.menus.torrent = new Ext.menu.Menu({ }, '-', { + torrentAction: 'copy_magnet', + text: _('Copy Magnet URI'), + iconCls: 'icon-magnet-copy', + handler: deluge.menus.onTorrentActionShow, + scope: deluge.menus, + }, + { torrentAction: 'force_reannounce', text: _('Update Tracker'), iconCls: 'icon-update-tracker', @@ -7796,7 +7894,7 @@ deluge.menus.filePriorities = new Ext.menu.Menu({ Ext.namespace('Deluge'); Deluge.MoveStorage = Ext.extend(Ext.Window, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { title: _('Move Download Folder'), @@ -7816,7 +7914,7 @@ Deluge.MoveStorage = Ext.extend(Ext.Window, { Deluge.MoveStorage.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.MoveStorage.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancel, this); @@ -7848,21 +7946,21 @@ Deluge.MoveStorage = Ext.extend(Ext.Window, { //}); }, - hide: function() { + hide: function () { Deluge.MoveStorage.superclass.hide.call(this); this.torrentIds = null; }, - show: function(torrentIds) { + show: function (torrentIds) { Deluge.MoveStorage.superclass.show.call(this); this.torrentIds = torrentIds; }, - onCancel: function() { + onCancel: function () { this.hide(); }, - onMove: function() { + onMove: function () { var dest = this.moveLocation.getValue(); deluge.client.core.move_storage(this.torrentIds, dest); this.hide(); @@ -7886,7 +7984,7 @@ deluge.moveStorage = new Deluge.MoveStorage(); * @extends Deluge.OptionsManager */ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { - constructor: function(config) { + constructor: function (config) { this.currentId = null; this.stored = {}; Deluge.MultiOptionsManager.superclass.constructor.call(this, config); @@ -7896,7 +7994,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * Changes bound fields to use the specified id. * @param {String} id */ - changeId: function(id, dontUpdateBinds) { + changeId: function (id, dontUpdateBinds) { var oldId = this.currentId; this.currentId = id; if (!dontUpdateBinds) { @@ -7904,7 +8002,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { if (!this.binds[option]) continue; Ext.each( this.binds[option], - function(bind) { + function (bind) { bind.setValue(this.get(option)); }, this @@ -7918,7 +8016,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * Changes all the changed values to be the default values * @param {String} id */ - commit: function() { + commit: function () { this.stored[this.currentId] = Ext.apply( this.stored[this.currentId], this.changed[this.currentId] @@ -7931,7 +8029,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String/Array} option A single option or an array of options to return. * @returns {Object} the options value. */ - get: function() { + get: function () { if (arguments.length == 1) { var option = arguments[0]; return this.isDirty(option) @@ -7949,7 +8047,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { var options = {}; Ext.each( arguments, - function(option) { + function (option) { options[option] = this.isDirty(option) ? this.changed[this.currentId][option] : this.getDefault(option); @@ -7965,7 +8063,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String} option A single option. * @returns {Object} the value of the option */ - getDefault: function(option) { + getDefault: function (option) { return this.has(option) ? this.stored[this.currentId][option] : this.options[option]; @@ -7975,7 +8073,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * Returns the dirty (changed) values. * @returns {Object} the changed options */ - getDirty: function() { + getDirty: function () { return this.changed[this.currentId] ? this.changed[this.currentId] : {}; }, @@ -7984,7 +8082,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String} option * @returns {Boolean} true if the option has been changed, else false. */ - isDirty: function(option) { + isDirty: function (option) { return ( this.changed[this.currentId] && !Ext.isEmpty(this.changed[this.currentId][option]) @@ -7997,7 +8095,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String} option * @returns {Boolean} true if the id has an option, else false. */ - has: function(option) { + has: function (option) { return ( this.stored[this.currentId] && !Ext.isEmpty(this.stored[this.currentId][option]) @@ -8007,7 +8105,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { /** * Reset the options back to the default values for the specified id. */ - reset: function() { + reset: function () { if (this.changed[this.currentId]) delete this.changed[this.currentId]; if (this.stored[this.currentId]) delete this.stored[this.currentId]; }, @@ -8015,7 +8113,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { /** * Reset the options back to their defaults for all ids. */ - resetAll: function() { + resetAll: function () { this.changed = {}; this.stored = {}; this.changeId(null); @@ -8027,7 +8125,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String} option * @param {Object} value The value for the option */ - setDefault: function(option, value) { + setDefault: function (option, value) { if (option === undefined) { return; } else if (value === undefined) { @@ -8058,7 +8156,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String/Object} option or options to update * @param {Object} [value]; */ - update: function(option, value) { + update: function (option, value) { if (option === undefined) { return; } else if (value === undefined) { @@ -8109,7 +8207,7 @@ Deluge.OtherLimitWindow = Ext.extend(Ext.Window, { constrainHeader: true, closeAction: 'hide', - initComponent: function() { + initComponent: function () { Deluge.OtherLimitWindow.superclass.initComponent.call(this); this.form = this.add({ xtype: 'form', @@ -8142,31 +8240,28 @@ Deluge.OtherLimitWindow = Ext.extend(Ext.Window, { this.afterMethod('show', this.doFocusField, this); }, - setValue: function(value) { + setValue: function (value) { this.form.getForm().setValues({ limit: value }); }, - onCancelClick: function() { + onCancelClick: function () { this.form.getForm().reset(); this.hide(); }, - onOkClick: function() { + onOkClick: function () { var config = {}; config[this.group] = this.form.getForm().getValues().limit; deluge.client.core.set_config(config, { - success: function() { + success: function () { deluge.ui.update(); }, }); this.hide(); }, - doFocusField: function() { - this.form - .getForm() - .findField('limit') - .focus(true, 10); + doFocusField: function () { + this.form.getForm().findField('limit').focus(true, 10); }, }); /** @@ -8192,7 +8287,7 @@ Deluge.Plugin = Ext.extend(Ext.util.Observable, { */ name: null, - constructor: function(config) { + constructor: function (config) { this.isDelugePlugin = true; this.addEvents({ /** @@ -8214,7 +8309,7 @@ Deluge.Plugin = Ext.extend(Ext.util.Observable, { * Disables the plugin, firing the "{@link #disabled}" event and * then executing the plugins clean up method onDisabled. */ - disable: function() { + disable: function () { this.fireEvent('disabled', this); if (this.onDisable) this.onDisable(); }, @@ -8223,13 +8318,13 @@ Deluge.Plugin = Ext.extend(Ext.util.Observable, { * Enables the plugin, firing the "{@link #enabled}" event and * then executes the plugins setup method, onEnabled. */ - enable: function() { + enable: function () { deluge.client.reloadMethods(); this.fireEvent('enable', this); if (this.onEnable) this.onEnable(); }, - registerTorrentStatus: function(key, header, options) { + registerTorrentStatus: function (key, header, options) { options = options || {}; var cc = options.colCfg || {}, sc = options.storeCfg || {}; @@ -8250,23 +8345,23 @@ Deluge.Plugin = Ext.extend(Ext.util.Observable, { deluge.torrents.getView().refresh(true); }, - deregisterTorrentStatus: function(key) { + deregisterTorrentStatus: function (key) { var fields = []; - Ext.each(deluge.torrents.meta.fields, function(field) { + Ext.each(deluge.torrents.meta.fields, function (field) { if (field.name != key) fields.push(field); }); deluge.torrents.meta.fields = fields; deluge.torrents.getStore().reader.onMetaChange(deluge.torrents.meta); var cols = []; - Ext.each(deluge.torrents.columns, function(col) { + Ext.each(deluge.torrents.columns, function (col) { if (col.dataIndex != key) cols.push(col); }); deluge.torrents.colModel.setConfig(cols); deluge.torrents.columns = cols; var keys = []; - Ext.each(Deluge.Keys.Grid, function(k) { + Ext.each(Deluge.Keys.Grid, function (k) { if (k == key) keys.push(k); }); Deluge.Keys.Grid = keys; @@ -8304,16 +8399,16 @@ Deluge.RemoveWindow = Ext.extend(Ext.Window, { bodyStyle: 'padding: 5px; padding-left: 10px;', html: 'Are you sure you wish to remove the torrent (s)?', - initComponent: function() { + initComponent: function () { Deluge.RemoveWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancel, this); this.addButton(_('Remove With Data'), this.onRemoveData, this); this.addButton(_('Remove Torrent'), this.onRemove, this); }, - remove: function(removeData) { + remove: function (removeData) { deluge.client.core.remove_torrents(this.torrentIds, removeData, { - success: function(result) { + success: function (result) { if (result == true) { console.log( 'Error(s) occured when trying to delete torrent(s).' @@ -8326,25 +8421,25 @@ Deluge.RemoveWindow = Ext.extend(Ext.Window, { }); }, - show: function(ids) { + show: function (ids) { Deluge.RemoveWindow.superclass.show.call(this); this.torrentIds = ids; }, - onCancel: function() { + onCancel: function () { this.hide(); this.torrentIds = null; }, - onRemove: function() { + onRemove: function () { this.remove(false); }, - onRemoveData: function() { + onRemoveData: function () { this.remove(true); }, - onRemoved: function(torrentIds) { + onRemoved: function (torrentIds) { deluge.events.fire('torrentsRemoved', torrentIds); this.hide(); deluge.ui.update(); @@ -8378,7 +8473,7 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { // private selected: null, - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { id: 'sidebar', @@ -8397,16 +8492,16 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { }, // private - initComponent: function() { + initComponent: function () { Deluge.Sidebar.superclass.initComponent.call(this); deluge.events.on('disconnect', this.onDisconnect, this); }, - createFilter: function(filter, states) { + createFilter: function (filter, states) { var panel = new Deluge.FilterPanel({ filter: filter, }); - panel.on('selectionchange', function(view, nodes) { + panel.on('selectionchange', function (view, nodes) { deluge.ui.update(); }); this.add(panel); @@ -8414,7 +8509,7 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { this.doLayout(); this.panels[filter] = panel; - panel.header.on('click', function(header) { + panel.header.on('click', function (header) { if (!deluge.config.sidebar_multiple_filters) { deluge.ui.update(); } @@ -8428,16 +8523,16 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { this.fireEvent('afterfiltercreate', this, panel); }, - getFilter: function(filter) { + getFilter: function (filter) { return this.panels[filter]; }, - getFilterStates: function() { + getFilterStates: function () { var states = {}; if (deluge.config.sidebar_multiple_filters) { // Grab the filters from each of the filter panels - this.items.each(function(panel) { + this.items.each(function (panel) { var state = panel.getState(); if (state == null) return; states[panel.filterType] = state; @@ -8454,12 +8549,12 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { return states; }, - hasFilter: function(filter) { + hasFilter: function (filter) { return this.panels[filter] ? true : false; }, // private - onDisconnect: function() { + onDisconnect: function () { for (var filter in this.panels) { this.remove(this.panels[filter]); } @@ -8467,11 +8562,11 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { this.selected = null; }, - onFilterSelect: function(selModel, rowIndex, record) { + onFilterSelect: function (selModel, rowIndex, record) { deluge.ui.update(); }, - update: function(filters) { + update: function (filters) { for (var filter in filters) { var states = filters[filter]; if (Ext.getKeys(this.panels).indexOf(filter) > -1) { @@ -8484,7 +8579,7 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { // Perform a cleanup of fitlers that are not enabled any more. Ext.each( Ext.keys(this.panels), - function(filter) { + function (filter) { if (Ext.keys(filters).indexOf(filter) == -1) { // We need to remove the panel this.remove(this.panels[filter]); @@ -8508,7 +8603,7 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { Ext.namespace('Deluge'); Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { id: 'deluge-statusbar', @@ -8520,14 +8615,14 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { Deluge.Statusbar.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.Statusbar.superclass.initComponent.call(this); deluge.events.on('connect', this.onConnect, this); deluge.events.on('disconnect', this.onDisconnect, this); }, - createButtons: function() { + createButtons: function () { this.buttons = this.add( { id: 'statusbar-connections', @@ -8711,7 +8806,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { cls: 'x-btn-text-icon', iconCls: 'x-deluge-traffic', tooltip: _('Protocol Traffic Download/Upload'), - handler: function() { + handler: function () { deluge.preferences.show(); deluge.preferences.selectPage('Network'); }, @@ -8738,7 +8833,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { cls: 'x-btn-text-icon', iconCls: 'x-deluge-freespace', tooltip: _('Freespace in download folder'), - handler: function() { + handler: function () { deluge.preferences.show(); deluge.preferences.selectPage('Downloads'); }, @@ -8747,7 +8842,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { this.created = true; }, - onConnect: function() { + onConnect: function () { this.setStatus({ iconCls: 'x-connected', text: '', @@ -8755,7 +8850,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { if (!this.created) { this.createButtons(); } else { - Ext.each(this.buttons, function(item) { + Ext.each(this.buttons, function (item) { item.show(); item.enable(); }); @@ -8763,23 +8858,23 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { this.doLayout(); }, - onDisconnect: function() { + onDisconnect: function () { this.clearStatus({ useDefaults: true }); - Ext.each(this.buttons, function(item) { + Ext.each(this.buttons, function (item) { item.hide(); item.disable(); }); this.doLayout(); }, - update: function(stats) { + update: function (stats) { if (!stats) return; function addSpeed(val) { return val + ' KiB/s'; } - var updateStat = function(name, config) { + var updateStat = function (name, config) { var item = this.items.get('statusbar-' + name); if (config.limit.value > 0) { var value = config.value.formatter @@ -8874,7 +8969,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { * @extends Ext.Toolbar */ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { items: [ @@ -8978,62 +9073,62 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { connectedButtons: ['add', 'remove', 'pause', 'resume', 'up', 'down'], - initComponent: function() { + initComponent: function () { Deluge.Toolbar.superclass.initComponent.call(this); deluge.events.on('connect', this.onConnect, this); deluge.events.on('login', this.onLogin, this); }, - onConnect: function() { + onConnect: function () { Ext.each( this.connectedButtons, - function(buttonId) { + function (buttonId) { this.items.get(buttonId).enable(); }, this ); }, - onDisconnect: function() { + onDisconnect: function () { Ext.each( this.connectedButtons, - function(buttonId) { + function (buttonId) { this.items.get(buttonId).disable(); }, this ); }, - onLogin: function() { + onLogin: function () { this.items.get('logout').enable(); }, - onLogout: function() { + onLogout: function () { this.items.get('logout').disable(); deluge.login.logout(); }, - onConnectionManagerClick: function() { + onConnectionManagerClick: function () { deluge.connectionManager.show(); }, - onHelpClick: function() { + onHelpClick: function () { window.open('http://dev.deluge-torrent.org/wiki/UserGuide'); }, - onAboutClick: function() { + onAboutClick: function () { var about = new Deluge.about.AboutWindow(); about.show(); }, - onPreferencesClick: function() { + onPreferencesClick: function () { deluge.preferences.show(); }, - onTorrentAction: function(item) { + onTorrentAction: function (item) { var selection = deluge.torrents.getSelections(); var ids = []; - Ext.each(selection, function(record) { + Ext.each(selection, function (record) { ids.push(record.id); }); @@ -9044,7 +9139,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { case 'pause': case 'resume': deluge.client.core[item.id + '_torrent'](ids, { - success: function() { + success: function () { deluge.ui.update(); }, }); @@ -9052,7 +9147,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { case 'up': case 'down': deluge.client.core['queue_' + item.id](ids, { - success: function() { + success: function () { deluge.ui.update(); }, }); @@ -9060,7 +9155,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { } }, - onTorrentAdd: function() { + onTorrentAdd: function () { deluge.add.show(); }, }); @@ -9074,7 +9169,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { * See LICENSE for more details. */ -(function() { +(function () { /* Renderers for the Torrent Grid */ function queueRenderer(value) { return value == -1 ? '' : value + 1; @@ -9083,7 +9178,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { return String.format( '<div class="torrent-name x-deluge-{0}">{1}</div>', r.data['state'].toLowerCase(), - value + Ext.util.Format.htmlEncode(value) ); } function torrentSpeedRenderer(value) { @@ -9128,12 +9223,14 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { '<div style="background: url(' + deluge.config.base + 'tracker/{0}) no-repeat; padding-left: 20px;">{0}</div>', - value + Ext.util.Format.htmlEncode(value) ); } function etaSorter(eta) { - return eta * -1; + if (eta === 0) return Number.MAX_VALUE; + if (eta <= -1) return Number.MAX_SAFE_INTEGER; + return eta; } function dateOrNever(date) { @@ -9141,7 +9238,9 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { } function timeOrInf(time) { - return time < 0 ? '∞' : ftime(time); + if (time === 0) return ''; + if (time <= -1) return '∞'; + return ftime(time); } /** @@ -9386,6 +9485,8 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { { name: 'ratio', type: 'float' }, { name: 'distributed_copies', type: 'float' }, { name: 'time_added', type: 'int' }, + { name: 'last_seen_complete', type: 'int' }, + { name: 'completed_time', type: 'int' }, { name: 'tracker_host' }, { name: 'download_location' }, { name: 'total_done', type: 'int' }, @@ -9403,21 +9504,21 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { key: 'a', ctrl: true, stopEvent: true, - handler: function() { + handler: function () { deluge.torrents.getSelectionModel().selectAll(); }, }, { key: [46], stopEvent: true, - handler: function() { + handler: function () { ids = deluge.torrents.getSelectedIds(); deluge.removeWindow.show(ids); }, }, ], - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { id: 'torrentGrid', @@ -9442,12 +9543,12 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { Deluge.TorrentGrid.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.TorrentGrid.superclass.initComponent.call(this); deluge.events.on('torrentsRemoved', this.onTorrentsRemoved, this); deluge.events.on('disconnect', this.onDisconnect, this); - this.on('rowcontextmenu', function(grid, rowIndex, e) { + this.on('rowcontextmenu', function (grid, rowIndex, e) { e.stopEvent(); var selection = grid.getSelectionModel(); if (!selection.isSelected(rowIndex)) { @@ -9463,7 +9564,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { * @param index {int} The row index of the torrent you wish to retrieve. * @return {Ext.data.Record} The record representing the torrent. */ - getTorrent: function(index) { + getTorrent: function (index) { return this.getStore().getAt(index); }, @@ -9471,14 +9572,14 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { * Returns the currently selected record. * @ return {Array/Ext.data.Record} The record(s) representing the rows */ - getSelected: function() { + getSelected: function () { return this.getSelectionModel().getSelected(); }, /** * Returns the currently selected records. */ - getSelections: function() { + getSelections: function () { return this.getSelectionModel().getSelections(); }, @@ -9486,7 +9587,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { * Return the currently selected torrent id. * @return {String} The currently selected id. */ - getSelectedId: function() { + getSelectedId: function () { return this.getSelectionModel().getSelected().id; }, @@ -9494,15 +9595,15 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { * Return the currently selected torrent ids. * @return {Array} The currently selected ids. */ - getSelectedIds: function() { + getSelectedIds: function () { var ids = []; - Ext.each(this.getSelectionModel().getSelections(), function(r) { + Ext.each(this.getSelectionModel().getSelections(), function (r) { ids.push(r.id); }); return ids; }, - update: function(torrents, wipe) { + update: function (torrents, wipe) { var store = this.getStore(); // Need to perform a complete reload of the torrent grid. @@ -9536,7 +9637,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { store.add(newTorrents); // Remove any torrents that should not be in the store. - store.each(function(record) { + store.each(function (record) { if (!torrents[record.id]) { store.remove(record); delete this.torrents[record.id]; @@ -9550,17 +9651,17 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { }, // private - onDisconnect: function() { + onDisconnect: function () { this.getStore().removeAll(); this.torrents = {}; }, // private - onTorrentsRemoved: function(torrentIds) { + onTorrentsRemoved: function (torrentIds) { var selModel = this.getSelectionModel(); Ext.each( torrentIds, - function(torrentId) { + function (torrentId) { var record = this.getStore().getById(torrentId); if (selModel.isSelected(record)) { selModel.deselectRow(this.getStore().indexOf(record)); @@ -9618,7 +9719,7 @@ deluge.ui = { * @description Create all the interface components, the json-rpc client * and set up various events that the UI will utilise. */ - initialize: function() { + initialize: function () { deluge.add = new Deluge.add.AddWindow(); deluge.details = new Deluge.details.DetailsPanel(); deluge.connectionManager = new Deluge.ConnectionManager(); @@ -9676,7 +9777,7 @@ deluge.ui = { deluge.client.on( 'connected', - function(e) { + function (e) { deluge.login.show(); }, this, @@ -9689,7 +9790,7 @@ deluge.ui = { this.originalTitle = document.title; }, - checkConnection: function() { + checkConnection: function () { deluge.client.web.connected({ success: this.onConnectionSuccess, failure: this.onConnectionError, @@ -9697,7 +9798,7 @@ deluge.ui = { }); }, - update: function() { + update: function () { var filters = deluge.sidebar.getFilterStates(); this.oldFilters = this.filters; this.filters = filters; @@ -9710,9 +9811,9 @@ deluge.ui = { deluge.details.update(); }, - onConnectionError: function(error) {}, + onConnectionError: function (error) {}, - onConnectionSuccess: function(result) { + onConnectionSuccess: function (result) { deluge.statusbar.setStatus({ iconCls: 'x-deluge-statusbar icon-ok', text: _('Connection restored'), @@ -9723,7 +9824,7 @@ deluge.ui = { } }, - onUpdateError: function(error) { + onUpdateError: function (error) { if (this.errorCount == 2) { Ext.MessageBox.show({ title: _('Lost Connection'), @@ -9745,7 +9846,7 @@ deluge.ui = { * @private * Updates the various components in the interface. */ - onUpdate: function(data) { + onUpdate: function (data) { if (!data['connected']) { deluge.connectionManager.disconnect(true); return; @@ -9775,7 +9876,7 @@ deluge.ui = { * @private * Start the Deluge UI polling the server and update the interface. */ - onConnect: function() { + onConnect: function () { if (!this.running) { this.running = setInterval(this.update, 2000); this.update(); @@ -9790,14 +9891,14 @@ deluge.ui = { * @static * @private */ - onDisconnect: function() { + onDisconnect: function () { this.stop(); }, - onGotPlugins: function(plugins) { + onGotPlugins: function (plugins) { Ext.each( plugins.enabled_plugins, - function(plugin) { + function (plugin) { if (deluge.plugins[plugin]) return; deluge.client.web.get_plugin_resources(plugin, { success: this.onGotPluginResources, @@ -9808,7 +9909,7 @@ deluge.ui = { ); }, - onPluginEnabled: function(pluginName) { + onPluginEnabled: function (pluginName) { if (deluge.plugins[pluginName]) { deluge.plugins[pluginName].enable(); } else { @@ -9819,13 +9920,13 @@ deluge.ui = { } }, - onGotPluginResources: function(resources) { + onGotPluginResources: function (resources) { var scripts = Deluge.debug ? resources.debug_scripts : resources.scripts; Ext.each( scripts, - function(script) { + function (script) { Ext.ux.JSLoader({ url: deluge.config.base + script, onLoad: this.onPluginLoaded, @@ -9836,11 +9937,11 @@ deluge.ui = { ); }, - onPluginDisabled: function(pluginName) { + onPluginDisabled: function (pluginName) { if (deluge.plugins[pluginName]) deluge.plugins[pluginName].disable(); }, - onPluginLoaded: function(options) { + onPluginLoaded: function (options) { // This could happen if the plugin has multiple scripts if (!Deluge.hasPlugin(options.pluginName)) return; @@ -9854,7 +9955,7 @@ deluge.ui = { * @static * Stop the Deluge UI polling the server and clear the interface. */ - stop: function() { + stop: function () { if (this.running) { clearInterval(this.running); this.running = false; @@ -9863,6 +9964,6 @@ deluge.ui = { }, }; -Ext.onReady(function(e) { +Ext.onReady(function (e) { deluge.ui.initialize(); }); diff --git a/deluge/ui/web/js/deluge-all.js b/deluge/ui/web/js/deluge-all.js new file mode 100644 index 0000000..f9fd796 --- /dev/null +++ b/deluge/ui/web/js/deluge-all.js @@ -0,0 +1,291 @@ +Ext.ns("Deluge.add");Deluge.add.Window=Ext.extend(Ext.Window,{initComponent:function(){Deluge.add.Window.superclass.initComponent.call(this);this.addEvents("beforeadd","add","addfailed")},createTorrentId:function(){return(new Date).getTime().toString()}});Ext.namespace("Deluge.add"); +Ext.override(Ext.ux.form.FileUploadField,{onRender:function(a,b){Ext.ux.form.FileUploadField.superclass.onRender.call(this,a,b);this.wrap=this.el.wrap({cls:"x-form-field-wrap x-form-file-wrap"});this.el.addClass("x-form-file-text");this.el.dom.removeAttribute("name");this.createFileInput();var c=Ext.applyIf(this.buttonCfg||{},{text:this.buttonText});this.button=new Ext.Button(Ext.apply(c,{renderTo:this.wrap,cls:"x-form-file-btn"+(c.iconCls?" x-btn-text-icon":"")}));this.buttonOnly&&(this.el.hide(), +this.wrap.setWidth(this.button.getEl().getWidth()));this.bindListeners();this.resizeEl=this.positionEl=this.wrap}}); +Deluge.add.AddWindow=Ext.extend(Deluge.add.Window,{title:_("Add Torrents"),layout:"border",width:470,height:450,bodyStyle:"padding: 10px 5px;",buttonAlign:"right",closeAction:"hide",closable:!0,plain:!0,iconCls:"x-deluge-add-window-icon",initComponent:function(){Deluge.add.AddWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancelClick,this);this.addButton(_("Add"),this.onAddClick,this);this.list=new Ext.list.ListView({store:new Ext.data.SimpleStore({fields:[{name:"info_hash", +mapping:1},{name:"text",mapping:2}],id:0}),columns:[{id:"torrent",width:150,sortable:!0,dataIndex:"text",tpl:new Ext.XTemplate('<div class="x-deluge-add-torrent-name">{text:htmlEncode}</div>')}],stripeRows:!0,singleSelect:!0,listeners:{selectionchange:{fn:this.onSelect,scope:this}},hideHeaders:!0,autoExpandColumn:"torrent",height:"100%",autoScroll:!0});this.add({region:"center",items:[this.list],border:!1,bbar:new Ext.Toolbar({items:[{id:"fileUploadForm",xtype:"form",layout:"fit",baseCls:"x-plain", +fileUpload:!0,items:[{buttonOnly:!0,xtype:"fileuploadfield",id:"torrentFile",name:"file",multiple:!0,buttonCfg:{iconCls:"x-deluge-add-file",text:_("File")},listeners:{scope:this,fileselected:this.onFileSelected}}]},{text:_("Url"),iconCls:"icon-add-url",handler:this.onUrl,scope:this},{text:_("Infohash"),iconCls:"icon-magnet-add",hidden:!0,disabled:!0},"->",{text:_("Remove"),iconCls:"icon-remove",handler:this.onRemove,scope:this}]})});this.fileUploadForm=Ext.getCmp("fileUploadForm").getForm();this.optionsPanel= +this.add(new Deluge.add.OptionsPanel);this.on("hide",this.onHide,this);this.on("show",this.onShow,this)},clear:function(){this.list.getStore().removeAll();this.optionsPanel.clear();this.fileUploadForm.reset()},onAddClick:function(){var a=[];this.list&&(this.list.getStore().each(function(b){b=b.get("info_hash");a.push({path:this.optionsPanel.getFilename(b),options:this.optionsPanel.getOptions(b)})},this),deluge.client.web.add_torrents(a,{success:function(a){}}),this.clear(),this.hide())},onCancelClick:function(){this.clear(); +this.hide()},onFile:function(){this.file||(this.file=new Deluge.add.FileWindow);this.file.show()},onHide:function(){this.optionsPanel.setActiveTab(0);this.optionsPanel.files.setDisabled(!0);this.optionsPanel.form.setDisabled(!0)},onRemove:function(){if(this.list.getSelectionCount()){var a=this.list.getSelectedRecords()[0];a&&(this.list.getStore().remove(a),this.optionsPanel.clear(),this.torrents&&this.torrents[a.id]&&delete this.torrents[a.id])}},onSelect:function(a,b){if(b.length){var c=this.list.getRecord(b[0]); +this.optionsPanel.setTorrent(c.get("info_hash"))}else this.optionsPanel.files.setDisabled(!0),this.optionsPanel.form.setDisabled(!0)},onShow:function(){this.url||(this.url=new Deluge.add.UrlWindow,this.url.on("beforeadd",this.onTorrentBeforeAdd,this),this.url.on("add",this.onTorrentAdd,this),this.url.on("addfailed",this.onTorrentAddFailed,this));this.optionsPanel.form.getDefaults()},onFileSelected:function(){if(this.fileUploadForm.isValid()){var a=[],b=this.fileUploadForm.findField("torrentFile").value, +c=this.createTorrentId();Array.prototype.forEach.call(b,function(b,f){var g=c+f.toString();a.push(g);this.onTorrentBeforeAdd(g,b.name)}.bind(this));this.fileUploadForm.submit({url:deluge.config.base+"upload",waitMsg:_("Uploading your torrent..."),success:this.onUploadSuccess,failure:this.onUploadFailure,scope:this,torrentIds:a})}},onUploadSuccess:function(a,b){b.result.success?(b.result.files.forEach(function(a,d){deluge.client.web.get_torrent_info(a,{success:this.onGotInfo,scope:this,filename:a, +torrentId:b.options.torrentIds[d]})}.bind(this)),this.fileUploadForm.reset()):this.clear()},onUploadFailure:function(a,b){this.hide();Ext.MessageBox.show({title:_("Error"),msg:_("Failed to upload torrent"),buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});this.fireEvent("addfailed",this.torrentId)},onGotInfo:function(a,b,c,d){a.filename=d.options.filename;torrentId=d.options.torrentId;this.onTorrentAdd(torrentId,a)},onTorrentBeforeAdd:function(a,b){this.list.getStore().loadData([[a, +null,b]],!0)},onTorrentAdd:function(a,b){var c=this.list.getStore().getById(a);b?(c.set("info_hash",b.info_hash),c.set("text",b.name),this.list.getStore().commitChanges(),this.optionsPanel.addTorrent(b),this.list.select(c)):(Ext.MessageBox.show({title:_("Error"),msg:_("Not a valid torrent"),buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"}),this.list.getStore().remove(c))},onTorrentAddFailed:function(a){var b=this.list.getStore();(a=b.getById(a))&&b.remove(a)}, +onUrl:function(a,b){this.url.show()}});Ext.ns("Deluge.add"); +Deluge.add.FilesTab=Ext.extend(Ext.ux.tree.TreeGrid,{layout:"fit",title:_("Files"),autoScroll:!1,animate:!1,border:!1,disabled:!0,rootVisible:!1,columns:[{header:_("Filename"),width:295,dataIndex:"filename",tpl:new Ext.XTemplate("{filename:htmlEncode}")},{header:_("Size"),width:60,dataIndex:"size",tpl:new Ext.XTemplate("{size:this.fsize}",{fsize:function(a){return fsize(a)}})},{header:_("Download"),width:65,dataIndex:"download",tpl:new Ext.XTemplate("{download:this.format}",{format:function(a){return'<div rel="chkbox" class="x-grid3-check-col'+ +(a?"-on":"")+'"> </div>'}})}],initComponent:function(){Deluge.add.FilesTab.superclass.initComponent.call(this);this.on("click",this.onNodeClick,this)},clearFiles:function(){var a=this.getRootNode();a.hasChildNodes()&&a.cascade(function(a){a.parentNode&&a.getOwnerTree()&&a.remove()})},setDownload:function(a,b,c){a.attributes.download=b;a.ui.updateColumns();if(a.isLeaf()){if(!c)return this.fireEvent("fileschecked",[a],b,!b)}else{var d=[a];a.cascade(function(a){a.attributes.download=b;a.ui.updateColumns(); +d.push(a)},this);if(!c)return this.fireEvent("fileschecked",d,b,!b)}},onNodeClick:function(a,b){"chkbox"==(new Ext.Element(b.target)).getAttribute("rel")&&this.setDownload(a,!a.attributes.download)}});Ext.namespace("Ext.deluge.add");Ext.ns("Deluge.add"); +Deluge.add.OptionsPanel=Ext.extend(Ext.TabPanel,{torrents:{},region:"south",border:!1,activeTab:0,height:265,initComponent:function(){Deluge.add.OptionsPanel.superclass.initComponent.call(this);this.files=this.add(new Deluge.add.FilesTab);this.form=this.add(new Deluge.add.OptionsTab);this.files.on("fileschecked",this.onFilesChecked,this)},addTorrent:function(a){this.torrents[a.info_hash]=a;var b={};this.walkFileTree(a.files_tree,function(a,c,g,e){"file"==c&&(b[g.index]=g.download)},this);var c=[]; +Ext.each(Ext.keys(b),function(a){c[a]=b[a]});a=this.form.optionsManager.changeId(a.info_hash,!0);this.form.optionsManager.setDefault("file_priorities",c);this.form.optionsManager.changeId(a,!0)},clear:function(){this.files.clearFiles();this.form.optionsManager.resetAll()},getFilename:function(a){return this.torrents[a].filename},getOptions:function(a){a=this.form.optionsManager.changeId(a,!0);var b=this.form.optionsManager.get();this.form.optionsManager.changeId(a,!0);Ext.each(b.file_priorities,function(a, +d){b.file_priorities[d]=a?1:0});return b},setTorrent:function(a){if(a){this.torrentId=a;this.form.optionsManager.changeId(a);this.files.clearFiles();var b=this.files.getRootNode(),c=this.form.optionsManager.get("file_priorities");this.form.setDisabled(!1);this.torrents[a].files_tree?(this.walkFileTree(this.torrents[a].files_tree,function(a,b,g,e){a=new Ext.tree.TreeNode({download:g.index?c[g.index]:!0,filename:a,fileindex:g.index,leaf:"dir"!=b,size:g.length});e.appendChild(a);if("dir"==b)return a}, +this,b),b.firstChild.expand(),this.files.setDisabled(!1),this.files.show()):(this.form.show(),this.files.setDisabled(!0))}},walkFileTree:function(a,b,c,d){for(var f in a.contents){var g=a.contents[f],e=g.type,l=c?b.apply(c,[f,e,g,d]):b(f,e,g,d);"dir"==e&&this.walkFileTree(g,b,c,l)}},onFilesChecked:function(a,b,c){Ext.each(a,function(a){if(!(0>a.attributes.fileindex)){var c=this.form.optionsManager.get("file_priorities");c[a.attributes.fileindex]=b;this.form.optionsManager.update("file_priorities", +c)}},this)}});Ext.ns("Deluge.add"); +Deluge.add.OptionsTab=Ext.extend(Ext.form.FormPanel,{title:_("Options"),height:170,border:!1,bodyStyle:"padding: 5px",disabled:!0,labelWidth:1,initComponent:function(){Deluge.add.OptionsTab.superclass.initComponent.call(this);this.optionsManager=new Deluge.MultiOptionsManager;var a=this.add({xtype:"fieldset",title:_("Download Folder"),border:!1,autoHeight:!0,defaultType:"textfield",labelWidth:1,fieldLabel:"",style:"padding: 5px 0; margin-bottom: 0;"});this.optionsManager.bind("download_location",a.add({fieldLabel:"", +name:"download_location",anchor:"95%",labelSeparator:""}));a=this.add({xtype:"fieldset",title:_("Move Completed Folder"),border:!1,autoHeight:!0,defaultType:"togglefield",labelWidth:1,fieldLabel:"",style:"padding: 5px 0; margin-bottom: 0;"});a=a.add({fieldLabel:"",name:"move_completed_path",anchor:"98%"});this.optionsManager.bind("move_completed",a.toggle);this.optionsManager.bind("move_completed_path",a.input);var b=this.add({border:!1,layout:"column",defaultType:"fieldset"}),a=b.add({title:_("Bandwidth"), +border:!1,autoHeight:!0,bodyStyle:"padding: 2px 5px",labelWidth:105,width:200,defaultType:"spinnerfield",style:"padding-right: 10px;"});this.optionsManager.bind("max_download_speed",a.add({fieldLabel:_("Max Down Speed"),name:"max_download_speed",width:60}));this.optionsManager.bind("max_upload_speed",a.add({fieldLabel:_("Max Up Speed"),name:"max_upload_speed",width:60}));this.optionsManager.bind("max_connections",a.add({fieldLabel:_("Max Connections"),name:"max_connections",width:60}));this.optionsManager.bind("max_upload_slots", +a.add({fieldLabel:_("Max Upload Slots"),name:"max_upload_slots",width:60}));a=b.add({border:!1,autoHeight:!0,defaultType:"checkbox"});this.optionsManager.bind("add_paused",a.add({name:"add_paused",boxLabel:_("Add In Paused State"),fieldLabel:"",labelSeparator:""}));this.optionsManager.bind("prioritize_first_last_pieces",a.add({name:"prioritize_first_last_pieces",boxLabel:_("Prioritize First/Last Pieces"),fieldLabel:"",labelSeparator:""}));this.optionsManager.bind("sequential_download",a.add({name:"sequential_download", +boxLabel:_("Sequential Download"),fieldLabel:"",labelSeparator:""}));this.optionsManager.bind("seed_mode",a.add({name:"seed_mode",boxLabel:_("Skip File Hash Check"),fieldLabel:"",labelSeparator:""}));this.optionsManager.bind("super_seeding",a.add({name:"super_seeding",boxLabel:_("Super Seed"),fieldLabel:"",labelSeparator:""}));this.optionsManager.bind("pre_allocate_storage",a.add({name:"pre_allocate_storage",boxLabel:_("Preallocate Disk Space"),fieldLabel:"",labelSeparator:""}))},getDefaults:function(){deluge.client.core.get_config_values("add_paused pre_allocate_storage download_location max_connections_per_torrent max_download_speed_per_torrent move_completed move_completed_path max_upload_slots_per_torrent max_upload_speed_per_torrent prioritize_first_last_pieces sequential_download".split(" "), +{success:function(a){this.optionsManager.options={file_priorities:[],add_paused:a.add_paused,sequential_download:a.sequential_download,pre_allocate_storage:a.pre_allocate_storage,download_location:a.download_location,move_completed:a.move_completed,move_completed_path:a.move_completed_path,max_connections:a.max_connections_per_torrent,max_download_speed:a.max_download_speed_per_torrent,max_upload_slots:a.max_upload_slots_per_torrent,max_upload_speed:a.max_upload_speed_per_torrent,prioritize_first_last_pieces:a.prioritize_first_last_pieces, +seed_mode:!1,super_seeding:!1};this.optionsManager.resetAll()},scope:this})}});Ext.namespace("Deluge.add"); +Deluge.add.UrlWindow=Ext.extend(Deluge.add.Window,{title:_("Add from Url"),modal:!0,plain:!0,layout:"fit",width:350,height:155,buttonAlign:"center",closeAction:"hide",bodyStyle:"padding: 10px 5px;",iconCls:"x-deluge-add-url-window-icon",initComponent:function(){Deluge.add.UrlWindow.superclass.initComponent.call(this);this.addButton(_("Add"),this.onAddClick,this);var a=this.add({xtype:"form",defaultType:"textfield",baseCls:"x-plain",labelWidth:55});this.urlField=a.add({fieldLabel:_("Url"),id:"url", +name:"url",width:"97%"});this.urlField.on("specialkey",this.onAdd,this);this.cookieField=a.add({fieldLabel:_("Cookies"),id:"cookies",name:"cookies",width:"97%"});this.cookieField.on("specialkey",this.onAdd,this)},onAddClick:function(a,b){if(!(("url"==a.id||"cookies"==a.id)&&b.getKey()!=b.ENTER)){a=this.urlField;var c=a.getValue(),d=this.cookieField.getValue(),f=this.createTorrentId();0==c.indexOf("magnet:?")&&-1<c.indexOf("xt=urn:btih")?deluge.client.web.get_magnet_info(c,{success:this.onGotInfo, +scope:this,filename:c,torrentId:f}):deluge.client.web.download_torrent_from_url(c,d,{success:this.onDownload,failure:this.onDownloadFailed,scope:this,torrentId:f});this.hide();this.urlField.setValue("");this.fireEvent("beforeadd",f,c)}},onDownload:function(a,b,c,d){deluge.client.web.get_torrent_info(a,{success:this.onGotInfo,failure:this.onDownloadFailed,scope:this,filename:a,torrentId:d.options.torrentId})},onDownloadFailed:function(a,b,c){Ext.MessageBox.show({title:_("Error"),msg:_("Failed to download torrent"), +buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});this.fireEvent("addfailed",c.options.torrentId)},onGotInfo:function(a,b,c,d){a.filename=d.options.filename;this.fireEvent("add",d.options.torrentId,a)}});Ext.namespace("Deluge.data");Deluge.data.SortTypes={asIPAddress:function(a){a=a.match(/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\:(\d+)/);return 256*(256*(256*+a[1]+ +a[2])+ +a[3])+ +a[4]},asQueuePosition:function(a){return-1<a?a:Number.MAX_VALUE},asName:function(a){return String(a).toLowerCase()}}; +Ext.namespace("Deluge.data");Deluge.data.Peer=Ext.data.Record.create([{name:"country",type:"string"},{name:"ip",type:"string",sortType:Deluge.data.SortTypes.asIPAddress},{name:"client",type:"string"},{name:"progress",type:"float"},{name:"down_speed",type:"int"},{name:"up_speed",type:"int"},{name:"seed",type:"int"}]);Ext.namespace("Deluge.data"); +Deluge.data.Torrent=Ext.data.Record.create([{name:"queue",type:"int"},{name:"name",type:"string",sortType:Deluge.data.SortTypes.asName},{name:"total_wanted",type:"int"},{name:"state",type:"string"},{name:"progress",type:"int"},{name:"num_seeds",type:"int"},{name:"total_seeds",type:"int"},{name:"num_peers",type:"int"},{name:"total_peers",type:"int"},{name:"download_payload_rate",type:"int"},{name:"upload_payload_rate",type:"int"},{name:"eta",type:"int"},{name:"ratio",type:"float"},{name:"distributed_copies", +type:"float"},{name:"time_added",type:"int"},{name:"tracker_host",type:"string"},{name:"save_path",type:"string"},{name:"total_done",type:"int"},{name:"total_uploaded",type:"int"},{name:"total_remaining",type:"int"},{name:"max_download_speed",type:"int"},{name:"max_upload_speed",type:"int"},{name:"seeds_peers_ratio",type:"float"},{name:"time_since_transfer",type:"int"}]);Ext.namespace("Deluge.details"); +Deluge.details.DetailsPanel=Ext.extend(Ext.TabPanel,{id:"torrentDetails",activeTab:0,initComponent:function(){Deluge.details.DetailsPanel.superclass.initComponent.call(this);this.add(new Deluge.details.StatusTab);this.add(new Deluge.details.DetailsTab);this.add(new Deluge.details.FilesTab);this.add(new Deluge.details.PeersTab);this.add(new Deluge.details.OptionsTab)},clear:function(){this.items.each(function(a){a.clear&&(a.clear.defer(100,a),a.disable())})},update:function(a){var b=deluge.torrents.getSelected(); +b?(this.items.each(function(a){a.disabled&&a.enable()}),a=a||this.getActiveTab(),a.update&&a.update(b.id)):this.clear()},onRender:function(a,b){Deluge.details.DetailsPanel.superclass.onRender.call(this,a,b);deluge.events.on("disconnect",this.clear,this);deluge.torrents.on("rowclick",this.onTorrentsClick,this);this.on("tabchange",this.onTabChange,this);deluge.torrents.getSelectionModel().on("selectionchange",function(a){a.hasSelection()||this.clear()},this)},onTabChange:function(a,b){this.update(b)}, +onTorrentsClick:function(a,b,c){this.update()}}); +Deluge.details.DetailsTab=Ext.extend(Ext.Panel,{title:_("Details"),fields:{},autoScroll:!0,queuedItems:{},oldData:{},initComponent:function(){Deluge.details.DetailsTab.superclass.initComponent.call(this);this.addItem("torrent_name",_("Name:"));this.addItem("hash",_("Hash:"));this.addItem("path",_("Download Folder:"));this.addItem("size",_("Total Size:"));this.addItem("files",_("Total Files:"));this.addItem("comment",_("Comment:"));this.addItem("status",_("Status:"));this.addItem("tracker",_("Tracker:")); +this.addItem("creator",_("Created By:"))},onRender:function(a,b){Deluge.details.DetailsTab.superclass.onRender.call(this,a,b);this.body.setStyle("padding","10px");this.dl=Ext.DomHelper.append(this.body,{tag:"dl"},!0);for(var c in this.queuedItems)this.doAddItem(c,this.queuedItems[c])},addItem:function(a,b){this.rendered?this.doAddItem(a,b):this.queuedItems[a]=b},doAddItem:function(a,b){Ext.DomHelper.append(this.dl,{tag:"dt",cls:a,html:b});this.fields[a]=Ext.DomHelper.append(this.dl,{tag:"dd",cls:a, +html:""},!0)},clear:function(){if(this.fields){for(var a in this.fields)this.fields[a].dom.innerHTML="";this.oldData={}}},update:function(a){deluge.client.web.get_torrent_status(a,Deluge.Keys.Details,{success:this.onRequestComplete,scope:this,torrentId:a})},onRequestComplete:function(a,b,c,d){a={torrent_name:a.name,hash:d.options.torrentId,path:a.download_location,size:fsize(a.total_size),files:a.num_files,status:a.message,tracker:a.tracker_host,comment:a.comment,creator:a.creator};for(var f in this.fields)Ext.isDefined(a[f])&& +a[f]!=this.oldData[f]&&(this.fields[f].dom.innerHTML=Ext.util.Format.htmlEncode(a[f]));this.oldData=a}}); +Deluge.details.FilesTab=Ext.extend(Ext.ux.tree.TreeGrid,{title:_("Files"),rootVisible:!1,columns:[{header:_("Filename"),width:330,dataIndex:"filename",tpl:new Ext.XTemplate("{filename:htmlEncode}")},{header:_("Size"),width:150,dataIndex:"size",tpl:new Ext.XTemplate("{size:this.fsize}",{fsize:function(a){return fsize(a)}})},{xtype:"tgrendercolumn",header:_("Progress"),width:150,dataIndex:"progress",renderer:function(a){a*=100;return Deluge.progressBar(a,this.col.width,a.toFixed(2)+"%",0)}},{header:_("Priority"), +width:150,dataIndex:"priority",tpl:new Ext.XTemplate('<tpl if="!isNaN(priority)"><div class="{priority:this.getClass}">{priority:this.getName}</div></tpl>',{getClass:function(a){return FILE_PRIORITY_CSS[a]},getName:function(a){return _(FILE_PRIORITY[a])}})}],selModel:new Ext.tree.MultiSelectionModel,initComponent:function(){Deluge.details.FilesTab.superclass.initComponent.call(this);this.setRootNode(new Ext.tree.TreeNode({text:_("Files")}))},clear:function(){var a=this.getRootNode();a.hasChildNodes()&& +a.cascade(function(a){var c=a.parentNode;c&&c.ownerTree&&c.removeChild(a)})},createFileTree:function(a){function b(a,c){for(var g in a.contents){var e=a.contents[g];"dir"==e.type?b(e,c.appendChild(new Ext.tree.TreeNode({text:g,filename:g,size:e.size,progress:e.progress,priority:e.priority}))):c.appendChild(new Ext.tree.TreeNode({text:g,filename:g,fileIndex:e.index,size:e.size,progress:e.progress,priority:e.priority,leaf:!0,iconCls:"x-deluge-file",uiProvider:Ext.ux.tree.TreeGridNodeUI}))}}var c=this.getRootNode(); +b(a,c);c.firstChild.expand()},update:function(a){this.torrentId!=a&&(this.clear(),this.torrentId=a);deluge.client.web.get_torrent_files(a,{success:this.onRequestComplete,scope:this,torrentId:a})},updateFileTree:function(a){function b(a,d){for(var f in a.contents){var g=a.contents[f],e=d.findChild("filename",f);e.attributes.size=g.size;e.attributes.progress=g.progress;e.attributes.priority=g.priority;e.ui.updateColumns();"dir"==g.type&&b(g,e)}}b(a,this.getRootNode())},onRender:function(a,b){Deluge.details.FilesTab.superclass.onRender.call(this, +a,b);deluge.menus.filePriorities.on("itemclick",this.onItemClick,this);this.on("contextmenu",this.onContextMenu,this);this.sorter=new Ext.tree.TreeSorter(this,{folderSort:!0})},onContextMenu:function(a,b){b.stopEvent();var c=this.getSelectionModel();2>c.getSelectedNodes().length&&(c.clearSelections(),a.select());deluge.menus.filePriorities.showAt(b.getPoint())},onItemClick:function(a,b){switch(a.id){case "expandAll":this.expandAll();break;default:var c={};this.getRootNode().cascade(function(a){Ext.isEmpty(a.attributes.fileIndex)|| +(c[a.attributes.fileIndex]=a.attributes.priority)});var d=this.getSelectionModel().getSelectedNodes();Ext.each(d,function(b){b.isLeaf()?Ext.isEmpty(b.attributes.fileIndex)||(c[b.attributes.fileIndex]=a.filePriority):b.cascade(function(b){Ext.isEmpty(b.attributes.fileIndex)||(c[b.attributes.fileIndex]=a.filePriority)})});var f=Array(Ext.keys(c).length),g;for(g in c)f[g]=c[g];deluge.client.core.set_torrent_options([this.torrentId],{file_priorities:f},{success:function(){Ext.each(d,function(b){b.setColumnValue(3, +a.filePriority)})},scope:this})}},onRequestComplete:function(a,b){this.getRootNode().hasChildNodes()?this.updateFileTree(a):this.createFileTree(a)}}); +Deluge.details.OptionsTab=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({autoScroll:!0,bodyStyle:"padding: 5px;",border:!1,cls:"x-deluge-options",defaults:{autoHeight:!0,labelWidth:1,defaultType:"checkbox"},deferredRender:!1,layout:"column",title:_("Options")},a);Deluge.details.OptionsTab.superclass.constructor.call(this,a)},initComponent:function(){Deluge.details.OptionsTab.superclass.initComponent.call(this);this.fieldsets={};this.fields={};this.optionsManager=new Deluge.MultiOptionsManager({options:{max_download_speed:-1, +max_upload_speed:-1,max_connections:-1,max_upload_slots:-1,auto_managed:!1,stop_at_ratio:!1,stop_ratio:2,remove_at_ratio:!1,move_completed:!1,move_completed_path:"","private":!1,prioritize_first_last:!1,super_seeding:!1}});this.fieldsets.bandwidth=this.add({xtype:"fieldset",defaultType:"spinnerfield",bodyStyle:"padding: 5px",layout:"table",layoutConfig:{columns:3},labelWidth:150,style:"margin-left: 10px; margin-right: 5px; padding: 5px",title:_("Bandwidth"),width:250});this.fieldsets.bandwidth.add({xtype:"label", +text:_("Max Download Speed:"),forId:"max_download_speed",cls:"x-deluge-options-label"});this.fields.max_download_speed=this.fieldsets.bandwidth.add({id:"max_download_speed",name:"max_download_speed",width:70,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}});this.fieldsets.bandwidth.add({xtype:"label",text:_("KiB/s"),style:"margin-left: 10px"});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Upload Speed:"),forId:"max_upload_speed",cls:"x-deluge-options-label"});this.fields.max_upload_speed= +this.fieldsets.bandwidth.add({id:"max_upload_speed",name:"max_upload_speed",width:70,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}});this.fieldsets.bandwidth.add({xtype:"label",text:_("KiB/s"),style:"margin-left: 10px"});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Connections:"),forId:"max_connections",cls:"x-deluge-options-label"});this.fields.max_connections=this.fieldsets.bandwidth.add({id:"max_connections",name:"max_connections",width:70,value:-1, +strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},colspan:2});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Upload Slots:"),forId:"max_upload_slots",cls:"x-deluge-options-label"});this.fields.max_upload_slots=this.fieldsets.bandwidth.add({id:"max_upload_slots",name:"max_upload_slots",width:70,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},colspan:2});this.fieldsets.queue=this.add({xtype:"fieldset",title:_("Queue"),style:"margin-left: 5px; margin-right: 5px; padding: 5px", +width:210,layout:"table",layoutConfig:{columns:2},labelWidth:0,defaults:{fieldLabel:"",labelSeparator:""}});this.fields.auto_managed=this.fieldsets.queue.add({xtype:"checkbox",fieldLabel:"",labelSeparator:"",name:"is_auto_managed",boxLabel:_("Auto Managed"),width:200,colspan:2});this.fields.stop_at_ratio=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"stop_at_ratio",width:120,boxLabel:_("Stop seed at ratio:"),handler:this.onStopRatioChecked,scope:this});this.fields.stop_ratio=this.fieldsets.queue.add({xtype:"spinnerfield", +id:"stop_ratio",name:"stop_ratio",disabled:!0,width:50,value:2,strategy:{xtype:"number",minValue:-1,maxValue:99999,incrementValue:0.1,alternateIncrementValue:1,decimalPrecision:1}});this.fields.remove_at_ratio=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"remove_at_ratio",ctCls:"x-deluge-indent-checkbox",bodyStyle:"padding-left: 10px",boxLabel:_("Remove at ratio"),disabled:!0,colspan:2});this.fields.move_completed=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"move_completed", +boxLabel:_("Move Completed:"),colspan:2,handler:this.onMoveCompletedChecked,scope:this});this.fields.move_completed_path=this.fieldsets.queue.add({xtype:"textfield",fieldLabel:"",id:"move_completed_path",colspan:3,bodyStyle:"margin-left: 20px",width:180,disabled:!0});this.rightColumn=this.add({border:!1,autoHeight:!0,style:"margin-left: 5px",width:210});this.fieldsets.general=this.rightColumn.add({xtype:"fieldset",autoHeight:!0,defaultType:"checkbox",title:_("General"),layout:"form"});this.fields["private"]= +this.fieldsets.general.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Private"),id:"private",disabled:!0});this.fields.prioritize_first_last=this.fieldsets.general.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Prioritize First/Last"),id:"prioritize_first_last"});this.fields.super_seeding=this.fieldsets.general.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Super Seeding"),id:"super_seeding"});for(var a in this.fields)this.optionsManager.bind(a,this.fields[a]);this.buttonPanel=this.rightColumn.add({layout:"hbox", +xtype:"panel",border:!1});this.buttonPanel.add({id:"edit_trackers",xtype:"button",text:_("Edit Trackers"),cls:"x-btn-text-icon",iconCls:"x-deluge-edit-trackers",border:!1,width:100,handler:this.onEditTrackers,scope:this});this.buttonPanel.add({id:"apply",xtype:"button",text:_("Apply"),style:"margin-left: 10px;",border:!1,width:100,handler:this.onApply,scope:this})},onRender:function(a,b){Deluge.details.OptionsTab.superclass.onRender.call(this,a,b);this.layout=new Ext.layout.ColumnLayout;this.layout.setContainer(this); +this.doLayout()},clear:function(){null!=this.torrentId&&(this.torrentId=null,this.optionsManager.changeId(null))},reset:function(){this.torrentId&&this.optionsManager.reset()},update:function(a){this.torrentId&&!a&&this.clear();a&&(this.torrentId!=a&&(this.torrentId=a,this.optionsManager.changeId(a)),deluge.client.web.get_torrent_status(a,Deluge.Keys.Options,{success:this.onRequestComplete,scope:this}))},onApply:function(){var a=this.optionsManager.getDirty();deluge.client.core.set_torrent_options([this.torrentId], +a,{success:function(){this.optionsManager.commit()},scope:this})},onEditTrackers:function(){deluge.editTrackers.show()},onMoveCompletedChecked:function(a,b){this.fields.move_completed_path.setDisabled(!b);b&&this.fields.move_completed_path.focus()},onStopRatioChecked:function(a,b){this.fields.remove_at_ratio.setDisabled(!b);this.fields.stop_ratio.setDisabled(!b)},onRequestComplete:function(a,b){this.fields["private"].setValue(a["private"]);this.fields["private"].setDisabled(!0);delete a["private"]; +a.auto_managed=a.is_auto_managed;a.prioritize_first_last_pieces=a.prioritize_first_last;this.optionsManager.setDefault(a);var c=this.optionsManager.get("stop_at_ratio");this.fields.remove_at_ratio.setDisabled(!c);this.fields.stop_ratio.setDisabled(!c);this.fields.move_completed_path.setDisabled(!this.optionsManager.get("move_completed"))}}); +(function(){function a(a){return!a.replace(" ","").replace(" ","")?"":String.format('<img alt="{1}" title="{1}" src="{0}flag/{1}" />',deluge.config.base,a)}function b(a,b,c){b=1024==c.data.seed?"x-deluge-seed":"x-deluge-peer";c=a.split(":");2<c.length&&(a=c.pop(),a="["+c.join(":")+"]:"+a);return String.format('<div class="{0}">{1}</div>',b,a)}function c(a){a=(100*a).toFixed(0);return Deluge.progressBar(a,this.width-8,a+"%")}Deluge.details.PeersTab=Ext.extend(Ext.grid.GridPanel,{peers:{},constructor:function(d){d= +Ext.apply({title:_("Peers"),cls:"x-deluge-peers",store:new Ext.data.Store({reader:new Ext.data.JsonReader({idProperty:"ip",root:"peers"},Deluge.data.Peer)}),columns:[{header:" ",width:30,sortable:!0,renderer:a,dataIndex:"country"},{header:_("Address"),width:125,sortable:!0,renderer:b,dataIndex:"ip"},{header:_("Client"),width:125,sortable:!0,renderer:"htmlEncode",dataIndex:"client"},{header:_("Progress"),width:150,sortable:!0,renderer:c,dataIndex:"progress"},{header:_("Down Speed"),width:100, +sortable:!0,renderer:fspeed,dataIndex:"down_speed"},{header:_("Up Speed"),width:100,sortable:!0,renderer:fspeed,dataIndex:"up_speed"}],stripeRows:!0,deferredRender:!1,autoScroll:!0},d);Deluge.details.PeersTab.superclass.constructor.call(this,d)},clear:function(){this.getStore().removeAll();this.peers={}},update:function(a){deluge.client.web.get_torrent_status(a,Deluge.Keys.Peers,{success:this.onRequestComplete,scope:this})},onRequestComplete:function(a,b){if(a){var c=this.getStore(),e=[],l={};Ext.each(a.peers, +function(a){if(this.peers[a.ip]){var b=c.getById(a.ip);b.beginEdit();for(var d in a)b.get(d)!=a[d]&&b.set(d,a[d]);b.endEdit()}else this.peers[a.ip]=1,e.push(new Deluge.data.Peer(a,a.ip));l[a.ip]=1},this);c.add(e);c.each(function(a){l[a.id]||(c.remove(a),delete this.peers[a.id])},this);c.commitChanges();var h=c.getSortState();h&&c.sort(h.field,h.direction)}}})})();Ext.ns("Deluge.details"); +Deluge.details.StatusTab=Ext.extend(Ext.Panel,{title:_("Status"),autoScroll:!0,onRender:function(a,b){Deluge.details.StatusTab.superclass.onRender.call(this,a,b);this.progressBar=this.add({xtype:"progress",cls:"x-deluge-status-progressbar"});this.status=this.add({cls:"x-deluge-status",id:"deluge-details-status",border:!1,width:1E3,listeners:{render:{fn:function(a){a.load({url:deluge.config.base+"render/tab_status.html",text:_("Loading")+"..."});a.getUpdater().on("update",this.onPanelUpdate,this)}, +scope:this}}})},clear:function(){this.progressBar.updateProgress(0," ");for(var a in this.fields)this.fields[a].innerHTML=""},update:function(a){this.fields||this.getFields();deluge.client.web.get_torrent_status(a,Deluge.Keys.Status,{success:this.onRequestComplete,scope:this})},onPanelUpdate:function(a,b){this.fields={};Ext.each(Ext.query("dd",this.status.body.dom),function(a){this.fields[a.className]=a},this)},onRequestComplete:function(a){seeds=-1<a.total_seeds?a.num_seeds+" ("+a.total_seeds+")": +a.num_seeds;peers=-1<a.total_peers?a.num_peers+" ("+a.total_peers+")":a.num_peers;last_seen_complete=0<a.last_seen_complete?fdate(a.last_seen_complete):"Never";completed_time=0<a.completed_time?fdate(a.completed_time):"";var b={downloaded:fsize(a.total_done,!0),uploaded:fsize(a.total_uploaded,!0),share:-1==a.ratio?"∞":a.ratio.toFixed(3),announce:ftime(a.next_announce),tracker_status:a.tracker_status,downspeed:a.download_payload_rate?fspeed(a.download_payload_rate):"0.0 KiB/s",upspeed:a.upload_payload_rate? +fspeed(a.upload_payload_rate):"0.0 KiB/s",eta:0>a.eta?"∞":ftime(a.eta),pieces:a.num_pieces+" ("+fsize(a.piece_length)+")",seeds:seeds,peers:peers,avail:a.distributed_copies.toFixed(3),active_time:ftime(a.active_time),seeding_time:ftime(a.seeding_time),seed_rank:a.seed_rank,time_added:fdate(a.time_added),last_seen_complete:last_seen_complete,completed_time:completed_time,time_since_transfer:ftime(a.time_since_transfer)};b.auto_managed=_(a.is_auto_managed?"True":"False");var c={Error:_("Error"), +Warning:_("Warning"),"Announce OK":_("Announce OK"),"Announce Sent":_("Announce Sent")},d;for(d in c)if(-1!=b.tracker_status.indexOf(d)){b.tracker_status=b.tracker_status.replace(d,c[d]);break}b.downloaded+=" ("+(a.total_payload_download?fsize(a.total_payload_download):"0.0 KiB")+")";b.uploaded+=" ("+(a.total_payload_upload?fsize(a.total_payload_upload):"0.0 KiB")+")";for(var f in this.fields)this.fields[f].innerHTML=b[f];b=a.state+" "+a.progress.toFixed(2)+"%";this.progressBar.updateProgress(a.progress/ +100,b)}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Bandwidth=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:!1,title:_("Bandwidth"),header:!1,layout:"form",labelWidth:10},a);Deluge.preferences.Bandwidth.superclass.constructor.call(this,a)},initComponent:function(){Deluge.preferences.Bandwidth.superclass.initComponent.call(this);var a=deluge.preferences.getOptionsManager(),b=this.add({xtype:"fieldset",border:!1,title:_("Global Bandwidth Usage"),labelWidth:200,defaultType:"spinnerfield",defaults:{minValue:-1, +maxValue:99999},style:"margin-bottom: 0px; padding-bottom: 0px;",autoHeight:!0});a.bind("max_connections_global",b.add({name:"max_connections_global",fieldLabel:_("Maximum Connections:"),labelSeparator:"",width:80,value:-1,decimalPrecision:0}));a.bind("max_upload_slots_global",b.add({name:"max_upload_slots_global",fieldLabel:_("Maximum Upload Slots"),labelSeparator:"",width:80,value:-1,decimalPrecision:0}));a.bind("max_download_speed",b.add({name:"max_download_speed",fieldLabel:_("Maximum Download Speed (KiB/s):"), +labelSeparator:"",width:80,value:-1,decimalPrecision:1}));a.bind("max_upload_speed",b.add({name:"max_upload_speed",fieldLabel:_("Maximum Upload Speed (KiB/s):"),labelSeparator:"",width:80,value:-1,decimalPrecision:1}));a.bind("max_half_open_connections",b.add({name:"max_half_open_connections",fieldLabel:_("Maximum Half-Open Connections:"),labelSeparator:"",width:80,value:-1,decimalPrecision:0}));a.bind("max_connections_per_second",b.add({name:"max_connections_per_second",fieldLabel:_("Maximum Connection Attempts per Second:"), +labelSeparator:"",width:80,value:-1,decimalPrecision:0}));b=this.add({xtype:"fieldset",border:!1,title:"",defaultType:"checkbox",style:"padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;",autoHeight:!0});a.bind("ignore_limits_on_local_network",b.add({name:"ignore_limits_on_local_network",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Ignore limits on local network")}));a.bind("rate_limit_ip_overhead",b.add({name:"rate_limit_ip_overhead",height:22,fieldLabel:"",labelSeparator:"", +boxLabel:_("Rate limit IP overhead")}));b=this.add({xtype:"fieldset",border:!1,title:_("Per Torrent Bandwidth Usage"),style:"margin-bottom: 0px; padding-bottom: 0px;",defaultType:"spinnerfield",labelWidth:200,defaults:{minValue:-1,maxValue:99999},autoHeight:!0});a.bind("max_connections_per_torrent",b.add({name:"max_connections_per_torrent",fieldLabel:_("Maximum Connections:"),labelSeparator:"",width:80,value:-1,decimalPrecision:0}));a.bind("max_upload_slots_per_torrent",b.add({name:"max_upload_slots_per_torrent", +fieldLabel:_("Maximum Upload Slots:"),labelSeparator:"",width:80,value:-1,decimalPrecision:0}));a.bind("max_download_speed_per_torrent",b.add({name:"max_download_speed_per_torrent",fieldLabel:_("Maximum Download Speed (KiB/s):"),labelSeparator:"",width:80,value:-1,decimalPrecision:0}));a.bind("max_upload_speed_per_torrent",b.add({name:"max_upload_speed_per_torrent",fieldLabel:_("Maximum Upload Speed (KiB/s):"),labelSeparator:"",width:80,value:-1,decimalPrecision:0}))}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Cache=Ext.extend(Ext.form.FormPanel,{border:!1,title:_("Cache"),header:!1,layout:"form",initComponent:function(){Deluge.preferences.Cache.superclass.initComponent.call(this);var a=deluge.preferences.getOptionsManager(),b=this.add({xtype:"fieldset",border:!1,title:_("Settings"),autoHeight:!0,labelWidth:180,defaultType:"spinnerfield",defaults:{decimalPrecision:0,minValue:-1,maxValue:999999}});a.bind("cache_size",b.add({fieldLabel:_("Cache Size (16 KiB Blocks):"),labelSeparator:"", +name:"cache_size",width:60,value:512}));a.bind("cache_expiry",b.add({fieldLabel:_("Cache Expiry (seconds):"),labelSeparator:"",name:"cache_expiry",width:60,value:60}))}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Daemon=Ext.extend(Ext.form.FormPanel,{border:!1,title:_("Daemon"),header:!1,layout:"form",initComponent:function(){Deluge.preferences.Daemon.superclass.initComponent.call(this);var a=deluge.preferences.getOptionsManager(),b=this.add({xtype:"fieldset",border:!1,title:_("Port"),autoHeight:!0,defaultType:"spinnerfield"});a.bind("daemon_port",b.add({fieldLabel:_("Daemon port:"),labelSeparator:"",name:"daemon_port",value:58846,decimalPrecision:0,minValue:-1,maxValue:99999}));b=this.add({xtype:"fieldset", +border:!1,title:_("Connections"),autoHeight:!0,labelWidth:1,defaultType:"checkbox"});a.bind("allow_remote",b.add({fieldLabel:"",height:22,labelSeparator:"",boxLabel:_("Allow Remote Connections"),name:"allow_remote"}));b=this.add({xtype:"fieldset",border:!1,title:_("Other"),autoHeight:!0,labelWidth:1,defaultType:"checkbox"});a.bind("new_release_check",b.add({fieldLabel:"",labelSeparator:"",height:40,boxLabel:_("Periodically check the website for new releases"),id:"new_release_check"}))}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Downloads=Ext.extend(Ext.FormPanel,{constructor:function(a){a=Ext.apply({border:!1,title:_("Downloads"),header:!1,layout:"form",autoHeight:!0,width:320},a);Deluge.preferences.Downloads.superclass.constructor.call(this,a)},initComponent:function(){Deluge.preferences.Downloads.superclass.initComponent.call(this);var a=deluge.preferences.getOptionsManager(),b=this.add({xtype:"fieldset",border:!1,title:_("Folders"),labelWidth:150,defaultType:"togglefield",autoHeight:!0,labelAlign:"top", +width:300,style:"margin-bottom: 5px; padding-bottom: 5px;"});a.bind("download_location",b.add({xtype:"textfield",name:"download_location",fieldLabel:_("Download to:"),labelSeparator:"",width:280}));var c=b.add({name:"move_completed_path",fieldLabel:_("Move completed to:"),labelSeparator:"",width:280});a.bind("move_completed",c.toggle);a.bind("move_completed_path",c.input);c=b.add({name:"torrentfiles_location",fieldLabel:_("Copy of .torrent files to:"),labelSeparator:"",width:280});a.bind("copy_torrent_file", +c.toggle);a.bind("torrentfiles_location",c.input);b=this.add({xtype:"fieldset",border:!1,title:_("Options"),autoHeight:!0,labelWidth:1,defaultType:"checkbox",style:"margin-bottom: 0; padding-bottom: 0;",width:280});a.bind("prioritize_first_last_pieces",b.add({name:"prioritize_first_last_pieces",labelSeparator:"",height:22,boxLabel:_("Prioritize first and last pieces of torrent")}));a.bind("sequential_download",b.add({name:"sequential_download",labelSeparator:"",height:22,boxLabel:_("Sequential download")})); +a.bind("add_paused",b.add({name:"add_paused",labelSeparator:"",height:22,boxLabel:_("Add torrents in Paused state")}));a.bind("pre_allocate_storage",b.add({name:"pre_allocate_storage",labelSeparator:"",height:22,boxLabel:_("Pre-allocate disk space")}))}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Encryption=Ext.extend(Ext.form.FormPanel,{border:!1,title:_("Encryption"),header:!1,initComponent:function(){Deluge.preferences.Encryption.superclass.initComponent.call(this);var a=deluge.preferences.getOptionsManager(),b=this.add({xtype:"fieldset",border:!1,title:_("Settings"),header:!1,autoHeight:!0,defaultType:"combo",width:300});a.bind("enc_in_policy",b.add({fieldLabel:_("Incoming:"),labelSeparator:"",mode:"local",width:150,store:new Ext.data.ArrayStore({fields:["id","text"], +data:[[0,_("Forced")],[1,_("Enabled")],[2,_("Disabled")]]}),editable:!1,triggerAction:"all",valueField:"id",displayField:"text"}));a.bind("enc_out_policy",b.add({fieldLabel:_("Outgoing:"),labelSeparator:"",mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("Forced")],[1,_("Enabled")],[2,_("Disabled")]]}),editable:!1,triggerAction:"all",valueField:"id",displayField:"text"}));a.bind("enc_level",b.add({fieldLabel:_("Level:"),labelSeparator:"",mode:"local",width:150, +store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("Handshake")],[1,_("Full Stream")],[2,_("Either")]]}),editable:!1,triggerAction:"all",valueField:"id",displayField:"text"}))}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.InstallPluginWindow=Ext.extend(Ext.Window,{title:_("Install Plugin"),layout:"fit",height:115,width:350,constrainHeader:!0,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closeAction:"hide",iconCls:"x-deluge-install-plugin",modal:!0,plain:!0,initComponent:function(){Deluge.preferences.InstallPluginWindow.superclass.initComponent.call(this);this.addButton(_("Install"),this.onInstall,this);this.form=this.add({xtype:"form",baseCls:"x-plain",labelWidth:70,autoHeight:!0,fileUpload:!0, +items:[{xtype:"fileuploadfield",width:240,emptyText:_("Select an egg"),fieldLabel:_("Plugin Egg"),name:"file",buttonCfg:{text:_("Browse...")}}]})},onInstall:function(a,b){this.form.getForm().submit({url:deluge.config.base+"upload",waitMsg:_("Uploading your plugin..."),success:this.onUploadSuccess,scope:this})},onUploadPlugin:function(a,b,c,d){this.fireEvent("pluginadded")},onUploadSuccess:function(a,b){this.hide();if(b.result.success){var c=this.form.getForm().getFieldValues().file,c=c.split("\\").slice(-1)[0], +d=b.result.files[0];this.form.getForm().setValues({file:""});deluge.client.web.upload_plugin(c,d,{success:this.onUploadPlugin,scope:this,filename:c})}}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Interface=Ext.extend(Ext.form.FormPanel,{border:!1,title:_("Interface"),header:!1,layout:"form",initComponent:function(){Deluge.preferences.Interface.superclass.initComponent.call(this);var a=this.optionsManager=new Deluge.OptionsManager;this.on("show",this.onPageShow,this);var b=this.add({xtype:"fieldset",border:!1,title:_("Interface"),style:"margin-bottom: 0px; padding-bottom: 5px; padding-top: 5px",autoHeight:!0,labelWidth:1,defaultType:"checkbox",defaults:{height:17,fieldLabel:"", +labelSeparator:""}});a.bind("show_session_speed",b.add({name:"show_session_speed",boxLabel:_("Show session speed in titlebar")}));a.bind("sidebar_show_zero",b.add({name:"sidebar_show_zero",boxLabel:_("Show filters with zero torrents")}));a.bind("sidebar_multiple_filters",b.add({name:"sidebar_multiple_filters",boxLabel:_("Allow the use of multiple filters at once")}));b=this.add({xtype:"fieldset",border:!1,title:_("Language"),style:"margin-bottom: 0px; padding-bottom: 5px; padding-top: 5px",autoHeight:!0, +labelWidth:1,defaultType:"checkbox"});this.language=a.bind("language",b.add({xtype:"combo",labelSeparator:"",name:"language",mode:"local",width:200,store:new Ext.data.ArrayStore({fields:["id","text"]}),editable:!1,triggerAction:"all",valueField:"id",displayField:"text"}));b=this.add({xtype:"fieldset",border:!1,title:_("WebUI Password"),style:"margin-bottom: 0px; padding-bottom: 5px; padding-top: 5px",autoHeight:!0,labelWidth:100,defaultType:"textfield",defaults:{width:100,inputType:"password",labelStyle:"padding-left: 5px", +height:20,labelSeparator:""}});this.oldPassword=b.add({name:"old_password",fieldLabel:_("Old:")});this.newPassword=b.add({name:"new_password",fieldLabel:_("New:")});this.confirmPassword=b.add({name:"confirm_password",fieldLabel:_("Confirm:")});b=this.add({xtype:"fieldset",border:!1,title:_("Server"),style:"padding-top: 5px; margin-bottom: 0px; padding-bottom: 5px",autoHeight:!0,labelWidth:100,defaultType:"spinnerfield",defaults:{labelSeparator:"",labelStyle:"padding-left: 5px",height:20,width:80}}); +a.bind("session_timeout",b.add({name:"session_timeout",fieldLabel:_("Session Timeout:"),decimalPrecision:0,minValue:-1,maxValue:99999}));a.bind("port",b.add({name:"port",fieldLabel:_("Port:"),decimalPrecision:0,minValue:1,maxValue:65535}));this.httpsField=a.bind("https",b.add({xtype:"checkbox",name:"https",hideLabel:!0,width:300,style:"margin-left: 5px",boxLabel:_("Enable SSL (paths relative to Deluge config folder)")}));this.httpsField.on("check",this.onSSLCheck,this);this.pkeyField=a.bind("pkey", +b.add({xtype:"textfield",disabled:!0,name:"pkey",width:180,fieldLabel:_("Private Key:")}));this.certField=a.bind("cert",b.add({xtype:"textfield",disabled:!0,name:"cert",width:180,fieldLabel:_("Certificate:")}))},onApply:function(){var a=this.optionsManager.getDirty();if(!Ext.isObjectEmpty(a)){deluge.client.web.set_config(a,{success:this.onSetConfig,scope:this});for(var b in deluge.config)deluge.config[b]=this.optionsManager.get(b);"language"in a&&Ext.Msg.show({title:_("WebUI Language Changed"),msg:_("Do you want to refresh the page now to use the new language?"), +buttons:{yes:_("Refresh"),no:_("Close")},multiline:!1,fn:function(a){"yes"===a&&location.reload()},icon:Ext.MessageBox.QUESTION})}if(this.oldPassword.getValue()||this.newPassword.getValue())this.onPasswordChange()},onOk:function(){this.onApply()},onGotConfig:function(a){this.optionsManager.set(a)},onGotLanguages:function(a,b,c,d){a.unshift(["",_("System Default")]);this.language.store.loadData(a);this.language.setValue(this.optionsManager.get("language"))},onPasswordChange:function(){var a=this.newPassword.getValue(); +if(a!=this.confirmPassword.getValue())Ext.MessageBox.show({title:_("Invalid Password"),msg:_("Your passwords don't match!"),buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});else{var b=this.oldPassword.getValue();deluge.client.auth.change_password(b,a,{success:function(a){a?(Ext.MessageBox.show({title:_("Change Successful"),msg:_("Your password was successfully changed!"),buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.INFO,iconCls:"x-deluge-icon-info"}), +this.oldPassword.setValue(""),this.newPassword.setValue(""),this.confirmPassword.setValue("")):(Ext.MessageBox.show({title:_("Password"),msg:_("Your old password was incorrect!"),buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"}),this.oldPassword.setValue(""))},scope:this})}},onSetConfig:function(){this.optionsManager.commit()},onPageShow:function(){deluge.client.web.get_config({success:this.onGotConfig,scope:this});deluge.client.webutils.get_languages({success:this.onGotLanguages, +scope:this})},onSSLCheck:function(a,b){this.pkeyField.setDisabled(!b);this.certField.setDisabled(!b)}});Ext.namespace("Deluge.preferences");Ext.apply(Ext.form.VTypes,{IPAddress:function(a){return/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(a)},IPAddressText:"Must be a numeric IP address",IPAddressMask:/[\d\.]/i}); +Deluge.preferences.Network=Ext.extend(Ext.form.FormPanel,{border:!1,layout:"form",title:_("Network"),header:!1,initComponent:function(){Deluge.preferences.Network.superclass.initComponent.call(this);var a=deluge.preferences.getOptionsManager(),b=this.add({xtype:"fieldset",border:!1,title:_("Incoming Address"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:!0,labelWidth:1,defaultType:"textfield"});a.bind("listen_interface",b.add({name:"listen_interface",fieldLabel:"",labelSeparator:"", +width:200,vtype:"IPAddress"}));var b=this.add({xtype:"fieldset",border:!1,title:_("Incoming Port"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:!0,labelWidth:1,defaultType:"checkbox"});a.bind("random_port",b.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Use Random Port"),name:"random_port",height:22,listeners:{check:{fn:function(a,b){this.listenPort.setDisabled(b)},scope:this}}}));this.listenPort=b.add({xtype:"spinnerfield",name:"listen_port",fieldLabel:"",labelSeparator:"",width:75, +strategy:{xtype:"number",decimalPrecision:0,minValue:0,maxValue:65535}});a.bind("listen_ports",this.listenPort);b=this.add({xtype:"fieldset",border:!1,title:_("Outgoing Interface"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:!0,labelWidth:1,defaultType:"textfield"});a.bind("outgoing_interface",b.add({name:"outgoing_interface",fieldLabel:"",labelSeparator:"",width:40}));b=this.add({xtype:"fieldset",border:!1,title:_("Outgoing Ports"),style:"margin-bottom: 5px; padding-bottom: 0px;", +autoHeight:!0,labelWidth:1,defaultType:"checkbox"});a.bind("random_outgoing_ports",b.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Use Random Ports"),name:"random_outgoing_ports",height:22,listeners:{check:{fn:function(a,b){this.outgoingPorts.setDisabled(b)},scope:this}}}));this.outgoingPorts=b.add({xtype:"spinnergroup",name:"outgoing_ports",fieldLabel:"",labelSeparator:"",colCfg:{labelWidth:40,style:"margin-right: 10px;"},items:[{fieldLabel:_("From:"),labelSeparator:"",strategy:{xtype:"number", +decimalPrecision:0,minValue:0,maxValue:65535}},{fieldLabel:_("To:"),labelSeparator:"",strategy:{xtype:"number",decimalPrecision:0,minValue:0,maxValue:65535}}]});a.bind("outgoing_ports",this.outgoingPorts);b=this.add({xtype:"fieldset",border:!1,title:_("Network Extras"),autoHeight:!0,layout:"table",layoutConfig:{columns:3},defaultType:"checkbox"});a.bind("upnp",b.add({fieldLabel:"",labelSeparator:"",boxLabel:_("UPnP"),name:"upnp"}));a.bind("natpmp",b.add({fieldLabel:"",labelSeparator:"",boxLabel:_("NAT-PMP"), +ctCls:"x-deluge-indent-checkbox",name:"natpmp"}));a.bind("utpex",b.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Peer Exchange"),ctCls:"x-deluge-indent-checkbox",name:"utpex"}));a.bind("lsd",b.add({fieldLabel:"",labelSeparator:"",boxLabel:_("LSD"),name:"lsd"}));a.bind("dht",b.add({fieldLabel:"",labelSeparator:"",boxLabel:_("DHT"),ctCls:"x-deluge-indent-checkbox",name:"dht"}));b=this.add({xtype:"fieldset",border:!1,title:_("Type Of Service"),style:"margin-bottom: 5px; padding-bottom: 0px;",bodyStyle:"margin: 0px; padding: 0px", +autoHeight:!0,defaultType:"textfield"});a.bind("peer_tos",b.add({name:"peer_tos",fieldLabel:_("Peer TOS Byte:"),labelSeparator:"",width:40}))}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Other=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:!1,title:_("Other"),header:!1,layout:"form"},a);Deluge.preferences.Other.superclass.constructor.call(this,a)},initComponent:function(){Deluge.preferences.Other.superclass.initComponent.call(this);var a=deluge.preferences.getOptionsManager(),b=this.add({xtype:"fieldset",border:!1,title:_("Updates"),autoHeight:!0,labelWidth:1,defaultType:"checkbox"});a.bind("new_release_check",b.add({fieldLabel:"",labelSeparator:"", +height:22,name:"new_release_check",boxLabel:_("Be alerted about new releases")}));b=this.add({xtype:"fieldset",border:!1,title:_("System Information"),autoHeight:!0,labelWidth:1,defaultType:"checkbox"});b.add({xtype:"panel",border:!1,bodyCfg:{html:_("Help us improve Deluge by sending us your Python version, PyGTK version, OS and processor types. Absolutely no other information is sent.")}});a.bind("send_info",b.add({fieldLabel:"",labelSeparator:"",height:22,boxLabel:_("Yes, please send anonymous statistics"), +name:"send_info"}));b=this.add({xtype:"fieldset",border:!1,title:_("GeoIP Database"),autoHeight:!0,labelWidth:80,defaultType:"textfield"});a.bind("geoip_db_location",b.add({name:"geoip_db_location",fieldLabel:_("Path:"),labelSeparator:"",width:200}))}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Plugins=Ext.extend(Ext.Panel,{layout:"border",title:_("Plugins"),header:!1,border:!1,cls:"x-deluge-plugins",pluginTemplate:new Ext.Template('<dl class="singleline"><dt>'+_("Author:")+"</dt><dd>{author}</dd><dt>"+_("Version:")+"</dt><dd>{version}</dd><dt>"+_("Author Email:")+"</dt><dd>{email}</dd><dt>"+_("Homepage:")+"</dt><dd>{homepage}</dd><dt>"+_("Details:")+'</dt><dd style="white-space:normal">{details}</dd></dl>'),initComponent:function(){Deluge.preferences.Plugins.superclass.initComponent.call(this); +this.defaultValues={version:"",email:"",homepage:"",details:""};this.pluginTemplate.compile();this.list=this.add({xtype:"listview",store:new Ext.data.ArrayStore({fields:[{name:"enabled",mapping:0},{name:"plugin",mapping:1,sortType:"asUCString"}]}),columns:[{id:"enabled",header:_("Enabled"),width:0.2,sortable:!0,tpl:new Ext.XTemplate("{enabled:this.getCheckbox}",{getCheckbox:function(a){return'<div class="x-grid3-check-col'+(a?"-on":"")+'" rel="chkbox"> </div>'}}),dataIndex:"enabled"},{id:"plugin", +header:_("Plugin"),width:0.8,sortable:!0,dataIndex:"plugin"}],singleSelect:!0,autoExpandColumn:"plugin",listeners:{selectionchange:{fn:this.onPluginSelect,scope:this}}});this.panel=this.add({region:"center",autoScroll:!0,items:[this.list],bbar:new Ext.Toolbar({items:[{cls:"x-btn-text-icon",iconCls:"x-deluge-install-plugin",text:_("Install"),handler:this.onInstallPluginWindow,scope:this},"->",{cls:"x-btn-text-icon",text:_("Find More"),iconCls:"x-deluge-find-more",handler:this.onFindMorePlugins,scope:this}]})}); +this.pluginInfo=this.add({xtype:"panel",border:!1,height:100,region:"south",padding:"5",autoScroll:!0,bodyCfg:{style:"white-space: nowrap"}});this.pluginInfo.on("render",this.onPluginInfoRender,this);this.list.on("click",this.onNodeClick,this);deluge.preferences.on("show",this.onPreferencesShow,this);deluge.events.on("PluginDisabledEvent",this.onPluginDisabled,this);deluge.events.on("PluginEnabledEvent",this.onPluginEnabled,this)},disablePlugin:function(a){deluge.client.core.disable_plugin(a)},enablePlugin:function(a){deluge.client.core.enable_plugin(a)}, +setInfo:function(a){this.pluginInfo.rendered&&(this.pluginInfo.body.dom.innerHTML=this.pluginTemplate.apply(a||this.defaultValues))},updatePlugins:function(){var a=function(a){this.enabledPlugins=a;this.onGotPlugins()};deluge.client.core.get_available_plugins({success:function(b){this.availablePlugins=b.sort(function(a,b){return a.toLowerCase().localeCompare(b.toLowerCase())});deluge.client.core.get_enabled_plugins({success:a,scope:this})},scope:this})},updatePluginsGrid:function(){var a=[];Ext.each(this.availablePlugins, +function(b){-1<this.enabledPlugins.indexOf(b)?a.push([!0,b]):a.push([!1,b])},this);this.list.getStore().loadData(a)},onNodeClick:function(a,b,c,d){"chkbox"==(new Ext.Element(d.target)).getAttribute("rel")&&(a=a.getStore().getAt(b),"WebUi"!=a.get("plugin")&&(a.set("enabled",!a.get("enabled")),a.commit(),a.get("enabled")?this.enablePlugin(a.get("plugin")):this.disablePlugin(a.get("plugin"))))},onFindMorePlugins:function(){window.open("http://dev.deluge-torrent.org/wiki/Plugins")},onGotPlugins:function(){this.setInfo(); +this.updatePluginsGrid()},onGotPluginInfo:function(a){this.setInfo({author:a.Author,version:a.Version,email:a["Author-email"],homepage:a["Home-page"],details:a.Description});delete a},onInstallPluginWindow:function(){this.installWindow||(this.installWindow=new Deluge.preferences.InstallPluginWindow,this.installWindow.on("pluginadded",this.onPluginInstall,this));this.installWindow.show()},onPluginEnabled:function(a){a=this.list.getStore().find("plugin",a);-1!=a&&(a=this.list.getStore().getAt(a),a.set("enabled", +!0),a.commit())},onPluginDisabled:function(a){a=this.list.getStore().find("plugin",a);-1!=a&&(a=this.list.getStore().getAt(a),a.set("enabled",!1),a.commit())},onPluginInstall:function(){this.updatePlugins()},onPluginSelect:function(a,b){if(0!=b.length){var c=a.getRecords(b)[0];deluge.client.web.get_plugin_info(c.get("plugin"),{success:this.onGotPluginInfo,scope:this})}},onPreferencesShow:function(){this.updatePlugins()},onPluginInfoRender:function(a,b){this.setInfo()}});Ext.namespace("Deluge.preferences"); +PreferencesRecord=Ext.data.Record.create([{name:"name",type:"string"}]); +Deluge.preferences.PreferencesWindow=Ext.extend(Ext.Window,{currentPage:null,title:_("Preferences"),layout:"border",width:485,height:500,border:!1,constrainHeader:!0,buttonAlign:"right",closeAction:"hide",closable:!0,iconCls:"x-deluge-preferences",plain:!0,resizable:!1,pages:{},initComponent:function(){Deluge.preferences.PreferencesWindow.superclass.initComponent.call(this);this.list=new Ext.list.ListView({store:new Ext.data.Store,columns:[{id:"name",dataIndex:"name"}],singleSelect:!0,listeners:{selectionchange:{fn:this.onPageSelect, +scope:this}},hideHeaders:!0,autoExpandColumn:"name",deferredRender:!1,autoScroll:!0,collapsible:!0});this.add({region:"west",items:[this.list],width:120,margins:"0 5 0 0",cmargins:"0 5 0 0"});this.configPanel=this.add({type:"container",autoDestroy:!1,region:"center",layout:"card",layoutConfig:{deferredRender:!0},autoScroll:!0,width:300});this.addButton(_("Close"),this.onClose,this);this.addButton(_("Apply"),this.onApply,this);this.addButton(_("OK"),this.onOk,this);this.optionsManager=new Deluge.OptionsManager; +this.on("afterrender",this.onAfterRender,this);this.on("show",this.onShow,this);this.initPages()},initPages:function(){deluge.preferences=this;this.addPage(new Deluge.preferences.Downloads);this.addPage(new Deluge.preferences.Network);this.addPage(new Deluge.preferences.Encryption);this.addPage(new Deluge.preferences.Bandwidth);this.addPage(new Deluge.preferences.Interface);this.addPage(new Deluge.preferences.Other);this.addPage(new Deluge.preferences.Daemon);this.addPage(new Deluge.preferences.Queue); +this.addPage(new Deluge.preferences.Proxy);this.addPage(new Deluge.preferences.Cache);this.addPage(new Deluge.preferences.Plugins)},onApply:function(a){a=this.optionsManager.getDirty();Ext.isObjectEmpty(a)||("listen_ports"in a&&(a.listen_ports=[a.listen_ports,a.listen_ports]),deluge.client.core.set_config(a,{success:this.onSetConfig,scope:this}));for(var b in this.pages)if(this.pages[b].onApply)this.pages[b].onApply()},getOptionsManager:function(){return this.optionsManager},addPage:function(a){var b= +this.list.getStore(),c=a.title;b.add([new PreferencesRecord({name:c})]);a.bodyStyle="padding: 5px";a.preferences=this;this.pages[c]=this.configPanel.add(a);this.pages[c].index=-1;return this.pages[c]},removePage:function(a){var b=a.title,c=this.list.getStore();c.removeAt(c.find("name",b));this.configPanel.remove(a);delete this.pages[a.title]},selectPage:function(a){0>this.pages[a].index&&(this.pages[a].index=this.configPanel.items.indexOf(this.pages[a]));this.list.select(this.pages[a].index)},doSelectPage:function(a){0> +this.pages[a].index&&(this.pages[a].index=this.configPanel.items.indexOf(this.pages[a]));this.configPanel.getLayout().setActiveItem(this.pages[a].index);this.currentPage=a},onGotConfig:function(a){this.getOptionsManager().set(a)},onPageSelect:function(a,b){var c=a.getRecord(b[0]);this.doSelectPage(c.get("name"))},onSetConfig:function(){this.getOptionsManager().commit()},onAfterRender:function(){this.list.getSelectionCount()||this.list.select(0);this.configPanel.getLayout().setActiveItem(0)},onShow:function(){deluge.client.core&& +deluge.client.core.get_config({success:this.onGotConfig,scope:this})},onClose:function(){this.hide()},onOk:function(){var a=this.optionsManager.getDirty();Ext.isObjectEmpty(a)||deluge.client.core.set_config(a,{success:this.onSetConfig,scope:this});for(var b in this.pages)if(this.pages[b].onOk)this.pages[b].onOk();this.hide()}});Ext.ns("Deluge.preferences"); +Deluge.preferences.ProxyField=Ext.extend(Ext.form.FieldSet,{border:!1,autoHeight:!0,labelWidth:70,initComponent:function(){Deluge.preferences.ProxyField.superclass.initComponent.call(this);this.proxyType=this.add({xtype:"combo",fieldLabel:_("Type:"),labelSeparator:"",name:"proxytype",mode:"local",width:150,store:new Ext.data.ArrayStore({fields:["id","text"],data:[[0,_("None")],[1,_("Socks4")],[2,_("Socks5")],[3,_("Socks5 Auth")],[4,_("HTTP")],[5,_("HTTP Auth")],[6,_("I2P")]]}),editable:!1,triggerAction:"all", +valueField:"id",displayField:"text"});this.proxyType.on("change",this.onFieldChange,this);this.proxyType.on("select",this.onTypeSelect,this);this.hostname=this.add({xtype:"textfield",name:"hostname",fieldLabel:_("Host:"),labelSeparator:"",width:220});this.hostname.on("change",this.onFieldChange,this);this.port=this.add({xtype:"spinnerfield",name:"port",fieldLabel:_("Port:"),labelSeparator:"",width:80,decimalPrecision:0,minValue:0,maxValue:65535});this.port.on("change",this.onFieldChange,this);this.username= +this.add({xtype:"textfield",name:"username",fieldLabel:_("Username:"),labelSeparator:"",width:220});this.username.on("change",this.onFieldChange,this);this.password=this.add({xtype:"textfield",name:"password",fieldLabel:_("Password:"),labelSeparator:"",inputType:"password",width:220});this.password.on("change",this.onFieldChange,this);this.proxy_host_resolve=this.add({xtype:"checkbox",name:"proxy_host_resolve",fieldLabel:"",boxLabel:_("Proxy Hostnames"),width:220});this.proxy_host_resolve.on("change", +this.onFieldChange,this);this.proxy_peer_conn=this.add({xtype:"checkbox",name:"proxy_peer_conn",fieldLabel:"",boxLabel:_("Proxy Peers"),width:220});this.proxy_peer_conn.on("change",this.onFieldChange,this);this.proxy_tracker_conn=this.add({xtype:"checkbox",name:"proxy_tracker_conn",fieldLabel:"",boxLabel:_("Proxy Trackers"),width:220});this.proxy_tracker_conn.on("change",this.onFieldChange,this);var a=this.add({xtype:"fieldset",border:!1,title:_("Force Proxy"),autoHeight:!0,labelWidth:1,defaultType:"checkbox", +style:"padding-left: 0px; margin-top: 10px"});this.force_proxy=a.add({fieldLabel:"",labelSeparator:"",height:20,name:"force_proxy",boxLabel:_("Force Use of Proxy")});this.force_proxy.on("change",this.onFieldChange,this);this.anonymous_mode=a.add({fieldLabel:"",labelSeparator:"",height:20,name:"anonymous_mode",boxLabel:_("Hide Client Identity")});this.anonymous_mode.on("change",this.onFieldChange,this);this.setting=!1},getName:function(){return this.initialConfig.name},getValue:function(){return{type:this.proxyType.getValue(), +hostname:this.hostname.getValue(),port:Number(this.port.getValue()),username:this.username.getValue(),password:this.password.getValue(),proxy_hostnames:this.proxy_host_resolve.getValue(),proxy_peer_connections:this.proxy_peer_conn.getValue(),proxy_tracker_connections:this.proxy_tracker_conn.getValue(),force_proxy:this.force_proxy.getValue(),anonymous_mode:this.anonymous_mode.getValue()}},setValue:function(a){this.setting=!0;this.proxyType.setValue(a.type);var b=this.proxyType.getStore().find("id", +a.type),c=this.proxyType.getStore().getAt(b);this.hostname.setValue(a.hostname);this.port.setValue(a.port);this.username.setValue(a.username);this.password.setValue(a.password);this.proxy_host_resolve.setValue(a.proxy_hostnames);this.proxy_peer_conn.setValue(a.proxy_peer_connections);this.proxy_tracker_conn.setValue(a.proxy_tracker_connections);this.force_proxy.setValue(a.force_proxy);this.anonymous_mode.setValue(a.anonymous_mode);this.onTypeSelect(this.type,c,b);this.setting=!1},onFieldChange:function(a, +b,c){if(!this.setting){b=this.getValue();var d=Ext.apply({},b);d[a.getName()]=c;this.fireEvent("change",this,b,d)}},onTypeSelect:function(a,b,c){a=b.get("id");0<a?(this.hostname.show(),this.port.show(),this.proxy_peer_conn.show(),this.proxy_tracker_conn.show(),1<a&&6>a?this.proxy_host_resolve.show():this.proxy_host_resolve.hide()):(this.hostname.hide(),this.port.hide(),this.proxy_host_resolve.hide(),this.proxy_peer_conn.hide(),this.proxy_tracker_conn.hide());3==a||5==a?(this.username.show(),this.password.show()): +(this.username.hide(),this.password.hide())}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Proxy=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:!1,title:_("Proxy"),header:!1,layout:"form",autoScroll:!0},a);Deluge.preferences.Proxy.superclass.constructor.call(this,a)},initComponent:function(){Deluge.preferences.Proxy.superclass.initComponent.call(this);this.proxy=this.add(new Deluge.preferences.ProxyField({title:_("Proxy"),name:"proxy"}));this.proxy.on("change",this.onProxyChange,this);deluge.preferences.getOptionsManager().bind("proxy",this.proxy)}, +getValue:function(){return{proxy:this.proxy.getValue()}},setValue:function(a){for(var b in a)this[b].setValue(a[b])},onProxyChange:function(a,b,c){b=this.getValue();var d=Ext.apply({},b);d[a.getName()]=c;this.fireEvent("change",this,b,d)}});Ext.namespace("Deluge.preferences"); +Deluge.preferences.Queue=Ext.extend(Ext.form.FormPanel,{border:!1,title:_("Queue"),header:!1,layout:"form",initComponent:function(){Deluge.preferences.Queue.superclass.initComponent.call(this);var a=deluge.preferences.getOptionsManager(),b=this.add({xtype:"fieldset",border:!1,title:_("New Torrents"),style:"padding-top: 5px; margin-bottom: 0px;",autoHeight:!0,labelWidth:1,defaultType:"checkbox"});a.bind("queue_new_to_top",b.add({fieldLabel:"",labelSeparator:"",height:22,boxLabel:_("Queue to top"), +name:"queue_new_to_top"}));b=this.add({xtype:"fieldset",border:!1,title:_("Active Torrents"),autoHeight:!0,labelWidth:150,defaultType:"spinnerfield",style:"padding-top: 5px; margin-bottom: 0px"});a.bind("max_active_limit",b.add({fieldLabel:_("Total:"),labelSeparator:"",name:"max_active_limit",value:8,width:80,decimalPrecision:0,minValue:-1,maxValue:99999}));a.bind("max_active_downloading",b.add({fieldLabel:_("Downloading:"),labelSeparator:"",name:"max_active_downloading",value:3,width:80,decimalPrecision:0, +minValue:-1,maxValue:99999}));a.bind("max_active_seeding",b.add({fieldLabel:_("Seeding:"),labelSeparator:"",name:"max_active_seeding",value:5,width:80,decimalPrecision:0,minValue:-1,maxValue:99999}));a.bind("dont_count_slow_torrents",b.add({xtype:"checkbox",name:"dont_count_slow_torrents",height:22,hideLabel:!0,boxLabel:_("Ignore slow torrents")}));a.bind("auto_manage_prefer_seeds",b.add({xtype:"checkbox",name:"auto_manage_prefer_seeds",hideLabel:!0,boxLabel:_("Prefer seeding torrents")}));b=this.add({xtype:"fieldset", +border:!1,title:_("Seeding Rotation"),autoHeight:!0,labelWidth:150,defaultType:"spinnerfield",style:"padding-top: 5px; margin-bottom: 0px"});a.bind("share_ratio_limit",b.add({fieldLabel:_("Share Ratio:"),labelSeparator:"",name:"share_ratio_limit",value:8,width:80,incrementValue:0.1,minValue:-1,maxValue:99999,alternateIncrementValue:1,decimalPrecision:2}));a.bind("seed_time_ratio_limit",b.add({fieldLabel:_("Time Ratio:"),labelSeparator:"",name:"seed_time_ratio_limit",value:3,width:80,incrementValue:0.1, +minValue:-1,maxValue:99999,alternateIncrementValue:1,decimalPrecision:2}));a.bind("seed_time_limit",b.add({fieldLabel:_("Time (m):"),labelSeparator:"",name:"seed_time_limit",value:5,width:80,decimalPrecision:0,minValue:-1,maxValue:99999}));b=this.add({xtype:"fieldset",border:!1,autoHeight:!0,style:"padding-top: 5px; margin-bottom: 0px",title:_("Share Ratio Reached"),layout:"table",layoutConfig:{columns:2},labelWidth:0,defaultType:"checkbox",defaults:{fieldLabel:"",labelSeparator:""}});this.stopAtRatio= +b.add({name:"stop_seed_at_ratio",boxLabel:_("Share Ratio:")});this.stopAtRatio.on("check",this.onStopRatioCheck,this);a.bind("stop_seed_at_ratio",this.stopAtRatio);this.stopRatio=b.add({xtype:"spinnerfield",name:"stop_seed_ratio",ctCls:"x-deluge-indent-checkbox",disabled:!0,value:"2.0",width:60,incrementValue:0.1,minValue:-1,maxValue:99999,alternateIncrementValue:1,decimalPrecision:2});a.bind("stop_seed_ratio",this.stopRatio);this.removeAtRatio=b.add({xtype:"radiogroup",columns:1,colspan:2,disabled:!0, +style:"margin-left: 10px",items:[{boxLabel:_("Pause torrent"),name:"at_ratio",inputValue:!1,checked:!0},{boxLabel:_("Remove torrent"),name:"at_ratio",inputValue:!0}]});a.bind("remove_seed_at_ratio",this.removeAtRatio)},onStopRatioCheck:function(a,b){this.stopRatio.setDisabled(!b);this.removeAtRatio.setDisabled(!b)}});Ext.ns("Deluge"); +Deluge.StatusbarMenu=Ext.extend(Ext.menu.Menu,{initComponent:function(){Deluge.StatusbarMenu.superclass.initComponent.call(this);this.otherWin=new Deluge.OtherLimitWindow(this.initialConfig.otherWin||{});this.items.each(function(a){if("menucheckitem"==a.getXType())if("other"==a.value)a.on("click",this.onOtherClicked,this);else a.on("checkchange",this.onLimitChanged,this)},this)},setValue:function(a){var b=!1;this.value=a=0==a?-1:a;var c=null;this.items.each(function(d){d.setChecked&&(d.suspendEvents(), +d.value==a?(d.setChecked(!0),b=!0):d.setChecked(!1),d.resumeEvents());"other"==d.value&&(c=d)});b||(c.suspendEvents(),c.setChecked(!0),c.resumeEvents())},onLimitChanged:function(a,b){if(b&&"other"!=a.value){var c={};c[a.group]=a.value;deluge.client.core.set_config(c,{success:function(){deluge.ui.update()}})}},onOtherClicked:function(a,b){this.otherWin.group=a.group;this.otherWin.setValue(this.value);this.otherWin.show()}});Ext.namespace("Deluge"); +Deluge.OptionsManager=Ext.extend(Ext.util.Observable,{constructor:function(a){a=a||{};this.binds={};this.changed={};this.options=a&&a.options||{};this.focused=null;this.addEvents({add:!0,changed:!0,reset:!0});this.on("changed",this.onChange,this);Deluge.OptionsManager.superclass.constructor.call(this)},addOptions:function(a){this.options=Ext.applyIf(this.options,a)},bind:function(a,b){this.binds[a]=this.binds[a]||[];this.binds[a].push(b);b._doption=a;b.on("focus",this.onFieldFocus,this);b.on("blur", +this.onFieldBlur,this);b.on("change",this.onFieldChange,this);b.on("check",this.onFieldChange,this);b.on("spin",this.onFieldChange,this);return b},commit:function(){this.options=Ext.apply(this.options,this.changed);this.reset()},convertValueType:function(a,b){if(Ext.type(a)!=Ext.type(b))switch(Ext.type(a)){case "string":b=String(b);break;case "number":b=Number(b);break;case "boolean":"string"==Ext.type(b)?(b=b.toLowerCase(),b="true"==b||"1"==b||"on"==b?!0:!1):b=Boolean(b)}return b},get:function(){if(1== +arguments.length){var a=arguments[0];return this.isDirty(a)?this.changed[a]:this.options[a]}var b={};Ext.each(arguments,function(a){this.has(a)&&(b[a]=this.isDirty(a)?this.changed[a]:this.options[a])},this);return b},getDefault:function(a){return this.options[a]},getDirty:function(){return this.changed},isDirty:function(a){return!Ext.isEmpty(this.changed[a])},has:function(a){return this.options[a]},reset:function(){this.changed={}},set:function(a,b){if(void 0!==a)if("object"==typeof a){var c=a;this.options= +Ext.apply(this.options,c);for(a in c)this.onChange(a,c[a])}else this.options[a]=b,this.onChange(a,b)},update:function(a,b){if(void 0!==a)if(void 0===b)for(var c in a)this.update(c,a[c]);else{c=this.getDefault(a);b=this.convertValueType(c,b);var d=this.get(a);d!=b&&(c==b?this.isDirty(a)&&delete this.changed[a]:this.changed[a]=b,this.fireEvent("changed",a,b,d))}},onFieldBlur:function(a,b){this.focused==a&&(this.focused=null)},onFieldChange:function(a,b){a.field&&(a=a.field);this.update(a._doption,a.getValue())}, +onFieldFocus:function(a,b){this.focused=a},onChange:function(a,b,c){Ext.isEmpty(this.binds[a])||Ext.each(this.binds[a],function(a){a!=this.focused&&a.setValue(b)},this)}});Ext.namespace("Deluge.about"); +Deluge.about.AboutWindow=Ext.extend(Ext.Window,{id:"AboutWindow",title:_("About Deluge"),height:330,width:270,iconCls:"x-deluge-main-panel",resizable:!1,plain:!0,layout:{type:"vbox",align:"center"},buttonAlign:"center",initComponent:function(){Deluge.about.AboutWindow.superclass.initComponent.call(this);this.addEvents({build_ready:!0});var a=this,b=function(){deluge.client.core.get_libtorrent_version({success:function(b){d+="<br/>"+_("libtorrent:")+" "+b;Ext.getCmp("about_comment").setText(d,!1); +a.fireEvent("build_ready")}})},c=deluge.version,d=_("A peer-to-peer file sharing program\nutilizing the BitTorrent protocol.").replace("\n","<br/>")+"<br/><br/>"+_("Client:")+" "+c+"<br/>";deluge.client.web.connected({success:function(a){a?deluge.client.daemon.get_version({success:function(a){d+=_("Server:")+" "+a+"<br/>";b()}}):this.fireEvent("build_ready")},failure:function(){this.fireEvent("build_ready")},scope:this});this.add([{xtype:"box",style:"padding-top: 5px",height:80,width:240,cls:"x-deluge-logo", +hideLabel:!0},{xtype:"label",style:"padding-top: 10px; font-weight: bold; font-size: 16px;",text:_("Deluge")+" "+c},{xtype:"label",id:"about_comment",style:"padding-top: 10px; text-align:center; font-size: 12px;",html:d},{xtype:"label",style:"padding-top: 10px; font-size: 10px;",text:_("Copyright 2007-2018 Deluge Team")},{xtype:"label",style:"padding-top: 5px; font-size: 12px;",html:'<a href="https://deluge-torrent.org" target="_blank">deluge-torrent.org</a>'}]);this.addButton(_("Close"),this.onCloseClick, +this)},show:function(){this.on("build_ready",function(){Deluge.about.AboutWindow.superclass.show.call(this)})},onCloseClick:function(){this.close()}});Ext.namespace("Deluge");Deluge.About=function(){(new Deluge.about.AboutWindow).show()};Ext.ns("Deluge"); +Deluge.AddConnectionWindow=Ext.extend(Ext.Window,{title:_("Add Connection"),iconCls:"x-deluge-add-window-icon",layout:"fit",width:300,height:195,constrainHeader:!0,bodyStyle:"padding: 10px 5px;",closeAction:"hide",initComponent:function(){Deluge.AddConnectionWindow.superclass.initComponent.call(this);this.addEvents("hostadded");this.addButton(_("Close"),this.hide,this);this.addButton(_("Add"),this.onAddClick,this);this.on("hide",this.onHide,this);this.form=this.add({xtype:"form",defaultType:"textfield", +baseCls:"x-plain",labelWidth:60,items:[{fieldLabel:_("Host:"),labelSeparator:"",name:"host",anchor:"75%",value:""},{xtype:"spinnerfield",fieldLabel:_("Port:"),labelSeparator:"",name:"port",strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:65535},value:"58846",anchor:"40%"},{fieldLabel:_("Username:"),labelSeparator:"",name:"username",anchor:"75%",value:""},{fieldLabel:_("Password:"),labelSeparator:"",anchor:"75%",name:"password",inputType:"password",value:""}]})},onAddClick:function(){var a= +this.form.getForm().getValues();deluge.client.web.add_host(a.host,Number(a.port),a.username,a.password,{success:function(a){a[0]?this.fireEvent("hostadded"):Ext.MessageBox.show({title:_("Error"),msg:String.format(_("Unable to add host: {0}"),a[1]),buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});this.hide()},scope:this})},onHide:function(){this.form.getForm().reset()}});Ext.ns("Deluge");var trackerUrlTest=/(((^https?)|(^udp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i; +Ext.apply(Ext.form.VTypes,{trackerUrl:function(a,b){return trackerUrlTest.test(a)},trackerUrlText:"Not a valid tracker url"}); +Deluge.AddTrackerWindow=Ext.extend(Ext.Window,{title:_("Add Tracker"),layout:"fit",width:375,height:150,plain:!0,closable:!0,resizable:!1,constrainHeader:!0,bodyStyle:"padding: 5px",buttonAlign:"right",closeAction:"hide",iconCls:"x-deluge-edit-trackers",initComponent:function(){Deluge.AddTrackerWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancelClick,this);this.addButton(_("Add"),this.onAddClick,this);this.addEvents("add");this.form=this.add({xtype:"form",defaultType:"textarea", +baseCls:"x-plain",labelWidth:55,items:[{fieldLabel:_("Trackers:"),labelSeparator:"",name:"trackers",anchor:"100%"}]})},onAddClick:function(){var a=this.form.getForm().findField("trackers").getValue(),a=a.split("\n"),b=[];Ext.each(a,function(a){Ext.form.VTypes.trackerUrl(a)&&b.push(a)},this);this.fireEvent("add",b);this.hide();this.form.getForm().findField("trackers").setValue("")},onCancelClick:function(){this.form.getForm().findField("trackers").setValue("");this.hide()}});Ext.namespace("Ext.ux.util"); +Ext.ux.util.RpcClient=Ext.extend(Ext.util.Observable,{_components:[],_methods:[],_requests:{},_url:null,_optionKeys:["scope","success","failure"],constructor:function(a){Ext.ux.util.RpcClient.superclass.constructor.call(this,a);this._url=a.url||null;this._id=0;this.addEvents("connected","error");this.reloadMethods()},reloadMethods:function(){this._execute("system.listMethods",{success:this._setMethods,scope:this})},_execute:function(a,b){b=b||{};b.params=b.params||[];b.id=this._id;var c=Ext.encode({method:a, +params:b.params,id:b.id});this._id++;return Ext.Ajax.request({url:this._url,method:"POST",success:this._onSuccess,failure:this._onFailure,scope:this,jsonData:c,options:b})},_onFailure:function(a,b){var c=b.options;errorObj={id:c.id,result:null,error:{msg:"HTTP: "+a.status+" "+a.statusText,code:255}};this.fireEvent("error",errorObj,a,b);"function"==Ext.type(c.failure)&&(c.scope?c.failure.call(c.scope,errorObj,a,b):c.failure(errorObj,a,b))},_onSuccess:function(a,b){var c=Ext.decode(a.responseText), +d=b.options;c.error?(this.fireEvent("error",c,a,b),"function"==Ext.type(d.failure)&&(d.scope?d.failure.call(d.scope,c,a,b):d.failure(c,a,b))):"function"==Ext.type(d.success)&&(d.scope?d.success.call(d.scope,c.result,c,a,b):d.success(c.result,c,a,b))},_parseArgs:function(a){var b=[];Ext.each(a,function(a){b.push(a)});a=b[b.length-1];if("object"==Ext.type(a)){var c=Ext.keys(a),d=!1;Ext.each(this._optionKeys,function(a){-1<c.indexOf(a)&&(d=!0)});d?b.remove(a):a={}}else a={};a.params=b;return a},_setMethods:function(a){var b= +{},c=this;Ext.each(a,function(a){var d=a.split("."),e=b[d[0]]||{};e[d[1]]=function(){var b=c._parseArgs(arguments);return c._execute(a,b)};b[d[0]]=e});for(var d in b)c[d]=b[d];Ext.each(this._components,function(a){!a in b&&delete this[a]},this);this._components=Ext.keys(b);this.fireEvent("connected",this)}}); +Deluge.ConnectionManager=Ext.extend(Ext.Window,{layout:"fit",width:300,height:220,bodyStyle:"padding: 10px 5px;",buttonAlign:"right",closeAction:"hide",closable:!0,plain:!0,constrainHeader:!0,title:_("Connection Manager"),iconCls:"x-deluge-connect-window-icon",initComponent:function(){Deluge.ConnectionManager.superclass.initComponent.call(this);this.on("hide",this.onHide,this);this.on("show",this.onShow,this);deluge.events.on("login",this.onLogin,this);deluge.events.on("logout",this.onLogout,this); +this.addButton(_("Close"),this.onClose,this);this.addButton(_("Connect"),this.onConnect,this);this.list=new Ext.list.ListView({store:new Ext.data.ArrayStore({fields:[{name:"status",mapping:4},{name:"host",mapping:1},{name:"port",mapping:2},{name:"user",mapping:3},{name:"version",mapping:5}],id:0}),columns:[{header:_("Status"),width:0.24,sortable:!0,tpl:new Ext.XTemplate("<tpl if=\"status == 'Online'\">",_("Online"),"</tpl>","<tpl if=\"status == 'Offline'\">",_("Offline"),"</tpl>","<tpl if=\"status == 'Connected'\">", +_("Connected"),"</tpl>"),dataIndex:"status"},{id:"host",header:_("Host"),width:0.51,sortable:!0,tpl:"{user}@{host}:{port}",dataIndex:"host"},{header:_("Version"),width:0.25,sortable:!0,tpl:'<tpl if="version">{version}</tpl>',dataIndex:"version"}],singleSelect:!0,listeners:{selectionchange:{fn:this.onSelectionChanged,scope:this}}});this.panel=this.add({autoScroll:!0,items:[this.list],bbar:new Ext.Toolbar({buttons:[{id:"cm-add",cls:"x-btn-text-icon",text:_("Add"),iconCls:"icon-add",handler:this.onAddClick, +scope:this},{id:"cm-edit",cls:"x-btn-text-icon",text:_("Edit"),iconCls:"icon-edit",handler:this.onEditClick,scope:this},{id:"cm-remove",cls:"x-btn-text-icon",text:_("Remove"),iconCls:"icon-remove",handler:this.onRemoveClick,disabled:!0,scope:this},"->",{id:"cm-stop",cls:"x-btn-text-icon",text:_("Stop Daemon"),iconCls:"icon-error",handler:this.onStopClick,disabled:!0,scope:this}]})});this.update=this.update.createDelegate(this)},checkConnected:function(){deluge.client.web.connected({success:function(a){a? +deluge.events.fire("connect"):this.show()},scope:this})},disconnect:function(a){deluge.events.fire("disconnect");a&&!this.isVisible()&&this.show()},loadHosts:function(){deluge.client.web.get_hosts({success:this.onGetHosts,scope:this})},update:function(){this.list.getStore().each(function(a){deluge.client.web.get_host_status(a.id,{success:this.onGetHostStatus,scope:this})},this)},updateButtons:function(a){var b=this.buttons[1],c=a.get("status");b.enable();"connected"==c.toLowerCase()?b.setText(_("Disconnect")): +(b.setText(_("Connect")),"online"!=c.toLowerCase()&&b.disable());"connected"==c.toLowerCase()||"online"==c.toLowerCase()?(this.stopHostButton.enable(),this.stopHostButton.setText(_("Stop Daemon"))):"127.0.0.1"==a.get("host")||"localhost"==a.get("host")?(this.stopHostButton.enable(),this.stopHostButton.setText(_("Start Daemon"))):this.stopHostButton.disable()},onAddClick:function(a,b){this.addWindow||(this.addWindow=new Deluge.AddConnectionWindow,this.addWindow.on("hostadded",this.onHostChange,this)); +this.addWindow.show()},onEditClick:function(a,b){var c=this.list.getSelectedRecords()[0];c&&(this.editWindow||(this.editWindow=new Deluge.EditConnectionWindow,this.editWindow.on("hostedited",this.onHostChange,this)),this.editWindow.show(c))},onHostChange:function(){this.loadHosts()},onClose:function(a){this.hide()},onConnect:function(a){if(a=this.list.getSelectedRecords()[0]){var b=this,c=function(){deluge.client.web.disconnect({success:function(a){this.update(this);deluge.events.fire("disconnect")}, +scope:b})};"connected"==a.get("status").toLowerCase()?c():(-1<this.list.getStore().find("status","Connected",0,!1,!1)&&c(),deluge.client.web.connect(a.id,{success:function(a){deluge.client.reloadMethods();deluge.client.on("connected",function(a){deluge.events.fire("connect")},this,{single:!0})}}),this.hide())}},onGetHosts:function(a){this.list.getStore().loadData(a);Ext.each(a,function(a){deluge.client.web.get_host_status(a[0],{success:this.onGetHostStatus,scope:this})},this)},onGetHostStatus:function(a){var b= +this.list.getStore().getById(a[0]);b.set("status",a[1]);b.set("version",a[2]);b.commit();(a=this.list.getSelectedRecords()[0])&&a==b&&this.updateButtons(b)},onHide:function(){this.running&&window.clearInterval(this.running)},onLogin:function(){deluge.config.first_login?Ext.MessageBox.confirm(_("Change Default Password"),_("We recommend changing the default password.<br><br>Would you like to change it now?"),function(a){this.checkConnected();"yes"==a&&(deluge.preferences.show(),deluge.preferences.selectPage("Interface")); +deluge.client.web.set_config({first_login:!1})},this):this.checkConnected()},onLogout:function(){this.disconnect();!this.hidden&&this.rendered&&this.hide()},onRemoveClick:function(a){var b=this.list.getSelectedRecords()[0];b&&deluge.client.web.remove_host(b.id,{success:function(a){a?this.list.getStore().remove(b):Ext.MessageBox.show({title:_("Error"),msg:a[1],buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"})},scope:this})},onSelectionChanged:function(a,b){b[0]? +(this.editHostButton.enable(),this.removeHostButton.enable(),this.stopHostButton.enable(),this.stopHostButton.setText(_("Stop Daemon")),this.updateButtons(this.list.getRecord(b[0]))):(this.editHostButton.disable(),this.removeHostButton.disable(),this.stopHostButton.disable())},onShow:function(){if(!this.addHostButton){var a=this.panel.getBottomToolbar();this.addHostButton=a.items.get("cm-add");this.editHostButton=a.items.get("cm-edit");this.removeHostButton=a.items.get("cm-remove");this.stopHostButton= +a.items.get("cm-stop")}this.loadHosts();this.running||(this.running=window.setInterval(this.update,2E3,this))},onStopClick:function(a,b){var c=this.list.getSelectedRecords()[0];c&&("Offline"==c.get("status")?deluge.client.web.start_daemon(c.get("port")):deluge.client.web.stop_daemon(c.id,{success:function(a){a[0]||Ext.MessageBox.show({title:_("Error"),msg:a[1],buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"})}}))}}); +Deluge.CopyMagnet=Ext.extend(Ext.Window,{title:_("Copy Magnet URI"),width:375,closeAction:"hide",iconCls:"icon-magnet-copy",initComponent:function(){Deluge.CopyMagnet.superclass.initComponent.call(this);form=this.add({xtype:"form",defaultType:"textfield",hideLabels:!0});this.magnetURI=form.add({name:"URI",anchor:"100%"});this.addButton(_("Close"),this.onClose,this);this.addButton(_("Copy"),this.onCopy,this)},show:function(a){Deluge.CopyMagnet.superclass.show.call(this);a=deluge.torrents.getSelected(); +deluge.client.core.get_magnet_uri(a.id,{success:this.onRequestComplete,scope:this})},onRequestComplete:function(a){this.magnetURI.setValue(a)},onCopy:function(){this.magnetURI.focus();this.magnetURI.el.dom.select();document.execCommand("copy")},onClose:function(){this.hide()}});deluge.copyMagnetWindow=new Deluge.CopyMagnet;Ext.state.Manager.setProvider(new Ext.state.CookieProvider({expires:new Date((new Date).getTime()+31536E7)})); +Ext.apply(Ext,{isObjectEmpty:function(a){for(var b in a)return!1;return!0},areObjectsEqual:function(a,b){var c=!0;if(!a||!b)return!1;for(var d in a)a[d]!=b[d]&&(c=!1);return c},keys:function(a){var b=[],c;for(c in a)a.hasOwnProperty(c)&&b.push(c);return b},values:function(a){var b=[],c;for(c in a)a.hasOwnProperty(c)&&b.push(a[c]);return b},splat:function(a){var b=Ext.type(a);return b?"array"!=b?[a]:a:[]}});Ext.getKeys=Ext.keys;Ext.BLANK_IMAGE_URL=deluge.config.base+"images/s.gif"; +Ext.USE_NATIVE_JSON=!0; +Ext.apply(Deluge,{pluginStore:{},progressTpl:'<div class="x-progress-wrap x-progress-renderered"><div class="x-progress-inner"><div style="width: {2}px" class="x-progress-bar"><div style="z-index: 99; width: {3}px" class="x-progress-text"><div style="width: {1}px;">{0}</div></div></div><div class="x-progress-text x-progress-text-back"><div style="width: {1}px;">{0}</div></div></div></div>',progressBar:function(a,b,c,d){d=Ext.value(d,10);a=(b/100*a).toFixed(0);return String.format(Deluge.progressTpl,c, +b,a-1,0<a-d?a-d:0)},createPlugin:function(a){return new Deluge.pluginStore[a]},hasPlugin:function(a){return Deluge.pluginStore[a]?!0:!1},registerPlugin:function(a,b){Deluge.pluginStore[a]=b}});deluge.plugins={};FILE_PRIORITY={"0":"Skip",1:"Low",2:"Low",3:"Low",4:"Normal",5:"High",6:"High",7:"High",9:"Mixed",Skip:0,Low:1,Normal:4,High:7,Mixed:9}; +FILE_PRIORITY_CSS={"0":"x-no-download",1:"x-low-download",2:"x-low-download",3:"x-low-download",4:"x-normal-download",5:"x-high-download",6:"x-high-download",7:"x-high-download",9:"x-mixed-download"};Ext.ns("Deluge"); +Deluge.EditConnectionWindow=Ext.extend(Ext.Window,{title:_("Edit Connection"),iconCls:"x-deluge-add-window-icon",layout:"fit",width:300,height:195,constrainHeader:!0,bodyStyle:"padding: 10px 5px;",closeAction:"hide",initComponent:function(){Deluge.EditConnectionWindow.superclass.initComponent.call(this);this.addEvents("hostedited");this.addButton(_("Close"),this.hide,this);this.addButton(_("Edit"),this.onEditClick,this);this.on("hide",this.onHide,this);this.form=this.add({xtype:"form",defaultType:"textfield", +baseCls:"x-plain",labelWidth:60,items:[{fieldLabel:_("Host:"),labelSeparator:"",name:"host",anchor:"75%",value:""},{xtype:"spinnerfield",fieldLabel:_("Port:"),labelSeparator:"",name:"port",strategy:{xtype:"number",decimalPrecision:0,minValue:0,maxValue:65535},anchor:"40%",value:58846},{fieldLabel:_("Username:"),labelSeparator:"",name:"username",anchor:"75%",value:""},{fieldLabel:_("Password:"),labelSeparator:"",anchor:"75%",name:"password",inputType:"password",value:""}]})},show:function(a){Deluge.EditConnectionWindow.superclass.show.call(this); +this.form.getForm().findField("host").setValue(a.get("host"));this.form.getForm().findField("port").setValue(a.get("port"));this.form.getForm().findField("username").setValue(a.get("user"));this.host_id=a.id},onEditClick:function(){var a=this.form.getForm().getValues();deluge.client.web.edit_host(this.host_id,a.host,Number(a.port),a.username,a.password,{success:function(a){a?this.fireEvent("hostedited"):(console.log(a),Ext.MessageBox.show({title:_("Error"),msg:String.format(_("Unable to edit host")), +buttons:Ext.MessageBox.OK,modal:!1,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"}));this.hide()},scope:this})},onHide:function(){this.form.getForm().reset()}});Ext.ns("Deluge"); +Deluge.EditTrackerWindow=Ext.extend(Ext.Window,{title:_("Edit Tracker"),layout:"fit",width:375,height:110,plain:!0,closable:!0,resizable:!1,constrainHeader:!0,bodyStyle:"padding: 5px",buttonAlign:"right",closeAction:"hide",iconCls:"x-deluge-edit-trackers",initComponent:function(){Deluge.EditTrackerWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancelClick,this);this.addButton(_("Save"),this.onSaveClick,this);this.on("hide",this.onHide,this);this.form=this.add({xtype:"form", +defaultType:"textfield",baseCls:"x-plain",labelWidth:55,items:[{fieldLabel:_("Tracker:"),labelSeparator:"",name:"tracker",anchor:"100%"}]})},show:function(a){Deluge.EditTrackerWindow.superclass.show.call(this);this.record=a;this.form.getForm().findField("tracker").setValue(a.data.url)},onCancelClick:function(){this.hide()},onHide:function(){this.form.getForm().findField("tracker").setValue("")},onSaveClick:function(){var a=this.form.getForm().findField("tracker").getValue();this.record.set("url", +a);this.record.commit();this.hide()}});Ext.ns("Deluge"); +Deluge.EditTrackersWindow=Ext.extend(Ext.Window,{title:_("Edit Trackers"),layout:"fit",width:350,height:220,plain:!0,closable:!0,resizable:!0,constrainHeader:!0,bodyStyle:"padding: 5px",buttonAlign:"right",closeAction:"hide",iconCls:"x-deluge-edit-trackers",initComponent:function(){Deluge.EditTrackersWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancelClick,this);this.addButton(_("OK"),this.onOkClick,this);this.addEvents("save");this.on("show",this.onShow,this);this.on("save", +this.onSave,this);this.addWindow=new Deluge.AddTrackerWindow;this.addWindow.on("add",this.onAddTrackers,this);this.editWindow=new Deluge.EditTrackerWindow;this.list=new Ext.list.ListView({store:new Ext.data.JsonStore({root:"trackers",fields:["tier","url"]}),columns:[{header:_("Tier"),width:0.1,dataIndex:"tier"},{header:_("Tracker"),width:0.9,dataIndex:"url",tpl:new Ext.XTemplate("{url:htmlEncode}")}],columnSort:{sortClasses:["",""]},stripeRows:!0,singleSelect:!0,listeners:{dblclick:{fn:this.onListNodeDblClicked, +scope:this},selectionchange:{fn:this.onSelect,scope:this}}});this.panel=this.add({items:[this.list],autoScroll:!0,bbar:new Ext.Toolbar({items:[{text:_("Up"),iconCls:"icon-up",handler:this.onUpClick,scope:this},{text:_("Down"),iconCls:"icon-down",handler:this.onDownClick,scope:this},"->",{text:_("Add"),iconCls:"icon-add",handler:this.onAddClick,scope:this},{text:_("Edit"),iconCls:"icon-edit-trackers",handler:this.onEditClick,scope:this},{text:_("Remove"),iconCls:"icon-remove",handler:this.onRemoveClick, +scope:this}]})})},onAddClick:function(){this.addWindow.show()},onAddTrackers:function(a){var b=this.list.getStore();Ext.each(a,function(a){var d=!1,f=-1;b.each(function(b){b.get("tier")>f&&(f=b.get("tier"));if(a==b.get("tracker"))return d=!0,!1},this);d||b.add(new b.recordType({tier:f+1,url:a}))},this)},onCancelClick:function(){this.hide()},onEditClick:function(){var a=this.list.getSelectedRecords()[0];a&&this.editWindow.show(a)},onHide:function(){this.list.getStore().removeAll()},onListNodeDblClicked:function(a, +b,c,d){this.editWindow.show(this.list.getRecord(c))},onOkClick:function(){var a=[];this.list.getStore().each(function(b){a.push({tier:b.get("tier"),url:b.get("url")})},this);deluge.client.core.set_torrent_trackers(this.torrentId,a,{failure:this.onSaveFail,scope:this});this.hide()},onRemoveClick:function(){var a=this.list.getSelectedRecords()[0];a&&this.list.getStore().remove(a)},onRequestComplete:function(a){this.list.getStore().loadData(a);this.list.getStore().sort("tier","ASC")},onSaveFail:function(){}, +onSelect:function(a){a.getSelectionCount()&&this.panel.getBottomToolbar().items.get(4).enable()},onShow:function(){this.panel.getBottomToolbar().items.get(4).disable();var a=deluge.torrents.getSelected();this.torrentId=a.id;deluge.client.core.get_torrent_status(a.id,["trackers"],{success:this.onRequestComplete,scope:this})},onDownClick:function(){var a=this.list.getSelectedRecords()[0];a&&(a.set("tier",a.get("tier")+1),a.store.sort("tier","ASC"),a.store.commitChanges(),this.list.select(a.store.indexOf(a)))}, +onUpClick:function(){var a=this.list.getSelectedRecords()[0];a&&0!=a.get("tier")&&(a.set("tier",a.get("tier")-1),a.store.sort("tier","ASC"),a.store.commitChanges(),this.list.select(a.store.indexOf(a)))}}); +Deluge.EventsManager=Ext.extend(Ext.util.Observable,{constructor:function(){this.toRegister=[];this.on("login",this.onLogin,this);Deluge.EventsManager.superclass.constructor.call(this)},addListener:function(a,b,c,d){this.addEvents(a);/[A-Z]/.test(a.substring(0,1))&&(deluge.client?deluge.client.web.register_event_listener(a):this.toRegister.push(a));Deluge.EventsManager.superclass.addListener.call(this,a,b,c,d)},getEvents:function(){deluge.client.web.get_events({success:this.onGetEventsSuccess,failure:this.onGetEventsFailure, +scope:this})},start:function(){Ext.each(this.toRegister,function(a){deluge.client.web.register_event_listener(a)});this.running=!0;this.errorCount=0;this.getEvents()},stop:function(){this.running=!1},onLogin:function(){this.start()},onGetEventsSuccess:function(a){this.running&&(a&&Ext.each(a,function(a){var c=a[1];c.splice(0,0,a[0]);this.fireEvent.apply(this,c)},this),this.getEvents())},onGetEventsFailure:function(a,b){this.running&&(!b.isTimeout&&3<=this.errorCount++?this.stop():this.getEvents())}}); +Deluge.EventsManager.prototype.on=Deluge.EventsManager.prototype.addListener;Deluge.EventsManager.prototype.fire=Deluge.EventsManager.prototype.fireEvent;deluge.events=new Deluge.EventsManager;Ext.namespace("Deluge"); +Deluge.FileBrowser=Ext.extend(Ext.Window,{title:_("File Browser"),width:500,height:400,initComponent:function(){Deluge.FileBrowser.superclass.initComponent.call(this);this.add({xtype:"toolbar",items:[{text:_("Back"),iconCls:"icon-back"},{text:_("Forward"),iconCls:"icon-forward"},{text:_("Up"),iconCls:"icon-up"},{text:_("Home"),iconCls:"icon-home"}]})}});Ext.ns("Deluge"); +Deluge.FilterPanel=Ext.extend(Ext.Panel,{autoScroll:!0,border:!1,show_zero:null,initComponent:function(){Deluge.FilterPanel.superclass.initComponent.call(this);this.filterType=this.initialConfig.filter;var a="";"state"==this.filterType?a=_("States"):"tracker_host"==this.filterType?a=_("Trackers"):"owner"==this.filterType?a=_("Owner"):"label"==this.filterType?a=_("Labels"):(a=this.filterType.replace("_"," "),parts=a.split(" "),a="",Ext.each(parts,function(b){fl=b.substring(0,1).toUpperCase();a+=fl+ +b.substring(1)+" "}));this.setTitle(_(a));var b=Deluge.FilterPanel.templates[this.filterType]?Deluge.FilterPanel.templates[this.filterType]:'<div class="x-deluge-filter x-deluge-{filter:lowercase}">{filter} ({count})</div>';this.list=this.add({xtype:"listview",singleSelect:!0,hideHeaders:!0,reserveScrollOffset:!0,store:new Ext.data.ArrayStore({idIndex:0,fields:["filter","count"]}),columns:[{id:"filter",sortable:!1,tpl:b,dataIndex:"filter"}]});this.relayEvents(this.list,["selectionchange"])},getState:function(){if(this.list.getSelectionCount()){var a= +this.list.getSelectedRecords()[0];if(a&&"All"!=a.id)return a.id}},getStates:function(){return this.states},getStore:function(){return this.list.getStore()},updateStates:function(a){this.states={};Ext.each(a,function(a){this.states[a[0]]=a[1]},this);if(!(null==this.show_zero?deluge.config.sidebar_show_zero:this.show_zero)){var b=[];Ext.each(a,function(a){(0<a[1]||"All"==a[0])&&b.push(a)});a=b}var c=this.getStore(),d={};Ext.each(a,function(a,b){var e=c.getById(a[0]);e||(e=new c.recordType({filter:a[0], +count:a[1]}),e.id=a[0],c.insert(b,e));e.beginEdit();e.set("filter",_(a[0]));e.set("count",a[1]);e.endEdit();d[a[0]]=!0},this);c.each(function(a){if(!d[a.id]){c.remove(a);var b=this.list.getSelectedRecords()[0];b&&b.id==a.id&&this.list.select(0)}},this);c.commitChanges();this.list.getSelectionCount()||this.list.select(0)}});Deluge.FilterPanel.templates={tracker_host:'<div class="x-deluge-filter" style="background-image: url('+deluge.config.base+'tracker/{filter});">{filter:htmlEncode} ({count})</div>'}; +Deluge.Formatters=function(){var a={"&":"&",">":">","<":"<",'"':""","'":"'"},b=RegExp("("+Object.keys(a).join("|")+")","g"),c=function(b,c){return a[c]};return Formatters={date:function(a){function b(a,c){for(var d=a+"";d.length<c;)d="0"+d;return d}a=new Date(1E3*a);return String.format("{0}/{1}/{2} {3}:{4}:{5}",b(a.getDate(),2),b(a.getMonth()+1,2),a.getFullYear(),b(a.getHours(),2),b(a.getMinutes(),2),b(a.getSeconds(),2))},size:function(a,b){if(!a&&!b)return"";a/=1024;if(1024>a)return a.toFixed(1)+ +" KiB";a/=1024;return 1024>a?a.toFixed(1)+" MiB":(a/1024).toFixed(1)+" GiB"},sizeShort:function(a,b){if(!a&&!b)return"";a/=1024;if(1024>a)return a.toFixed(1)+" K";a/=1024;return 1024>a?a.toFixed(1)+" M":(a/1024).toFixed(1)+" G"},speed:function(a,b){return!a&&!b?"":fsize(a,b)+"/s"},timeRemaining:function(a){if(0>=a)return"∞";a=a.toFixed(0);if(60>a)return a+"s";a/=60;if(60>a){var b=Math.floor(a);a=Math.round(60*(a-b));return 0<a?b+"m "+a+"s":b+"m"}a/=60;if(24>a){var c=Math.floor(a),b=Math.round(60* +(a-c));return 0<b?c+"h "+b+"m":c+"h"}a/=24;b=Math.floor(a);c=Math.round(24*(a-b));return 0<c?b+"d "+c+"h":b+"d"},plain:function(a){return a},cssClassEscape:function(a){return a.toLowerCase().replace(".","_")},htmlEncode:function(a){return!a?a:String(a).replace(b,c)}}}();var fsize=Deluge.Formatters.size,fsize_short=Deluge.Formatters.sizeShort,fspeed=Deluge.Formatters.speed,ftime=Deluge.Formatters.timeRemaining,fdate=Deluge.Formatters.date,fplain=Deluge.Formatters.plain; +Ext.util.Format.cssClassEscape=Deluge.Formatters.cssClassEscape;Ext.util.Format.htmlEncode=Deluge.Formatters.htmlEncode; +Deluge.Keys={Grid:"queue name total_wanted state progress num_seeds total_seeds num_peers total_peers download_payload_rate upload_payload_rate eta ratio distributed_copies is_auto_managed time_added tracker_host download_location last_seen_complete total_done total_uploaded max_download_speed max_upload_speed seeds_peers_ratio total_remaining completed_time time_since_transfer".split(" "),Status:"total_done total_payload_download total_uploaded total_payload_upload next_announce tracker_status num_pieces piece_length is_auto_managed active_time seeding_time time_since_transfer seed_rank last_seen_complete completed_time owner public shared".split(" "), +Files:["files","file_progress","file_priorities"],Peers:["peers"],Details:"name download_location total_size num_files message tracker_host comment creator".split(" "),Options:"max_download_speed max_upload_speed max_connections max_upload_slots is_auto_managed stop_at_ratio stop_ratio remove_at_ratio private prioritize_first_last move_completed move_completed_path super_seeding".split(" ")};Ext.each(Deluge.Keys.Grid,function(a){Deluge.Keys.Status.push(a)}); +Deluge.LoginWindow=Ext.extend(Ext.Window,{firstShow:!0,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closable:!1,closeAction:"hide",iconCls:"x-deluge-login-window-icon",layout:"fit",modal:!0,plain:!0,resizable:!1,title:_("Login"),width:300,height:120,initComponent:function(){Deluge.LoginWindow.superclass.initComponent.call(this);this.on("show",this.onShow,this);this.addButton({text:_("Login"),handler:this.onLogin,scope:this});this.form=this.add({xtype:"form",baseCls:"x-plain",labelWidth:120, +labelAlign:"right",defaults:{width:110},defaultType:"textfield"});this.passwordField=this.form.add({xtype:"textfield",fieldLabel:_("Password:"),labelSeparator:"",grow:!0,growMin:"110",growMax:"145",id:"_password",name:"password",inputType:"password"});this.passwordField.on("specialkey",this.onSpecialKey,this)},logout:function(){deluge.events.fire("logout");deluge.client.auth.delete_session({success:function(a){this.show(!0)},scope:this})},show:function(a){this.firstShow&&(deluge.client.on("error", +this.onClientError,this),this.firstShow=!1);if(a)return Deluge.LoginWindow.superclass.show.call(this);deluge.client.auth.check_session({success:function(a){a?deluge.events.fire("login"):this.show(!0)},failure:function(a){this.show(!0)},scope:this})},onSpecialKey:function(a,b){if(13==b.getKey())this.onLogin()},onLogin:function(){var a=this.passwordField;deluge.client.auth.login(a.getValue(),{success:function(b){b?(deluge.events.fire("login"),this.hide(),a.setRawValue("")):Ext.MessageBox.show({title:_("Login Failed"), +msg:_("You entered an incorrect password"),buttons:Ext.MessageBox.OK,modal:!1,fn:function(){a.focus(!0,10)},icon:Ext.MessageBox.WARNING,iconCls:"x-deluge-icon-warning"})},scope:this})},onClientError:function(a,b,c){1==a.error.code&&(deluge.events.fire("logout"),this.show(!0))},onShow:function(){this.passwordField.focus(!0,300)}}); +deluge.menus={onTorrentActionSetOpt:function(a,b){var c=deluge.torrents.getSelectedIds(),d=a.initialConfig.torrentAction,f={};f[d[0]]=d[1];deluge.client.core.set_torrent_options(c,f)},onTorrentActionMethod:function(a,b){var c=deluge.torrents.getSelectedIds();deluge.client.core[a.initialConfig.torrentAction](c,{success:function(){deluge.ui.update()}})},onTorrentActionShow:function(a,b){var c=deluge.torrents.getSelectedIds();switch(a.initialConfig.torrentAction){case "copy_magnet":deluge.copyMagnetWindow.show(); +break;case "edit_trackers":deluge.editTrackers.show();break;case "remove":deluge.removeWindow.show(c);break;case "move":deluge.moveStorage.show(c)}}}; +deluge.menus.torrent=new Ext.menu.Menu({id:"torrentMenu",items:[{torrentAction:"pause_torrent",text:_("Pause"),iconCls:"icon-pause",handler:deluge.menus.onTorrentActionMethod,scope:deluge.menus},{torrentAction:"resume_torrent",text:_("Resume"),iconCls:"icon-resume",handler:deluge.menus.onTorrentActionMethod,scope:deluge.menus},"-",{text:_("Options"),iconCls:"icon-options",hideOnClick:!1,menu:new Ext.menu.Menu({items:[{text:_("D/L Speed Limit"),iconCls:"x-deluge-downloading",hideOnClick:!1,menu:new Ext.menu.Menu({items:[{torrentAction:["max_download_speed", +5],text:_("5 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_download_speed",10],text:_("10 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_download_speed",30],text:_("30 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_download_speed",80],text:_("80 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_download_speed",300],text:_("300 KiB/s"), +handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_download_speed",-1],text:_("Unlimited"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus}]})},{text:_("U/L Speed Limit"),iconCls:"x-deluge-seeding",hideOnClick:!1,menu:new Ext.menu.Menu({items:[{torrentAction:["max_upload_speed",5],text:_("5 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_speed",10],text:_("10 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt, +scope:deluge.menus},{torrentAction:["max_upload_speed",30],text:_("30 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_speed",80],text:_("80 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_speed",300],text:_("300 KiB/s"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_speed",-1],text:_("Unlimited"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus}]})}, +{text:_("Connection Limit"),iconCls:"x-deluge-connections",hideOnClick:!1,menu:new Ext.menu.Menu({items:[{torrentAction:["max_connections",50],text:"50",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_connections",100],text:"100",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_connections",200],text:"200",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_connections",300],text:"300",handler:deluge.menus.onTorrentActionSetOpt, +scope:deluge.menus},{torrentAction:["max_connections",500],text:"500",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_connections",-1],text:_("Unlimited"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus}]})},{text:_("Upload Slot Limit"),iconCls:"icon-upload-slots",hideOnClick:!1,menu:new Ext.menu.Menu({items:[{torrentAction:["max_upload_slots",0],text:"0",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_slots", +1],text:"1",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_slots",2],text:"2",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_slots",3],text:"3",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_slots",5],text:"5",handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["max_upload_slots",-1],text:_("Unlimited"),handler:deluge.menus.onTorrentActionSetOpt, +scope:deluge.menus}]})},{id:"auto_managed",text:_("Auto Managed"),hideOnClick:!1,menu:new Ext.menu.Menu({items:[{torrentAction:["auto_managed",!0],text:_("On"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus},{torrentAction:["auto_managed",!1],text:_("Off"),handler:deluge.menus.onTorrentActionSetOpt,scope:deluge.menus}]})}]})},"-",{text:_("Queue"),iconCls:"icon-queue",hideOnClick:!1,menu:new Ext.menu.Menu({items:[{torrentAction:"queue_top",text:_("Top"),iconCls:"icon-top",handler:deluge.menus.onTorrentActionMethod, +scope:deluge.menus},{torrentAction:"queue_up",text:_("Up"),iconCls:"icon-up",handler:deluge.menus.onTorrentActionMethod,scope:deluge.menus},{torrentAction:"queue_down",text:_("Down"),iconCls:"icon-down",handler:deluge.menus.onTorrentActionMethod,scope:deluge.menus},{torrentAction:"queue_bottom",text:_("Bottom"),iconCls:"icon-bottom",handler:deluge.menus.onTorrentActionMethod,scope:deluge.menus}]})},"-",{torrentAction:"copy_magnet",text:_("Copy Magnet URI"),iconCls:"icon-magnet-copy",handler:deluge.menus.onTorrentActionShow, +scope:deluge.menus},{torrentAction:"force_reannounce",text:_("Update Tracker"),iconCls:"icon-update-tracker",handler:deluge.menus.onTorrentActionMethod,scope:deluge.menus},{torrentAction:"edit_trackers",text:_("Edit Trackers"),iconCls:"icon-edit-trackers",handler:deluge.menus.onTorrentActionShow,scope:deluge.menus},"-",{torrentAction:"remove",text:_("Remove Torrent"),iconCls:"icon-remove",handler:deluge.menus.onTorrentActionShow,scope:deluge.menus},"-",{torrentAction:"force_recheck",text:_("Force Recheck"), +iconCls:"icon-recheck",handler:deluge.menus.onTorrentActionMethod,scope:deluge.menus},{torrentAction:"move",text:_("Move Download Folder"),iconCls:"icon-move",handler:deluge.menus.onTorrentActionShow,scope:deluge.menus}]}); +deluge.menus.filePriorities=new Ext.menu.Menu({id:"filePrioritiesMenu",items:[{id:"expandAll",text:_("Expand All"),iconCls:"icon-expand-all"},"-",{id:"skip",text:_("Skip"),iconCls:"icon-do-not-download",filePriority:FILE_PRIORITY.Skip},{id:"low",text:_("Low"),iconCls:"icon-low",filePriority:FILE_PRIORITY.Low},{id:"normal",text:_("Normal"),iconCls:"icon-normal",filePriority:FILE_PRIORITY.Normal},{id:"high",text:_("High"),iconCls:"icon-high",filePriority:FILE_PRIORITY.High}]});Ext.namespace("Deluge"); +Deluge.MoveStorage=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Move Download Folder"),width:375,height:110,layout:"fit",buttonAlign:"right",closeAction:"hide",closable:!0,iconCls:"x-deluge-move-storage",plain:!0,constrainHeader:!0,resizable:!1},a);Deluge.MoveStorage.superclass.constructor.call(this,a)},initComponent:function(){Deluge.MoveStorage.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Move"),this.onMove,this);this.form= +this.add({xtype:"form",border:!1,defaultType:"textfield",width:300,bodyStyle:"padding: 5px"});this.moveLocation=this.form.add({fieldLabel:_("Download Folder"),name:"location",width:240})},hide:function(){Deluge.MoveStorage.superclass.hide.call(this);this.torrentIds=null},show:function(a){Deluge.MoveStorage.superclass.show.call(this);this.torrentIds=a},onCancel:function(){this.hide()},onMove:function(){var a=this.moveLocation.getValue();deluge.client.core.move_storage(this.torrentIds,a);this.hide()}}); +deluge.moveStorage=new Deluge.MoveStorage; +Deluge.MultiOptionsManager=Ext.extend(Deluge.OptionsManager,{constructor:function(a){this.currentId=null;this.stored={};Deluge.MultiOptionsManager.superclass.constructor.call(this,a)},changeId:function(a,b){var c=this.currentId;this.currentId=a;if(!b)for(var d in this.options)this.binds[d]&&Ext.each(this.binds[d],function(a){a.setValue(this.get(d))},this);return c},commit:function(){this.stored[this.currentId]=Ext.apply(this.stored[this.currentId],this.changed[this.currentId]);this.reset()},get:function(){if(1== +arguments.length){var a=arguments[0];return this.isDirty(a)?this.changed[this.currentId][a]:this.getDefault(a)}if(0==arguments.length){var b={};for(a in this.options)b[a]=this.isDirty(a)?this.changed[this.currentId][a]:this.getDefault(a)}else b={},Ext.each(arguments,function(a){b[a]=this.isDirty(a)?this.changed[this.currentId][a]:this.getDefault(a)},this);return b},getDefault:function(a){return this.has(a)?this.stored[this.currentId][a]:this.options[a]},getDirty:function(){return this.changed[this.currentId]? +this.changed[this.currentId]:{}},isDirty:function(a){return this.changed[this.currentId]&&!Ext.isEmpty(this.changed[this.currentId][a])},has:function(a){return this.stored[this.currentId]&&!Ext.isEmpty(this.stored[this.currentId][a])},reset:function(){this.changed[this.currentId]&&delete this.changed[this.currentId];this.stored[this.currentId]&&delete this.stored[this.currentId]},resetAll:function(){this.changed={};this.stored={};this.changeId(null)},setDefault:function(a,b){if(void 0!==a)if(void 0=== +b)for(var c in a)this.setDefault(c,a[c]);else c=this.getDefault(a),b=this.convertValueType(c,b),c!=b&&(this.stored[this.currentId]||(this.stored[this.currentId]={}),this.stored[this.currentId][a]=b,this.isDirty(a)||this.fireEvent("changed",a,b,c))},update:function(a,b){if(void 0!==a)if(void 0===b)for(var c in a)this.update(c,a[c]);else{this.changed[this.currentId]||(this.changed[this.currentId]={});c=this.getDefault(a);b=this.convertValueType(c,b);var d=this.get(a);d!=b&&(c==b?this.isDirty(a)&&delete this.changed[this.currentId][a]: +this.changed[this.currentId][a]=b,this.fireEvent("changed",a,b,d))}}});Ext.ns("Deluge"); +Deluge.OtherLimitWindow=Ext.extend(Ext.Window,{layout:"fit",width:210,height:100,constrainHeader:!0,closeAction:"hide",initComponent:function(){Deluge.OtherLimitWindow.superclass.initComponent.call(this);this.form=this.add({xtype:"form",baseCls:"x-plain",bodyStyle:"padding: 5px",layout:"hbox",layoutConfig:{pack:"start"},items:[{xtype:"spinnerfield",name:"limit"}]});this.initialConfig.unit?this.form.add({border:!1,baseCls:"x-plain",bodyStyle:"padding: 5px",html:this.initialConfig.unit}):this.setSize(180, +100);this.addButton(_("Cancel"),this.onCancelClick,this);this.addButton(_("OK"),this.onOkClick,this);this.afterMethod("show",this.doFocusField,this)},setValue:function(a){this.form.getForm().setValues({limit:a})},onCancelClick:function(){this.form.getForm().reset();this.hide()},onOkClick:function(){var a={};a[this.group]=this.form.getForm().getValues().limit;deluge.client.core.set_config(a,{success:function(){deluge.ui.update()}});this.hide()},doFocusField:function(){this.form.getForm().findField("limit").focus(!0, +10)}});Ext.ns("Deluge"); +Deluge.Plugin=Ext.extend(Ext.util.Observable,{name:null,constructor:function(a){this.isDelugePlugin=!0;this.addEvents({enabled:!0,disabled:!0});Deluge.Plugin.superclass.constructor.call(this,a)},disable:function(){this.fireEvent("disabled",this);if(this.onDisable)this.onDisable()},enable:function(){deluge.client.reloadMethods();this.fireEvent("enable",this);if(this.onEnable)this.onEnable()},registerTorrentStatus:function(a,b,c){c=c||{};var d=c.colCfg||{};c=c.storeCfg||{};c=Ext.apply(c,{name:a});deluge.torrents.meta.fields.push(c); +deluge.torrents.getStore().reader.onMetaChange(deluge.torrents.meta);d=Ext.apply(d,{header:b,dataIndex:a});b=deluge.torrents.columns.slice(0);b.push(d);deluge.torrents.colModel.setConfig(b);deluge.torrents.columns=b;Deluge.Keys.Grid.push(a);deluge.torrents.getView().refresh(!0)},deregisterTorrentStatus:function(a){var b=[];Ext.each(deluge.torrents.meta.fields,function(c){c.name!=a&&b.push(c)});deluge.torrents.meta.fields=b;deluge.torrents.getStore().reader.onMetaChange(deluge.torrents.meta);var c= +[];Ext.each(deluge.torrents.columns,function(b){b.dataIndex!=a&&c.push(b)});deluge.torrents.colModel.setConfig(c);deluge.torrents.columns=c;var d=[];Ext.each(Deluge.Keys.Grid,function(b){b==a&&d.push(b)});Deluge.Keys.Grid=d;deluge.torrents.getView().refresh(!0)}});Ext.ns("Deluge.plugins"); +Deluge.RemoveWindow=Ext.extend(Ext.Window,{title:_("Remove Torrent"),layout:"fit",width:350,height:100,constrainHeader:!0,buttonAlign:"right",closeAction:"hide",closable:!0,iconCls:"x-deluge-remove-window-icon",plain:!0,bodyStyle:"padding: 5px; padding-left: 10px;",html:"Are you sure you wish to remove the torrent (s)?",initComponent:function(){Deluge.RemoveWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Remove With Data"),this.onRemoveData, +this);this.addButton(_("Remove Torrent"),this.onRemove,this)},remove:function(a){deluge.client.core.remove_torrents(this.torrentIds,a,{success:function(a){!0==a&&console.log("Error(s) occured when trying to delete torrent(s).");this.onRemoved(this.torrentIds)},scope:this,torrentIds:this.torrentIds})},show:function(a){Deluge.RemoveWindow.superclass.show.call(this);this.torrentIds=a},onCancel:function(){this.hide();this.torrentIds=null},onRemove:function(){this.remove(!1)},onRemoveData:function(){this.remove(!0)}, +onRemoved:function(a){deluge.events.fire("torrentsRemoved",a);this.hide();deluge.ui.update()}});deluge.removeWindow=new Deluge.RemoveWindow; +Deluge.Sidebar=Ext.extend(Ext.Panel,{panels:{},selected:null,constructor:function(a){a=Ext.apply({id:"sidebar",region:"west",cls:"deluge-sidebar",title:_("Filters"),layout:"accordion",split:!0,width:200,minSize:100,collapsible:!0},a);Deluge.Sidebar.superclass.constructor.call(this,a)},initComponent:function(){Deluge.Sidebar.superclass.initComponent.call(this);deluge.events.on("disconnect",this.onDisconnect,this)},createFilter:function(a,b){var c=new Deluge.FilterPanel({filter:a});c.on("selectionchange", +function(a,b){deluge.ui.update()});this.add(c);this.doLayout();this.panels[a]=c;c.header.on("click",function(a){deluge.config.sidebar_multiple_filters||deluge.ui.update();c.list.getSelectionCount()||c.list.select(0)});this.fireEvent("filtercreate",this,c);c.updateStates(b);this.fireEvent("afterfiltercreate",this,c)},getFilter:function(a){return this.panels[a]},getFilterStates:function(){var a={};if(deluge.config.sidebar_multiple_filters)this.items.each(function(b){var c=b.getState();null!=c&&(a[b.filterType]= +c)},this);else{var b=this.getLayout().activeItem;if(b){var c=b.getState();if(null==!c)return;a[b.filterType]=c}}return a},hasFilter:function(a){return this.panels[a]?!0:!1},onDisconnect:function(){for(var a in this.panels)this.remove(this.panels[a]);this.panels={};this.selected=null},onFilterSelect:function(a,b,c){deluge.ui.update()},update:function(a){for(var b in a){var c=a[b];-1<Ext.getKeys(this.panels).indexOf(b)?this.panels[b].updateStates(c):this.createFilter(b,c)}Ext.each(Ext.keys(this.panels), +function(b){-1==Ext.keys(a).indexOf(b)&&(this.remove(this.panels[b]),this.doLayout(),delete this.panels[b])},this)}});Ext.namespace("Deluge"); +Deluge.Statusbar=Ext.extend(Ext.ux.StatusBar,{constructor:function(a){a=Ext.apply({id:"deluge-statusbar",defaultIconCls:"x-deluge-statusbar x-not-connected",defaultText:_("Not Connected")},a);Deluge.Statusbar.superclass.constructor.call(this,a)},initComponent:function(){Deluge.Statusbar.superclass.initComponent.call(this);deluge.events.on("connect",this.onConnect,this);deluge.events.on("disconnect",this.onDisconnect,this)},createButtons:function(){this.buttons=this.add({id:"statusbar-connections", +text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-connections",tooltip:_("Connections"),menu:new Deluge.StatusbarMenu({items:[{text:"50",value:"50",group:"max_connections_global",checked:!1},{text:"100",value:"100",group:"max_connections_global",checked:!1},{text:"200",value:"200",group:"max_connections_global",checked:!1},{text:"300",value:"300",group:"max_connections_global",checked:!1},{text:"500",value:"500",group:"max_connections_global",checked:!1},{text:_("Unlimited"),value:"-1",group:"max_connections_global", +checked:!1},"-",{text:_("Other"),value:"other",group:"max_connections_global",checked:!1}],otherWin:{title:_("Set Maximum Connections")}})},"-",{id:"statusbar-downspeed",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-downloading",tooltip:_("Download Speed"),menu:new Deluge.StatusbarMenu({items:[{value:"5",text:_("5 KiB/s"),group:"max_download_speed",checked:!1},{value:"10",text:_("10 KiB/s"),group:"max_download_speed",checked:!1},{value:"30",text:_("30 KiB/s"),group:"max_download_speed",checked:!1}, +{value:"80",text:_("80 KiB/s"),group:"max_download_speed",checked:!1},{value:"300",text:_("300 KiB/s"),group:"max_download_speed",checked:!1},{value:"-1",text:_("Unlimited"),group:"max_download_speed",checked:!1},"-",{value:"other",text:_("Other"),group:"max_download_speed",checked:!1}],otherWin:{title:_("Set Maximum Download Speed"),unit:_("KiB/s")}})},"-",{id:"statusbar-upspeed",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-seeding",tooltip:_("Upload Speed"),menu:new Deluge.StatusbarMenu({items:[{value:"5", +text:_("5 KiB/s"),group:"max_upload_speed",checked:!1},{value:"10",text:_("10 KiB/s"),group:"max_upload_speed",checked:!1},{value:"30",text:_("30 KiB/s"),group:"max_upload_speed",checked:!1},{value:"80",text:_("80 KiB/s"),group:"max_upload_speed",checked:!1},{value:"300",text:_("300 KiB/s"),group:"max_upload_speed",checked:!1},{value:"-1",text:_("Unlimited"),group:"max_upload_speed",checked:!1},"-",{value:"other",text:_("Other"),group:"max_upload_speed",checked:!1}],otherWin:{title:_("Set Maximum Upload Speed"), +unit:_("KiB/s")}})},"-",{id:"statusbar-traffic",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-traffic",tooltip:_("Protocol Traffic Download/Upload"),handler:function(){deluge.preferences.show();deluge.preferences.selectPage("Network")}},"-",{id:"statusbar-externalip",text:" ",cls:"x-btn-text",tooltip:_("External IP Address")},"-",{id:"statusbar-dht",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-dht",tooltip:_("DHT Nodes")},"-",{id:"statusbar-freespace",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-freespace", +tooltip:_("Freespace in download folder"),handler:function(){deluge.preferences.show();deluge.preferences.selectPage("Downloads")}});this.created=!0},onConnect:function(){this.setStatus({iconCls:"x-connected",text:""});this.created?Ext.each(this.buttons,function(a){a.show();a.enable()}):this.createButtons();this.doLayout()},onDisconnect:function(){this.clearStatus({useDefaults:!0});Ext.each(this.buttons,function(a){a.hide();a.disable()});this.doLayout()},update:function(a){function b(a){return a+ +" KiB/s"}if(a){var c=function(a,b){var c=this.items.get("statusbar-"+a);if(0<b.limit.value)var e=b.value.formatter?b.value.formatter(b.value.value,!0):b.value.value,l=b.limit.formatter?b.limit.formatter(b.limit.value,!0):b.limit.value,e=String.format(b.format,e,l);else e=b.value.formatter?b.value.formatter(b.value.value,!0):b.value.value;c.setText(e);c.menu&&c.menu.setValue(b.limit.value)}.createDelegate(this);c("connections",{value:{value:a.num_connections},limit:{value:a.max_num_connections},format:"{0} ({1})"}); +c("downspeed",{value:{value:a.download_rate,formatter:Deluge.Formatters.speed},limit:{value:a.max_download,formatter:b},format:"{0} ({1})"});c("upspeed",{value:{value:a.upload_rate,formatter:Deluge.Formatters.speed},limit:{value:a.max_upload,formatter:b},format:"{0} ({1})"});c("traffic",{value:{value:a.download_protocol_rate,formatter:Deluge.Formatters.speed},limit:{value:a.upload_protocol_rate,formatter:Deluge.Formatters.speed},format:"{0}/{1}"});this.items.get("statusbar-dht").setText(a.dht_nodes); +this.items.get("statusbar-freespace").setText(0<=a.free_space?fsize(a.free_space):_("Error"));this.items.get("statusbar-externalip").setText(String.format(_("<b>IP</b> {0}"),a.external_ip?a.external_ip:_("n/a")))}}}); +Deluge.Toolbar=Ext.extend(Ext.Toolbar,{constructor:function(a){a=Ext.apply({items:[{id:"tbar-deluge-text",text:_("Deluge"),iconCls:"x-deluge-main-panel",handler:this.onAboutClick},new Ext.Toolbar.Separator,{id:"create",disabled:!0,hidden:!0,text:_("Create"),iconCls:"icon-create",handler:this.onTorrentAction},{id:"add",disabled:!0,text:_("Add"),iconCls:"icon-add",handler:this.onTorrentAdd},{id:"remove",disabled:!0,text:_("Remove"),iconCls:"icon-remove",handler:this.onTorrentAction},new Ext.Toolbar.Separator, +{id:"pause",disabled:!0,text:_("Pause"),iconCls:"icon-pause",handler:this.onTorrentAction},{id:"resume",disabled:!0,text:_("Resume"),iconCls:"icon-resume",handler:this.onTorrentAction},new Ext.Toolbar.Separator,{id:"up",cls:"x-btn-text-icon",disabled:!0,text:_("Up"),iconCls:"icon-up",handler:this.onTorrentAction},{id:"down",disabled:!0,text:_("Down"),iconCls:"icon-down",handler:this.onTorrentAction},new Ext.Toolbar.Separator,{id:"preferences",text:_("Preferences"),iconCls:"x-deluge-preferences",handler:this.onPreferencesClick, +scope:this},{id:"connectionman",text:_("Connection Manager"),iconCls:"x-deluge-connection-manager",handler:this.onConnectionManagerClick,scope:this},"->",{id:"help",iconCls:"icon-help",text:_("Help"),handler:this.onHelpClick,scope:this},{id:"logout",iconCls:"icon-logout",disabled:!0,text:_("Logout"),handler:this.onLogout,scope:this}]},a);Deluge.Toolbar.superclass.constructor.call(this,a)},connectedButtons:"add remove pause resume up down".split(" "),initComponent:function(){Deluge.Toolbar.superclass.initComponent.call(this); +deluge.events.on("connect",this.onConnect,this);deluge.events.on("login",this.onLogin,this)},onConnect:function(){Ext.each(this.connectedButtons,function(a){this.items.get(a).enable()},this)},onDisconnect:function(){Ext.each(this.connectedButtons,function(a){this.items.get(a).disable()},this)},onLogin:function(){this.items.get("logout").enable()},onLogout:function(){this.items.get("logout").disable();deluge.login.logout()},onConnectionManagerClick:function(){deluge.connectionManager.show()},onHelpClick:function(){window.open("http://dev.deluge-torrent.org/wiki/UserGuide")}, +onAboutClick:function(){(new Deluge.about.AboutWindow).show()},onPreferencesClick:function(){deluge.preferences.show()},onTorrentAction:function(a){var b=deluge.torrents.getSelections(),c=[];Ext.each(b,function(a){c.push(a.id)});switch(a.id){case "remove":deluge.removeWindow.show(c);break;case "pause":case "resume":deluge.client.core[a.id+"_torrent"](c,{success:function(){deluge.ui.update()}});break;case "up":case "down":deluge.client.core["queue_"+a.id](c,{success:function(){deluge.ui.update()}})}}, +onTorrentAdd:function(){deluge.add.show()}}); +(function(){function a(a){if(a)return fspeed(a)}function b(a){return-1==a?"":fspeed(1024*a)}function c(a,b,c){return 0>a?"∞":parseFloat((new Number(a)).toFixed(3))}function d(a){return 0<a?fdate(a):_("Never")}Deluge.TorrentGrid=Ext.extend(Ext.grid.GridPanel,{torrents:{},columns:[{id:"queue",header:"#",width:30,sortable:!0,renderer:function(a){return-1==a?"":a+1},dataIndex:"queue"},{id:"name",header:_("Name"),width:150,sortable:!0,renderer:function(a,b,c){return String.format('<div class="torrent-name x-deluge-{0}">{1}</div>', +c.data.state.toLowerCase(),Ext.util.Format.htmlEncode(a))},dataIndex:"name"},{header:_("Size"),width:75,sortable:!0,renderer:fsize,dataIndex:"total_wanted"},{header:_("Progress"),width:150,sortable:!0,renderer:function(a,b,c){a=new Number(a);c=_(c.data.state)+" "+a.toFixed(2)+"%";b=new Number((this.style?this.style:b.style).match(/\w+:\s*(\d+)\w+/)[1]);return Deluge.progressBar(a,b-8,c)},dataIndex:"progress"},{header:_("Seeds"),hidden:!0,width:60,sortable:!0,renderer:function(a,b,c){return-1<c.data.total_seeds? +String.format("{0} ({1})",a,c.data.total_seeds):a},dataIndex:"num_seeds"},{header:_("Peers"),hidden:!0,width:60,sortable:!0,renderer:function(a,b,c){return-1<c.data.total_peers?String.format("{0} ({1})",a,c.data.total_peers):a},dataIndex:"num_peers"},{header:_("Down Speed"),width:80,sortable:!0,renderer:a,dataIndex:"download_payload_rate"},{header:_("Up Speed"),width:80,sortable:!0,renderer:a,dataIndex:"upload_payload_rate"},{header:_("ETA"),width:60,sortable:!0,renderer:function(a){return 0===a? +"":-1>=a?"∞":ftime(a)},dataIndex:"eta"},{header:_("Ratio"),hidden:!0,width:60,sortable:!0,renderer:c,dataIndex:"ratio"},{header:_("Avail"),hidden:!0,width:60,sortable:!0,renderer:c,dataIndex:"distributed_copies"},{header:_("Added"),hidden:!0,width:80,sortable:!0,renderer:fdate,dataIndex:"time_added"},{header:_("Complete Seen"),hidden:!0,width:80,sortable:!0,renderer:d,dataIndex:"last_seen_complete"},{header:_("Completed"),hidden:!0,width:80,sortable:!0,renderer:d,dataIndex:"completed_time"}, +{header:_("Tracker"),hidden:!0,width:120,sortable:!0,renderer:function(a,b,c){return String.format('<div style="background: url('+deluge.config.base+'tracker/{0}) no-repeat; padding-left: 20px;">{0}</div>',Ext.util.Format.htmlEncode(a))},dataIndex:"tracker_host"},{header:_("Download Folder"),hidden:!0,width:120,sortable:!0,renderer:fplain,dataIndex:"download_location"},{header:_("Owner"),width:80,sortable:!0,renderer:fplain,dataIndex:"owner"},{header:_("Public"),hidden:!0,width:80,sortable:!0,renderer:fplain, +dataIndex:"public"},{header:_("Shared"),hidden:!0,width:80,sortable:!0,renderer:fplain,dataIndex:"shared"},{header:_("Downloaded"),hidden:!0,width:75,sortable:!0,renderer:fsize,dataIndex:"total_done"},{header:_("Uploaded"),hidden:!0,width:75,sortable:!0,renderer:fsize,dataIndex:"total_uploaded"},{header:_("Remaining"),hidden:!0,width:75,sortable:!0,renderer:fsize,dataIndex:"total_remaining"},{header:_("Down Limit"),hidden:!0,width:75,sortable:!0,renderer:b,dataIndex:"max_download_speed"},{header:_("Up Limit"), +hidden:!0,width:75,sortable:!0,renderer:b,dataIndex:"max_upload_speed"},{header:_("Seeds:Peers"),hidden:!0,width:75,sortable:!0,renderer:c,dataIndex:"seeds_peers_ratio"},{header:_("Last Transfer"),hidden:!0,width:75,sortable:!0,renderer:ftime,dataIndex:"time_since_transfer"}],meta:{root:"torrents",idProperty:"id",fields:[{name:"queue",sortType:Deluge.data.SortTypes.asQueuePosition},{name:"name",sortType:Deluge.data.SortTypes.asName},{name:"total_wanted",type:"int"},{name:"state"},{name:"progress", +type:"float"},{name:"num_seeds",type:"int"},{name:"total_seeds",type:"int"},{name:"num_peers",type:"int"},{name:"total_peers",type:"int"},{name:"download_payload_rate",type:"int"},{name:"upload_payload_rate",type:"int"},{name:"eta",type:"int",sortType:function(a){return 0===a?Number.MAX_VALUE:-1>=a?Number.MAX_SAFE_INTEGER:a}},{name:"ratio",type:"float"},{name:"distributed_copies",type:"float"},{name:"time_added",type:"int"},{name:"last_seen_complete",type:"int"},{name:"completed_time",type:"int"}, +{name:"tracker_host"},{name:"download_location"},{name:"total_done",type:"int"},{name:"total_uploaded",type:"int"},{name:"total_remaining",type:"int"},{name:"max_download_speed",type:"int"},{name:"max_upload_speed",type:"int"},{name:"seeds_peers_ratio",type:"float"},{name:"time_since_transfer",type:"int"}]},keys:[{key:"a",ctrl:!0,stopEvent:!0,handler:function(){deluge.torrents.getSelectionModel().selectAll()}},{key:[46],stopEvent:!0,handler:function(){ids=deluge.torrents.getSelectedIds();deluge.removeWindow.show(ids)}}], +constructor:function(a){a=Ext.apply({id:"torrentGrid",store:new Ext.data.JsonStore(this.meta),columns:this.columns,keys:this.keys,region:"center",cls:"deluge-torrents",stripeRows:!0,autoExpandColumn:"name",autoExpandMin:150,deferredRender:!1,autoScroll:!0,stateful:!0,view:new Ext.ux.grid.BufferView({rowHeight:26,scrollDelay:!1})},a);Deluge.TorrentGrid.superclass.constructor.call(this,a)},initComponent:function(){Deluge.TorrentGrid.superclass.initComponent.call(this);deluge.events.on("torrentsRemoved", +this.onTorrentsRemoved,this);deluge.events.on("disconnect",this.onDisconnect,this);this.on("rowcontextmenu",function(a,b,c){c.stopEvent();a=a.getSelectionModel();a.isSelected(b)||a.selectRow(b);deluge.menus.torrent.showAt(c.getPoint())})},getTorrent:function(a){return this.getStore().getAt(a)},getSelected:function(){return this.getSelectionModel().getSelected()},getSelections:function(){return this.getSelectionModel().getSelections()},getSelectedId:function(){return this.getSelectionModel().getSelected().id}, +getSelectedIds:function(){var a=[];Ext.each(this.getSelectionModel().getSelections(),function(b){a.push(b.id)});return a},update:function(a,b){var c=this.getStore();b&&(c.removeAll(),this.torrents={});var d=[],h;for(h in a){var m=a[h];if(this.torrents[h]){var k=c.getById(h);k.beginEdit();for(var n in m)k.get(n)!=m[n]&&k.set(n,m[n]);k.endEdit()}else k=new Deluge.data.Torrent(m),k.id=h,this.torrents[h]=1,d.push(k)}c.add(d);c.each(function(b){a[b.id]||(c.remove(b),delete this.torrents[b.id])},this); +c.commitChanges();(d=c.getSortState())&&c.sort(d.field,d.direction)},onDisconnect:function(){this.getStore().removeAll();this.torrents={}},onTorrentsRemoved:function(a){var b=this.getSelectionModel();Ext.each(a,function(a){var c=this.getStore().getById(a);b.isSelected(c)&&b.deselectRow(this.getStore().indexOf(c));this.getStore().remove(c);delete this.torrents[a]},this)}});deluge.torrents=new Deluge.TorrentGrid})(); +var TORRENT_STATE_TRANSLATION=[_("All"),_("Active"),_("Allocating"),_("Checking"),_("Downloading"),_("Seeding"),_("Paused"),_("Checking"),_("Queued"),_("Error")]; +deluge.ui={errorCount:0,filters:null,initialize:function(){deluge.add=new Deluge.add.AddWindow;deluge.details=new Deluge.details.DetailsPanel;deluge.connectionManager=new Deluge.ConnectionManager;deluge.editTrackers=new Deluge.EditTrackersWindow;deluge.login=new Deluge.LoginWindow;deluge.preferences=new Deluge.preferences.PreferencesWindow;deluge.sidebar=new Deluge.Sidebar;deluge.statusbar=new Deluge.Statusbar;deluge.toolbar=new Deluge.Toolbar;this.detailsPanel=new Ext.Panel({id:"detailsPanel",cls:"detailsPanel", +region:"south",split:!0,height:215,minSize:100,collapsible:!0,layout:"fit",items:[deluge.details]});this.MainPanel=new Ext.Panel({id:"mainPanel",iconCls:"x-deluge-main-panel",layout:"border",border:!1,tbar:deluge.toolbar,items:[deluge.sidebar,this.detailsPanel,deluge.torrents],bbar:deluge.statusbar});this.Viewport=new Ext.Viewport({layout:"fit",items:[this.MainPanel]});deluge.events.on("connect",this.onConnect,this);deluge.events.on("disconnect",this.onDisconnect,this);deluge.events.on("PluginDisabledEvent", +this.onPluginDisabled,this);deluge.events.on("PluginEnabledEvent",this.onPluginEnabled,this);deluge.client=new Ext.ux.util.RpcClient({url:deluge.config.base+"json"});for(var a in Deluge.pluginStore)a=Deluge.createPlugin(a),a.enable(),deluge.plugins[a.name]=a;Ext.QuickTips.init();deluge.client.on("connected",function(a){deluge.login.show()},this,{single:!0});this.update=this.update.createDelegate(this);this.checkConnection=this.checkConnection.createDelegate(this);this.originalTitle=document.title}, +checkConnection:function(){deluge.client.web.connected({success:this.onConnectionSuccess,failure:this.onConnectionError,scope:this})},update:function(){var a=deluge.sidebar.getFilterStates();this.oldFilters=this.filters;this.filters=a;deluge.client.web.update_ui(Deluge.Keys.Grid,a,{success:this.onUpdate,failure:this.onUpdateError,scope:this});deluge.details.update()},onConnectionError:function(a){},onConnectionSuccess:function(a){deluge.statusbar.setStatus({iconCls:"x-deluge-statusbar icon-ok",text:_("Connection restored")}); +clearInterval(this.checking);a||deluge.connectionManager.show()},onUpdateError:function(a){2==this.errorCount&&(Ext.MessageBox.show({title:_("Lost Connection"),msg:_("The connection to the webserver has been lost!"),buttons:Ext.MessageBox.OK,icon:Ext.MessageBox.ERROR}),deluge.events.fire("disconnect"),deluge.statusbar.setStatus({text:_("Lost connection to webserver")}),this.checking=setInterval(this.checkConnection,2E3));this.errorCount++},onUpdate:function(a){a.connected?(deluge.config.show_session_speed&& +(document.title="D: "+fsize_short(a.stats.download_rate,!0)+" U: "+fsize_short(a.stats.upload_rate,!0)+" - "+this.originalTitle),Ext.areObjectsEqual(this.filters,this.oldFilters)?deluge.torrents.update(a.torrents):deluge.torrents.update(a.torrents,!0),deluge.statusbar.update(a.stats),deluge.sidebar.update(a.filters),this.errorCount=0):deluge.connectionManager.disconnect(!0)},onConnect:function(){this.running||(this.running=setInterval(this.update,2E3),this.update());deluge.client.web.get_plugins({success:this.onGotPlugins, +scope:this})},onDisconnect:function(){this.stop()},onGotPlugins:function(a){Ext.each(a.enabled_plugins,function(a){deluge.plugins[a]||deluge.client.web.get_plugin_resources(a,{success:this.onGotPluginResources,scope:this})},this)},onPluginEnabled:function(a){deluge.plugins[a]?deluge.plugins[a].enable():deluge.client.web.get_plugin_resources(a,{success:this.onGotPluginResources,scope:this})},onGotPluginResources:function(a){Ext.each(Deluge.debug?a.debug_scripts:a.scripts,function(b){Ext.ux.JSLoader({url:deluge.config.base+ +b,onLoad:this.onPluginLoaded,pluginName:a.name})},this)},onPluginDisabled:function(a){deluge.plugins[a]&&deluge.plugins[a].disable()},onPluginLoaded:function(a){Deluge.hasPlugin(a.pluginName)&&(plugin=Deluge.createPlugin(a.pluginName),plugin.enable(),deluge.plugins[plugin.name]=plugin)},stop:function(){this.running&&(clearInterval(this.running),this.running=!1,deluge.torrents.getStore().removeAll())}};Ext.onReady(function(a){deluge.ui.initialize()}); diff --git a/deluge/ui/web/js/deluge-all/AboutWindow.js b/deluge/ui/web/js/deluge-all/AboutWindow.js index 5376d05..cfae7a8 100644 --- a/deluge/ui/web/js/deluge-all/AboutWindow.js +++ b/deluge/ui/web/js/deluge-all/AboutWindow.js @@ -28,16 +28,16 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, { }, buttonAlign: 'center', - initComponent: function() { + initComponent: function () { Deluge.about.AboutWindow.superclass.initComponent.call(this); this.addEvents({ build_ready: true, }); var self = this; - var libtorrent = function() { + var libtorrent = function () { deluge.client.core.get_libtorrent_version({ - success: function(lt_version) { + success: function (lt_version) { comment += '<br/>' + _('libtorrent:') + ' ' + lt_version; Ext.getCmp('about_comment').setText(comment, false); self.fireEvent('build_ready'); @@ -57,10 +57,10 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, { client_version + '<br/>'; deluge.client.web.connected({ - success: function(connected) { + success: function (connected) { if (connected) { deluge.client.daemon.get_version({ - success: function(server_version) { + success: function (server_version) { comment += _('Server:') + ' ' + server_version + '<br/>'; libtorrent(); @@ -70,7 +70,7 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, { this.fireEvent('build_ready'); } }, - failure: function() { + failure: function () { this.fireEvent('build_ready'); }, scope: this, @@ -104,26 +104,25 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, { { xtype: 'label', style: 'padding-top: 5px; font-size: 12px;', - html: - '<a href="https://deluge-torrent.org" target="_blank">deluge-torrent.org</a>', + html: '<a href="https://deluge-torrent.org" target="_blank">deluge-torrent.org</a>', }, ]); this.addButton(_('Close'), this.onCloseClick, this); }, - show: function() { - this.on('build_ready', function() { + show: function () { + this.on('build_ready', function () { Deluge.about.AboutWindow.superclass.show.call(this); }); }, - onCloseClick: function() { + onCloseClick: function () { this.close(); }, }); Ext.namespace('Deluge'); -Deluge.About = function() { +Deluge.About = function () { new Deluge.about.AboutWindow().show(); }; diff --git a/deluge/ui/web/js/deluge-all/AddConnectionWindow.js b/deluge/ui/web/js/deluge-all/AddConnectionWindow.js index 6d26370..4d821f2 100644 --- a/deluge/ui/web/js/deluge-all/AddConnectionWindow.js +++ b/deluge/ui/web/js/deluge-all/AddConnectionWindow.js @@ -24,7 +24,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { bodyStyle: 'padding: 10px 5px;', closeAction: 'hide', - initComponent: function() { + initComponent: function () { Deluge.AddConnectionWindow.superclass.initComponent.call(this); this.addEvents('hostadded'); @@ -80,7 +80,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { }); }, - onAddClick: function() { + onAddClick: function () { var values = this.form.getForm().getValues(); deluge.client.web.add_host( values.host, @@ -88,7 +88,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { values.username, values.password, { - success: function(result) { + success: function (result) { if (!result[0]) { Ext.MessageBox.show({ title: _('Error'), @@ -111,7 +111,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, { ); }, - onHide: function() { + onHide: function () { this.form.getForm().reset(); }, }); diff --git a/deluge/ui/web/js/deluge-all/AddTrackerWindow.js b/deluge/ui/web/js/deluge-all/AddTrackerWindow.js index c9c835d..aaf4a3f 100644 --- a/deluge/ui/web/js/deluge-all/AddTrackerWindow.js +++ b/deluge/ui/web/js/deluge-all/AddTrackerWindow.js @@ -10,9 +10,10 @@ Ext.ns('Deluge'); // Custom VType validator for tracker urls -var trackerUrlTest = /(((^https?)|(^udp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i; +var trackerUrlTest = + /(((^https?)|(^udp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i; Ext.apply(Ext.form.VTypes, { - trackerUrl: function(val, field) { + trackerUrl: function (val, field) { return trackerUrlTest.test(val); }, trackerUrlText: 'Not a valid tracker url', @@ -36,7 +37,7 @@ Deluge.AddTrackerWindow = Ext.extend(Ext.Window, { closeAction: 'hide', iconCls: 'x-deluge-edit-trackers', - initComponent: function() { + initComponent: function () { Deluge.AddTrackerWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancelClick, this); @@ -59,17 +60,14 @@ Deluge.AddTrackerWindow = Ext.extend(Ext.Window, { }); }, - onAddClick: function() { - var trackers = this.form - .getForm() - .findField('trackers') - .getValue(); + onAddClick: function () { + var trackers = this.form.getForm().findField('trackers').getValue(); trackers = trackers.split('\n'); var cleaned = []; Ext.each( trackers, - function(tracker) { + function (tracker) { if (Ext.form.VTypes.trackerUrl(tracker)) { cleaned.push(tracker); } @@ -78,17 +76,11 @@ Deluge.AddTrackerWindow = Ext.extend(Ext.Window, { ); this.fireEvent('add', cleaned); this.hide(); - this.form - .getForm() - .findField('trackers') - .setValue(''); + this.form.getForm().findField('trackers').setValue(''); }, - onCancelClick: function() { - this.form - .getForm() - .findField('trackers') - .setValue(''); + onCancelClick: function () { + this.form.getForm().findField('trackers').setValue(''); this.hide(); }, }); diff --git a/deluge/ui/web/js/deluge-all/Client.js b/deluge/ui/web/js/deluge-all/Client.js index bcabbae..a06e863 100644 --- a/deluge/ui/web/js/deluge-all/Client.js +++ b/deluge/ui/web/js/deluge-all/Client.js @@ -31,7 +31,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { * Fires when the client has retrieved the list of methods from the server. * @param {Ext.ux.util.RpcClient} this */ - constructor: function(config) { + constructor: function (config) { Ext.ux.util.RpcClient.superclass.constructor.call(this, config); this._url = config.url || null; this._id = 0; @@ -44,14 +44,14 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { this.reloadMethods(); }, - reloadMethods: function() { + reloadMethods: function () { this._execute('system.listMethods', { success: this._setMethods, scope: this, }); }, - _execute: function(method, options) { + _execute: function (method, options) { options = options || {}; options.params = options.params || []; options.id = this._id; @@ -74,7 +74,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { }); }, - _onFailure: function(response, requestOptions) { + _onFailure: function (response, requestOptions) { var options = requestOptions.options; errorObj = { id: options.id, @@ -100,7 +100,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { } }, - _onSuccess: function(response, requestOptions) { + _onSuccess: function (response, requestOptions) { var responseObj = Ext.decode(response.responseText); var options = requestOptions.options; if (responseObj.error) { @@ -138,9 +138,9 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { } }, - _parseArgs: function(args) { + _parseArgs: function (args) { var params = []; - Ext.each(args, function(arg) { + Ext.each(args, function (arg) { params.push(arg); }); @@ -149,7 +149,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { var keys = Ext.keys(options), isOption = false; - Ext.each(this._optionKeys, function(key) { + Ext.each(this._optionKeys, function (key) { if (keys.indexOf(key) > -1) isOption = true; }); @@ -165,15 +165,15 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { return options; }, - _setMethods: function(methods) { + _setMethods: function (methods) { var components = {}, self = this; - Ext.each(methods, function(method) { + Ext.each(methods, function (method) { var parts = method.split('.'); var component = components[parts[0]] || {}; - var fn = function() { + var fn = function () { var options = self._parseArgs(arguments); return self._execute(method, options); }; @@ -186,7 +186,7 @@ Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { } Ext.each( this._components, - function(component) { + function (component) { if (!component in components) { delete this[component]; } diff --git a/deluge/ui/web/js/deluge-all/ConnectionManager.js b/deluge/ui/web/js/deluge-all/ConnectionManager.js index 001e46b..5261726 100644 --- a/deluge/ui/web/js/deluge-all/ConnectionManager.js +++ b/deluge/ui/web/js/deluge-all/ConnectionManager.js @@ -21,7 +21,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { title: _('Connection Manager'), iconCls: 'x-deluge-connect-window-icon', - initComponent: function() { + initComponent: function () { Deluge.ConnectionManager.superclass.initComponent.call(this); this.on('hide', this.onHide, this); this.on('show', this.onShow, this); @@ -133,9 +133,9 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { * Check to see if the the web interface is currently connected * to a Deluge Daemon and show the Connection Manager if not. */ - checkConnected: function() { + checkConnected: function () { deluge.client.web.connected({ - success: function(connected) { + success: function (connected) { if (connected) { deluge.events.fire('connect'); } else { @@ -146,7 +146,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }); }, - disconnect: function(show) { + disconnect: function (show) { deluge.events.fire('disconnect'); if (show) { if (this.isVisible()) return; @@ -154,15 +154,15 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { } }, - loadHosts: function() { + loadHosts: function () { deluge.client.web.get_hosts({ success: this.onGetHosts, scope: this, }); }, - update: function() { - this.list.getStore().each(function(r) { + update: function () { + this.list.getStore().each(function (r) { deluge.client.web.get_host_status(r.id, { success: this.onGetHostStatus, scope: this, @@ -175,7 +175,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { * passed in records host state. * @param {Ext.data.Record} record The hosts record to update the UI for */ - updateButtons: function(record) { + updateButtons: function (record) { var button = this.buttons[1], status = record.get('status'); @@ -209,7 +209,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onAddClick: function(button, e) { + onAddClick: function (button, e) { if (!this.addWindow) { this.addWindow = new Deluge.AddConnectionWindow(); this.addWindow.on('hostadded', this.onHostChange, this); @@ -218,7 +218,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onEditClick: function(button, e) { + onEditClick: function (button, e) { var connection = this.list.getSelectedRecords()[0]; if (!connection) return; @@ -230,24 +230,24 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onHostChange: function() { + onHostChange: function () { this.loadHosts(); }, // private - onClose: function(e) { + onClose: function (e) { this.hide(); }, // private - onConnect: function(e) { + onConnect: function (e) { var selected = this.list.getSelectedRecords()[0]; if (!selected) return; var me = this; - var disconnect = function() { + var disconnect = function () { deluge.client.web.disconnect({ - success: function(result) { + success: function (result) { this.update(this); deluge.events.fire('disconnect'); }, @@ -268,11 +268,11 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { var id = selected.id; deluge.client.web.connect(id, { - success: function(methods) { + success: function (methods) { deluge.client.reloadMethods(); deluge.client.on( 'connected', - function(e) { + function (e) { deluge.events.fire('connect'); }, this, @@ -285,11 +285,11 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onGetHosts: function(hosts) { + onGetHosts: function (hosts) { this.list.getStore().loadData(hosts); Ext.each( hosts, - function(host) { + function (host) { deluge.client.web.get_host_status(host[0], { success: this.onGetHostStatus, scope: this, @@ -300,7 +300,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onGetHostStatus: function(host) { + onGetHostStatus: function (host) { var record = this.list.getStore().getById(host[0]); record.set('status', host[1]); record.set('version', host[2]); @@ -311,19 +311,19 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onHide: function() { + onHide: function () { if (this.running) window.clearInterval(this.running); }, // private - onLogin: function() { + onLogin: function () { if (deluge.config.first_login) { Ext.MessageBox.confirm( _('Change Default Password'), _( 'We recommend changing the default password.<br><br>Would you like to change it now?' ), - function(res) { + function (res) { this.checkConnected(); if (res == 'yes') { deluge.preferences.show(); @@ -339,7 +339,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onLogout: function() { + onLogout: function () { this.disconnect(); if (!this.hidden && this.rendered) { this.hide(); @@ -347,12 +347,12 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onRemoveClick: function(button) { + onRemoveClick: function (button) { var connection = this.list.getSelectedRecords()[0]; if (!connection) return; deluge.client.web.remove_host(connection.id, { - success: function(result) { + success: function (result) { if (!result) { Ext.MessageBox.show({ title: _('Error'), @@ -371,7 +371,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onSelectionChanged: function(list, selections) { + onSelectionChanged: function (list, selections) { if (selections[0]) { this.editHostButton.enable(); this.removeHostButton.enable(); @@ -387,7 +387,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { // FIXME: Find out why this is being fired twice // private - onShow: function() { + onShow: function () { if (!this.addHostButton) { var bbar = this.panel.getBottomToolbar(); this.addHostButton = bbar.items.get('cm-add'); @@ -401,7 +401,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { }, // private - onStopClick: function(button, e) { + onStopClick: function (button, e) { var connection = this.list.getSelectedRecords()[0]; if (!connection) return; @@ -411,7 +411,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, { } else { // This means we need to stop the daemon deluge.client.web.stop_daemon(connection.id, { - success: function(result) { + success: function (result) { if (!result[0]) { Ext.MessageBox.show({ title: _('Error'), diff --git a/deluge/ui/web/js/deluge-all/CopyMagnetWindow.js b/deluge/ui/web/js/deluge-all/CopyMagnetWindow.js new file mode 100644 index 0000000..ddcd4ab --- /dev/null +++ b/deluge/ui/web/js/deluge-all/CopyMagnetWindow.js @@ -0,0 +1,73 @@ +/* + * Deluge.CopyMagnet.js + * + * Copyright (c) Damien Churchill 2009-2010 <damoxc@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to: + * The Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the OpenSSL + * library. + * You must obey the GNU General Public License in all respects for all of + * the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete + * this exception statement from your version. If you delete this exception + * statement from all source files in the program, then also delete it here. + */ +Deluge.CopyMagnet = Ext.extend(Ext.Window, { + title: _('Copy Magnet URI'), + width: 375, + closeAction: 'hide', + iconCls: 'icon-magnet-copy', + + initComponent: function () { + Deluge.CopyMagnet.superclass.initComponent.call(this); + form = this.add({ + xtype: 'form', + defaultType: 'textfield', + hideLabels: true, + }); + this.magnetURI = form.add({ + name: 'URI', + anchor: '100%', + }); + this.addButton(_('Close'), this.onClose, this); + this.addButton(_('Copy'), this.onCopy, this); + }, + show: function (a) { + Deluge.CopyMagnet.superclass.show.call(this); + var torrent = deluge.torrents.getSelected(); + deluge.client.core.get_magnet_uri(torrent.id, { + success: this.onRequestComplete, + scope: this, + }); + }, + onRequestComplete: function (uri) { + this.magnetURI.setValue(uri); + }, + onCopy: function () { + this.magnetURI.focus(); + this.magnetURI.el.dom.select(); + document.execCommand('copy'); + }, + onClose: function () { + this.hide(); + }, +}); + +deluge.copyMagnetWindow = new Deluge.CopyMagnet(); diff --git a/deluge/ui/web/js/deluge-all/Deluge.js b/deluge/ui/web/js/deluge-all/Deluge.js index 31b9947..260ad97 100644 --- a/deluge/ui/web/js/deluge-all/Deluge.js +++ b/deluge/ui/web/js/deluge-all/Deluge.js @@ -25,21 +25,14 @@ Ext.state.Manager.setProvider( // Add some additional functions to ext and setup some of the // configurable parameters Ext.apply(Ext, { - escapeHTML: function(text) { - text = String(text) - .replace('<', '<') - .replace('>', '>'); - return text.replace('&', '&'); - }, - - isObjectEmpty: function(obj) { + isObjectEmpty: function (obj) { for (var i in obj) { return false; } return true; }, - areObjectsEqual: function(obj1, obj2) { + areObjectsEqual: function (obj1, obj2) { var equal = true; if (!obj1 || !obj2) return false; for (var i in obj1) { @@ -50,7 +43,7 @@ Ext.apply(Ext, { return equal; }, - keys: function(obj) { + keys: function (obj) { var keys = []; for (var i in obj) if (obj.hasOwnProperty(i)) { @@ -59,7 +52,7 @@ Ext.apply(Ext, { return keys; }, - values: function(obj) { + values: function (obj) { var values = []; for (var i in obj) { if (obj.hasOwnProperty(i)) { @@ -69,7 +62,7 @@ Ext.apply(Ext, { return values; }, - splat: function(obj) { + splat: function (obj) { var type = Ext.type(obj); return type ? (type != 'array' ? [obj] : obj) : []; }, @@ -106,7 +99,7 @@ Ext.apply(Deluge, { * @param {String} text The text to display on the bar * @param {Number} modified Amount to subtract from the width allowing for fixes */ - progressBar: function(progress, width, text, modifier) { + progressBar: function (progress, width, text, modifier) { modifier = Ext.value(modifier, 10); var progressWidth = ((width / 100.0) * progress).toFixed(0); var barWidth = progressWidth - 1; @@ -125,7 +118,7 @@ Ext.apply(Deluge, { * Constructs a new instance of the specified plugin. * @param {String} name The plugin name to create */ - createPlugin: function(name) { + createPlugin: function (name) { return new Deluge.pluginStore[name](); }, @@ -133,7 +126,7 @@ Ext.apply(Deluge, { * Check to see if a plugin has been registered. * @param {String} name The plugin name to check */ - hasPlugin: function(name) { + hasPlugin: function (name) { return Deluge.pluginStore[name] ? true : false; }, @@ -142,7 +135,7 @@ Ext.apply(Deluge, { * @param {String} name The plugin name to register * @param {Plugin} plugin The plugin to register */ - registerPlugin: function(name, plugin) { + registerPlugin: function (name, plugin) { Deluge.pluginStore[name] = plugin; }, }); diff --git a/deluge/ui/web/js/deluge-all/EditConnectionWindow.js b/deluge/ui/web/js/deluge-all/EditConnectionWindow.js index 63bd305..bfeb38f 100644 --- a/deluge/ui/web/js/deluge-all/EditConnectionWindow.js +++ b/deluge/ui/web/js/deluge-all/EditConnectionWindow.js @@ -24,7 +24,7 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { bodyStyle: 'padding: 10px 5px;', closeAction: 'hide', - initComponent: function() { + initComponent: function () { Deluge.EditConnectionWindow.superclass.initComponent.call(this); this.addEvents('hostedited'); @@ -80,17 +80,11 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { }); }, - show: function(connection) { + show: function (connection) { Deluge.EditConnectionWindow.superclass.show.call(this); - this.form - .getForm() - .findField('host') - .setValue(connection.get('host')); - this.form - .getForm() - .findField('port') - .setValue(connection.get('port')); + this.form.getForm().findField('host').setValue(connection.get('host')); + this.form.getForm().findField('port').setValue(connection.get('port')); this.form .getForm() .findField('username') @@ -98,7 +92,7 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { this.host_id = connection.id; }, - onEditClick: function() { + onEditClick: function () { var values = this.form.getForm().getValues(); deluge.client.web.edit_host( this.host_id, @@ -107,7 +101,7 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { values.username, values.password, { - success: function(result) { + success: function (result) { if (!result) { console.log(result); Ext.MessageBox.show({ @@ -128,7 +122,7 @@ Deluge.EditConnectionWindow = Ext.extend(Ext.Window, { ); }, - onHide: function() { + onHide: function () { this.form.getForm().reset(); }, }); diff --git a/deluge/ui/web/js/deluge-all/EditTrackerWindow.js b/deluge/ui/web/js/deluge-all/EditTrackerWindow.js index 82bc32c..646b8de 100644 --- a/deluge/ui/web/js/deluge-all/EditTrackerWindow.js +++ b/deluge/ui/web/js/deluge-all/EditTrackerWindow.js @@ -27,7 +27,7 @@ Deluge.EditTrackerWindow = Ext.extend(Ext.Window, { closeAction: 'hide', iconCls: 'x-deluge-edit-trackers', - initComponent: function() { + initComponent: function () { Deluge.EditTrackerWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancelClick, this); @@ -50,32 +50,23 @@ Deluge.EditTrackerWindow = Ext.extend(Ext.Window, { }); }, - show: function(record) { + show: function (record) { Deluge.EditTrackerWindow.superclass.show.call(this); this.record = record; - this.form - .getForm() - .findField('tracker') - .setValue(record.data['url']); + this.form.getForm().findField('tracker').setValue(record.data['url']); }, - onCancelClick: function() { + onCancelClick: function () { this.hide(); }, - onHide: function() { - this.form - .getForm() - .findField('tracker') - .setValue(''); + onHide: function () { + this.form.getForm().findField('tracker').setValue(''); }, - onSaveClick: function() { - var url = this.form - .getForm() - .findField('tracker') - .getValue(); + onSaveClick: function () { + var url = this.form.getForm().findField('tracker').getValue(); this.record.set('url', url); this.record.commit(); this.hide(); diff --git a/deluge/ui/web/js/deluge-all/EditTrackersWindow.js b/deluge/ui/web/js/deluge-all/EditTrackersWindow.js index 47ffa86..178fd58 100644 --- a/deluge/ui/web/js/deluge-all/EditTrackersWindow.js +++ b/deluge/ui/web/js/deluge-all/EditTrackersWindow.js @@ -10,7 +10,7 @@ Ext.ns('Deluge'); /** - * @class Deluge.EditTrackerWindow + * @class Deluge.EditTrackersWindow * @extends Ext.Window */ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { @@ -28,7 +28,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { closeAction: 'hide', iconCls: 'x-deluge-edit-trackers', - initComponent: function() { + initComponent: function () { Deluge.EditTrackersWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancelClick, this); @@ -57,6 +57,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { header: _('Tracker'), width: 0.9, dataIndex: 'url', + tpl: new Ext.XTemplate('{url:htmlEncode}'), }, ], columnSort: { @@ -111,18 +112,18 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { }); }, - onAddClick: function() { + onAddClick: function () { this.addWindow.show(); }, - onAddTrackers: function(trackers) { + onAddTrackers: function (trackers) { var store = this.list.getStore(); Ext.each( trackers, - function(tracker) { + function (tracker) { var duplicate = false, heightestTier = -1; - store.each(function(record) { + store.each(function (record) { if (record.get('tier') > heightestTier) { heightestTier = record.get('tier'); } @@ -143,27 +144,27 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { ); }, - onCancelClick: function() { + onCancelClick: function () { this.hide(); }, - onEditClick: function() { + onEditClick: function () { var selected = this.list.getSelectedRecords()[0]; if (!selected) return; this.editWindow.show(selected); }, - onHide: function() { + onHide: function () { this.list.getStore().removeAll(); }, - onListNodeDblClicked: function(list, index, node, e) { + onListNodeDblClicked: function (list, index, node, e) { this.editWindow.show(this.list.getRecord(node)); }, - onOkClick: function() { + onOkClick: function () { var trackers = []; - this.list.getStore().each(function(record) { + this.list.getStore().each(function (record) { trackers.push({ tier: record.get('tier'), url: record.get('url'), @@ -178,34 +179,28 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { this.hide(); }, - onRemoveClick: function() { + onRemoveClick: function () { // Remove from the grid var selected = this.list.getSelectedRecords()[0]; if (!selected) return; this.list.getStore().remove(selected); }, - onRequestComplete: function(status) { + onRequestComplete: function (status) { this.list.getStore().loadData(status); this.list.getStore().sort('tier', 'ASC'); }, - onSaveFail: function() {}, + onSaveFail: function () {}, - onSelect: function(list) { + onSelect: function (list) { if (list.getSelectionCount()) { - this.panel - .getBottomToolbar() - .items.get(4) - .enable(); + this.panel.getBottomToolbar().items.get(4).enable(); } }, - onShow: function() { - this.panel - .getBottomToolbar() - .items.get(4) - .disable(); + onShow: function () { + this.panel.getBottomToolbar().items.get(4).disable(); var r = deluge.torrents.getSelected(); this.torrentId = r.id; deluge.client.core.get_torrent_status(r.id, ['trackers'], { @@ -214,7 +209,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { }); }, - onDownClick: function() { + onDownClick: function () { var r = this.list.getSelectedRecords()[0]; if (!r) return; @@ -225,7 +220,7 @@ Deluge.EditTrackersWindow = Ext.extend(Ext.Window, { this.list.select(r.store.indexOf(r)); }, - onUpClick: function() { + onUpClick: function () { var r = this.list.getSelectedRecords()[0]; if (!r) return; diff --git a/deluge/ui/web/js/deluge-all/EventsManager.js b/deluge/ui/web/js/deluge-all/EventsManager.js index 1714339..89d8980 100644 --- a/deluge/ui/web/js/deluge-all/EventsManager.js +++ b/deluge/ui/web/js/deluge-all/EventsManager.js @@ -15,7 +15,7 @@ * Class for holding global events that occur within the UI. */ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { - constructor: function() { + constructor: function () { this.toRegister = []; this.on('login', this.onLogin, this); Deluge.EventsManager.superclass.constructor.call(this); @@ -24,7 +24,7 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { /** * Append an event handler to this object. */ - addListener: function(eventName, fn, scope, o) { + addListener: function (eventName, fn, scope, o) { this.addEvents(eventName); if (/[A-Z]/.test(eventName.substring(0, 1))) { if (!deluge.client) { @@ -42,7 +42,7 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { ); }, - getEvents: function() { + getEvents: function () { deluge.client.web.get_events({ success: this.onGetEventsSuccess, failure: this.onGetEventsFailure, @@ -53,8 +53,8 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { /** * Starts the EventsManagerManager checking for events. */ - start: function() { - Ext.each(this.toRegister, function(eventName) { + start: function () { + Ext.each(this.toRegister, function (eventName) { deluge.client.web.register_event_listener(eventName); }); this.running = true; @@ -65,21 +65,21 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { /** * Stops the EventsManagerManager checking for events. */ - stop: function() { + stop: function () { this.running = false; }, // private - onLogin: function() { + onLogin: function () { this.start(); }, - onGetEventsSuccess: function(events) { + onGetEventsSuccess: function (events) { if (!this.running) return; if (events) { Ext.each( events, - function(event) { + function (event) { var name = event[0], args = event[1]; args.splice(0, 0, name); @@ -92,7 +92,7 @@ Deluge.EventsManager = Ext.extend(Ext.util.Observable, { }, // private - onGetEventsFailure: function(result, error) { + onGetEventsFailure: function (result, error) { // the request timed out or we had a communication failure if (!this.running) return; if (!error.isTimeout && this.errorCount++ >= 3) { diff --git a/deluge/ui/web/js/deluge-all/FileBrowser.js b/deluge/ui/web/js/deluge-all/FileBrowser.js index 72962a6..2afe1b1 100644 --- a/deluge/ui/web/js/deluge-all/FileBrowser.js +++ b/deluge/ui/web/js/deluge-all/FileBrowser.js @@ -15,7 +15,7 @@ Deluge.FileBrowser = Ext.extend(Ext.Window, { width: 500, height: 400, - initComponent: function() { + initComponent: function () { Deluge.FileBrowser.superclass.initComponent.call(this); this.add({ diff --git a/deluge/ui/web/js/deluge-all/FilterPanel.js b/deluge/ui/web/js/deluge-all/FilterPanel.js index 2362dbb..f1fade1 100644 --- a/deluge/ui/web/js/deluge-all/FilterPanel.js +++ b/deluge/ui/web/js/deluge-all/FilterPanel.js @@ -20,7 +20,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { show_zero: null, - initComponent: function() { + initComponent: function () { Deluge.FilterPanel.superclass.initComponent.call(this); this.filterType = this.initialConfig.filter; var title = ''; @@ -36,7 +36,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { (title = this.filterType.replace('_', ' ')), (parts = title.split(' ')), (title = ''); - Ext.each(parts, function(p) { + Ext.each(parts, function (p) { fl = p.substring(0, 1).toUpperCase(); title += fl + p.substring(1) + ' '; }); @@ -75,7 +75,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { * Return the currently selected filter state * @returns {String} the current filter state */ - getState: function() { + getState: function () { if (!this.list.getSelectionCount()) return; var state = this.list.getSelectedRecords()[0]; @@ -87,7 +87,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { /** * Return the current states in the filter */ - getStates: function() { + getStates: function () { return this.states; }, @@ -95,18 +95,18 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { * Return the Store for the ListView of the FilterPanel * @returns {Ext.data.Store} the ListView store */ - getStore: function() { + getStore: function () { return this.list.getStore(); }, /** * Update the states in the FilterPanel */ - updateStates: function(states) { + updateStates: function (states) { this.states = {}; Ext.each( states, - function(state) { + function (state) { this.states[state[0]] = state[1]; }, this @@ -118,7 +118,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { : this.show_zero; if (!show_zero) { var newStates = []; - Ext.each(states, function(state) { + Ext.each(states, function (state) { if (state[1] > 0 || state[0] == 'All') { newStates.push(state); } @@ -130,7 +130,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { var filters = {}; Ext.each( states, - function(s, i) { + function (s, i) { var record = store.getById(s[0]); if (!record) { record = new store.recordType({ @@ -149,7 +149,7 @@ Deluge.FilterPanel = Ext.extend(Ext.Panel, { this ); - store.each(function(record) { + store.each(function (record) { if (filters[record.id]) return; store.remove(record); var selected = this.list.getSelectedRecords()[0]; @@ -171,5 +171,5 @@ Deluge.FilterPanel.templates = { tracker_host: '<div class="x-deluge-filter" style="background-image: url(' + deluge.config.base + - 'tracker/{filter});">{filter} ({count})</div>', + 'tracker/{filter});">{filter:htmlEncode} ({count})</div>', }; diff --git a/deluge/ui/web/js/deluge-all/Formatters.js b/deluge/ui/web/js/deluge-all/Formatters.js index a511f34..6b09abe 100644 --- a/deluge/ui/web/js/deluge-all/Formatters.js +++ b/deluge/ui/web/js/deluge-all/Formatters.js @@ -15,7 +15,23 @@ * @version 1.3 * @singleton */ -Deluge.Formatters = { +Deluge.Formatters = (function () { + var charToEntity = { + '&': '&', + '>': '>', + '<': '<', + '"': '"', + "'": ''', + }; + + var charToEntityRegex = new RegExp( + '(' + Object.keys(charToEntity).join('|') + ')', + 'g' + ); + var htmlEncodeReplaceFn = function (match, capture) { + return charToEntity[capture]; + }; + /** * Formats a date string in the date representation of the current locale, * based on the systems timezone. @@ -24,154 +40,162 @@ Deluge.Formatters = { * @return {String} a string in the date representation of the current locale * or "" if seconds < 0. */ - date: function(timestamp) { - function zeroPad(num, count) { - var numZeropad = num + ''; - while (numZeropad.length < count) { - numZeropad = '0' + numZeropad; + return (Formatters = { + date: function (timestamp) { + function zeroPad(num, count) { + var numZeropad = num + ''; + while (numZeropad.length < count) { + numZeropad = '0' + numZeropad; + } + return numZeropad; + } + timestamp = timestamp * 1000; + var date = new Date(timestamp); + return String.format( + '{0}/{1}/{2} {3}:{4}:{5}', + zeroPad(date.getDate(), 2), + zeroPad(date.getMonth() + 1, 2), + date.getFullYear(), + zeroPad(date.getHours(), 2), + zeroPad(date.getMinutes(), 2), + zeroPad(date.getSeconds(), 2) + ); + }, + + /** + * Formats the bytes value into a string with KiB, MiB or GiB units. + * + * @param {Number} bytes the filesize in bytes + * @param {Boolean} showZero pass in true to displays 0 values + * @return {String} formatted string with KiB, MiB or GiB units. + */ + size: function (bytes, showZero) { + if (!bytes && !showZero) return ''; + bytes = bytes / 1024.0; + + if (bytes < 1024) { + return bytes.toFixed(1) + ' KiB'; + } else { + bytes = bytes / 1024; } - return numZeropad; - } - timestamp = timestamp * 1000; - var date = new Date(timestamp); - return String.format( - '{0}/{1}/{2} {3}:{4}:{5}', - zeroPad(date.getDate(), 2), - zeroPad(date.getMonth() + 1, 2), - date.getFullYear(), - zeroPad(date.getHours(), 2), - zeroPad(date.getMinutes(), 2), - zeroPad(date.getSeconds(), 2) - ); - }, - - /** - * Formats the bytes value into a string with KiB, MiB or GiB units. - * - * @param {Number} bytes the filesize in bytes - * @param {Boolean} showZero pass in true to displays 0 values - * @return {String} formatted string with KiB, MiB or GiB units. - */ - size: function(bytes, showZero) { - if (!bytes && !showZero) return ''; - bytes = bytes / 1024.0; - - if (bytes < 1024) { - return bytes.toFixed(1) + ' KiB'; - } else { - bytes = bytes / 1024; - } - - if (bytes < 1024) { - return bytes.toFixed(1) + ' MiB'; - } else { - bytes = bytes / 1024; - } - - return bytes.toFixed(1) + ' GiB'; - }, - - /** - * Formats the bytes value into a string with K, M or G units. - * - * @param {Number} bytes the filesize in bytes - * @param {Boolean} showZero pass in true to displays 0 values - * @return {String} formatted string with K, M or G units. - */ - sizeShort: function(bytes, showZero) { - if (!bytes && !showZero) return ''; - bytes = bytes / 1024.0; - if (bytes < 1024) { - return bytes.toFixed(1) + ' K'; - } else { - bytes = bytes / 1024; - } + if (bytes < 1024) { + return bytes.toFixed(1) + ' MiB'; + } else { + bytes = bytes / 1024; + } - if (bytes < 1024) { - return bytes.toFixed(1) + ' M'; - } else { - bytes = bytes / 1024; - } + return bytes.toFixed(1) + ' GiB'; + }, + + /** + * Formats the bytes value into a string with K, M or G units. + * + * @param {Number} bytes the filesize in bytes + * @param {Boolean} showZero pass in true to displays 0 values + * @return {String} formatted string with K, M or G units. + */ + sizeShort: function (bytes, showZero) { + if (!bytes && !showZero) return ''; + bytes = bytes / 1024.0; + + if (bytes < 1024) { + return bytes.toFixed(1) + ' K'; + } else { + bytes = bytes / 1024; + } - return bytes.toFixed(1) + ' G'; - }, + if (bytes < 1024) { + return bytes.toFixed(1) + ' M'; + } else { + bytes = bytes / 1024; + } - /** - * Formats a string to display a transfer speed utilizing {@link #size} - * - * @param {Number} bytes the number of bytes per second - * @param {Boolean} showZero pass in true to displays 0 values - * @return {String} formatted string with KiB, MiB or GiB units. - */ - speed: function(bytes, showZero) { - return !bytes && !showZero ? '' : fsize(bytes, showZero) + '/s'; - }, + return bytes.toFixed(1) + ' G'; + }, + + /** + * Formats a string to display a transfer speed utilizing {@link #size} + * + * @param {Number} bytes the number of bytes per second + * @param {Boolean} showZero pass in true to displays 0 values + * @return {String} formatted string with KiB, MiB or GiB units. + */ + speed: function (bytes, showZero) { + return !bytes && !showZero ? '' : fsize(bytes, showZero) + '/s'; + }, + + /** + * Formats a string to show time in a human readable form. + * + * @param {Number} time the number of seconds + * @return {String} a formatted time string. will return '' if seconds == 0 + */ + timeRemaining: function (time) { + if (time <= 0) { + return '∞'; + } + time = time.toFixed(0); + if (time < 60) { + return time + 's'; + } else { + time = time / 60; + } - /** - * Formats a string to show time in a human readable form. - * - * @param {Number} time the number of seconds - * @return {String} a formatted time string. will return '' if seconds == 0 - */ - timeRemaining: function(time) { - if (time <= 0) { - return '∞'; - } - time = time.toFixed(0); - if (time < 60) { - return time + 's'; - } else { - time = time / 60; - } - - if (time < 60) { - var minutes = Math.floor(time); - var seconds = Math.round(60 * (time - minutes)); - if (seconds > 0) { - return minutes + 'm ' + seconds + 's'; + if (time < 60) { + var minutes = Math.floor(time); + var seconds = Math.round(60 * (time - minutes)); + if (seconds > 0) { + return minutes + 'm ' + seconds + 's'; + } else { + return minutes + 'm'; + } } else { - return minutes + 'm'; + time = time / 60; } - } else { - time = time / 60; - } - - if (time < 24) { - var hours = Math.floor(time); - var minutes = Math.round(60 * (time - hours)); - if (minutes > 0) { - return hours + 'h ' + minutes + 'm'; + + if (time < 24) { + var hours = Math.floor(time); + var minutes = Math.round(60 * (time - hours)); + if (minutes > 0) { + return hours + 'h ' + minutes + 'm'; + } else { + return hours + 'h'; + } } else { - return hours + 'h'; + time = time / 24; } - } else { - time = time / 24; - } - - var days = Math.floor(time); - var hours = Math.round(24 * (time - days)); - if (hours > 0) { - return days + 'd ' + hours + 'h'; - } else { - return days + 'd'; - } - }, - /** - * Simply returns the value untouched, for when no formatting is required. - * - * @param {Mixed} value the value to be displayed - * @return the untouched value. - */ - plain: function(value) { - return value; - }, - - cssClassEscape: function(value) { - return value.toLowerCase().replace('.', '_'); - }, -}; + var days = Math.floor(time); + var hours = Math.round(24 * (time - days)); + if (hours > 0) { + return days + 'd ' + hours + 'h'; + } else { + return days + 'd'; + } + }, + + /** + * Simply returns the value untouched, for when no formatting is required. + * + * @param {Mixed} value the value to be displayed + * @return the untouched value. + */ + plain: function (value) { + return value; + }, + + cssClassEscape: function (value) { + return value.toLowerCase().replace('.', '_'); + }, + + htmlEncode: function (value) { + return !value + ? value + : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn); + }, + }); +})(); var fsize = Deluge.Formatters.size; var fsize_short = Deluge.Formatters.sizeShort; var fspeed = Deluge.Formatters.speed; @@ -179,3 +203,4 @@ var ftime = Deluge.Formatters.timeRemaining; var fdate = Deluge.Formatters.date; var fplain = Deluge.Formatters.plain; Ext.util.Format.cssClassEscape = Deluge.Formatters.cssClassEscape; +Ext.util.Format.htmlEncode = Deluge.Formatters.htmlEncode; diff --git a/deluge/ui/web/js/deluge-all/Keys.js b/deluge/ui/web/js/deluge-all/Keys.js index 25cf38b..7b3e3af 100644 --- a/deluge/ui/web/js/deluge-all/Keys.js +++ b/deluge/ui/web/js/deluge-all/Keys.js @@ -133,6 +133,6 @@ Deluge.Keys = { // Merge the grid and status keys together as the status keys contain all the // grid ones. -Ext.each(Deluge.Keys.Grid, function(key) { +Ext.each(Deluge.Keys.Grid, function (key) { Deluge.Keys.Status.push(key); }); diff --git a/deluge/ui/web/js/deluge-all/LoginWindow.js b/deluge/ui/web/js/deluge-all/LoginWindow.js index 964f5ff..a055a69 100644 --- a/deluge/ui/web/js/deluge-all/LoginWindow.js +++ b/deluge/ui/web/js/deluge-all/LoginWindow.js @@ -23,7 +23,7 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { width: 300, height: 120, - initComponent: function() { + initComponent: function () { Deluge.LoginWindow.superclass.initComponent.call(this); this.on('show', this.onShow, this); @@ -56,17 +56,17 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { this.passwordField.on('specialkey', this.onSpecialKey, this); }, - logout: function() { + logout: function () { deluge.events.fire('logout'); deluge.client.auth.delete_session({ - success: function(result) { + success: function (result) { this.show(true); }, scope: this, }); }, - show: function(skipCheck) { + show: function (skipCheck) { if (this.firstShow) { deluge.client.on('error', this.onClientError, this); this.firstShow = false; @@ -77,28 +77,28 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { } deluge.client.auth.check_session({ - success: function(result) { + success: function (result) { if (result) { deluge.events.fire('login'); } else { this.show(true); } }, - failure: function(result) { + failure: function (result) { this.show(true); }, scope: this, }); }, - onSpecialKey: function(field, e) { + onSpecialKey: function (field, e) { if (e.getKey() == 13) this.onLogin(); }, - onLogin: function() { + onLogin: function () { var passwordField = this.passwordField; deluge.client.auth.login(passwordField.getValue(), { - success: function(result) { + success: function (result) { if (result) { deluge.events.fire('login'); this.hide(); @@ -109,7 +109,7 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { msg: _('You entered an incorrect password'), buttons: Ext.MessageBox.OK, modal: false, - fn: function() { + fn: function () { passwordField.focus(true, 10); }, icon: Ext.MessageBox.WARNING, @@ -121,14 +121,14 @@ Deluge.LoginWindow = Ext.extend(Ext.Window, { }); }, - onClientError: function(errorObj, response, requestOptions) { + onClientError: function (errorObj, response, requestOptions) { if (errorObj.error.code == 1) { deluge.events.fire('logout'); this.show(true); } }, - onShow: function() { + onShow: function () { this.passwordField.focus(true, 300); }, }); diff --git a/deluge/ui/web/js/deluge-all/Menus.js b/deluge/ui/web/js/deluge-all/Menus.js index 529c6cc..34550a6 100644 --- a/deluge/ui/web/js/deluge-all/Menus.js +++ b/deluge/ui/web/js/deluge-all/Menus.js @@ -9,7 +9,7 @@ */ deluge.menus = { - onTorrentActionSetOpt: function(item, e) { + onTorrentActionSetOpt: function (item, e) { var ids = deluge.torrents.getSelectedIds(); var action = item.initialConfig.torrentAction; var opts = {}; @@ -17,20 +17,23 @@ deluge.menus = { deluge.client.core.set_torrent_options(ids, opts); }, - onTorrentActionMethod: function(item, e) { + onTorrentActionMethod: function (item, e) { var ids = deluge.torrents.getSelectedIds(); var action = item.initialConfig.torrentAction; deluge.client.core[action](ids, { - success: function() { + success: function () { deluge.ui.update(); }, }); }, - onTorrentActionShow: function(item, e) { + onTorrentActionShow: function (item, e) { var ids = deluge.torrents.getSelectedIds(); var action = item.initialConfig.torrentAction; switch (action) { + case 'copy_magnet': + deluge.copyMagnetWindow.show(); + break; case 'edit_trackers': deluge.editTrackers.show(); break; @@ -312,6 +315,13 @@ deluge.menus.torrent = new Ext.menu.Menu({ }, '-', { + torrentAction: 'copy_magnet', + text: _('Copy Magnet URI'), + iconCls: 'icon-magnet-copy', + handler: deluge.menus.onTorrentActionShow, + scope: deluge.menus, + }, + { torrentAction: 'force_reannounce', text: _('Update Tracker'), iconCls: 'icon-update-tracker', diff --git a/deluge/ui/web/js/deluge-all/MoveStorage.js b/deluge/ui/web/js/deluge-all/MoveStorage.js index 208031f..9ba638a 100644 --- a/deluge/ui/web/js/deluge-all/MoveStorage.js +++ b/deluge/ui/web/js/deluge-all/MoveStorage.js @@ -10,7 +10,7 @@ Ext.namespace('Deluge'); Deluge.MoveStorage = Ext.extend(Ext.Window, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { title: _('Move Download Folder'), @@ -30,7 +30,7 @@ Deluge.MoveStorage = Ext.extend(Ext.Window, { Deluge.MoveStorage.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.MoveStorage.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancel, this); @@ -62,21 +62,21 @@ Deluge.MoveStorage = Ext.extend(Ext.Window, { //}); }, - hide: function() { + hide: function () { Deluge.MoveStorage.superclass.hide.call(this); this.torrentIds = null; }, - show: function(torrentIds) { + show: function (torrentIds) { Deluge.MoveStorage.superclass.show.call(this); this.torrentIds = torrentIds; }, - onCancel: function() { + onCancel: function () { this.hide(); }, - onMove: function() { + onMove: function () { var dest = this.moveLocation.getValue(); deluge.client.core.move_storage(this.torrentIds, dest); this.hide(); diff --git a/deluge/ui/web/js/deluge-all/MultiOptionsManager.js b/deluge/ui/web/js/deluge-all/MultiOptionsManager.js index 1cd7d19..82f9838 100644 --- a/deluge/ui/web/js/deluge-all/MultiOptionsManager.js +++ b/deluge/ui/web/js/deluge-all/MultiOptionsManager.js @@ -15,7 +15,7 @@ * @extends Deluge.OptionsManager */ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { - constructor: function(config) { + constructor: function (config) { this.currentId = null; this.stored = {}; Deluge.MultiOptionsManager.superclass.constructor.call(this, config); @@ -25,7 +25,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * Changes bound fields to use the specified id. * @param {String} id */ - changeId: function(id, dontUpdateBinds) { + changeId: function (id, dontUpdateBinds) { var oldId = this.currentId; this.currentId = id; if (!dontUpdateBinds) { @@ -33,7 +33,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { if (!this.binds[option]) continue; Ext.each( this.binds[option], - function(bind) { + function (bind) { bind.setValue(this.get(option)); }, this @@ -47,7 +47,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * Changes all the changed values to be the default values * @param {String} id */ - commit: function() { + commit: function () { this.stored[this.currentId] = Ext.apply( this.stored[this.currentId], this.changed[this.currentId] @@ -60,7 +60,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String/Array} option A single option or an array of options to return. * @returns {Object} the options value. */ - get: function() { + get: function () { if (arguments.length == 1) { var option = arguments[0]; return this.isDirty(option) @@ -78,7 +78,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { var options = {}; Ext.each( arguments, - function(option) { + function (option) { options[option] = this.isDirty(option) ? this.changed[this.currentId][option] : this.getDefault(option); @@ -94,7 +94,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String} option A single option. * @returns {Object} the value of the option */ - getDefault: function(option) { + getDefault: function (option) { return this.has(option) ? this.stored[this.currentId][option] : this.options[option]; @@ -104,7 +104,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * Returns the dirty (changed) values. * @returns {Object} the changed options */ - getDirty: function() { + getDirty: function () { return this.changed[this.currentId] ? this.changed[this.currentId] : {}; }, @@ -113,7 +113,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String} option * @returns {Boolean} true if the option has been changed, else false. */ - isDirty: function(option) { + isDirty: function (option) { return ( this.changed[this.currentId] && !Ext.isEmpty(this.changed[this.currentId][option]) @@ -126,7 +126,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String} option * @returns {Boolean} true if the id has an option, else false. */ - has: function(option) { + has: function (option) { return ( this.stored[this.currentId] && !Ext.isEmpty(this.stored[this.currentId][option]) @@ -136,7 +136,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { /** * Reset the options back to the default values for the specified id. */ - reset: function() { + reset: function () { if (this.changed[this.currentId]) delete this.changed[this.currentId]; if (this.stored[this.currentId]) delete this.stored[this.currentId]; }, @@ -144,7 +144,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { /** * Reset the options back to their defaults for all ids. */ - resetAll: function() { + resetAll: function () { this.changed = {}; this.stored = {}; this.changeId(null); @@ -156,7 +156,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String} option * @param {Object} value The value for the option */ - setDefault: function(option, value) { + setDefault: function (option, value) { if (option === undefined) { return; } else if (value === undefined) { @@ -187,7 +187,7 @@ Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { * @param {String/Object} option or options to update * @param {Object} [value]; */ - update: function(option, value) { + update: function (option, value) { if (option === undefined) { return; } else if (value === undefined) { diff --git a/deluge/ui/web/js/deluge-all/OptionsManager.js b/deluge/ui/web/js/deluge-all/OptionsManager.js index a1c4e65..529f7af 100644 --- a/deluge/ui/web/js/deluge-all/OptionsManager.js +++ b/deluge/ui/web/js/deluge-all/OptionsManager.js @@ -18,7 +18,7 @@ Ext.namespace('Deluge'); * @param {Object} config Configuration options */ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { - constructor: function(config) { + constructor: function (config) { config = config || {}; this.binds = {}; this.changed = {}; @@ -56,7 +56,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * Add a set of default options and values to the options manager * @param {Object} options The default options. */ - addOptions: function(options) { + addOptions: function (options) { this.options = Ext.applyIf(this.options, options); }, @@ -65,7 +65,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} option * @param {Ext.form.Field} field */ - bind: function(option, field) { + bind: function (option, field) { this.binds[option] = this.binds[option] || []; this.binds[option].push(field); field._doption = option; @@ -81,7 +81,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { /** * Changes all the changed values to be the default values */ - commit: function() { + commit: function () { this.options = Ext.apply(this.options, this.changed); this.reset(); }, @@ -91,7 +91,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {Mixed} oldValue The original value * @param {Mixed} value The new value to convert */ - convertValueType: function(oldValue, value) { + convertValueType: function (oldValue, value) { if (Ext.type(oldValue) != Ext.type(value)) { switch (Ext.type(oldValue)) { case 'string': @@ -121,7 +121,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} [option] A single option or an array of options to return. * @returns {Object} the options value. */ - get: function() { + get: function () { if (arguments.length == 1) { var option = arguments[0]; return this.isDirty(option) @@ -131,7 +131,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { var options = {}; Ext.each( arguments, - function(option) { + function (option) { if (!this.has(option)) return; options[option] = this.isDirty(option) ? this.changed[option] @@ -148,7 +148,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String|Array} [option] A single option or an array of options to return. * @returns {Object} the value of the option */ - getDefault: function(option) { + getDefault: function (option) { return this.options[option]; }, @@ -156,7 +156,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * Returns the dirty (changed) values. * @returns {Object} the changed options */ - getDirty: function() { + getDirty: function () { return this.changed; }, @@ -164,7 +164,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} [option] The option to check * @returns {Boolean} true if the option has been changed from the default. */ - isDirty: function(option) { + isDirty: function (option) { return !Ext.isEmpty(this.changed[option]); }, @@ -173,14 +173,14 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} option * @returns {Boolean} true if the option exists, else false. */ - has: function(option) { + has: function (option) { return this.options[option]; }, /** * Reset the options back to the default values. */ - reset: function() { + reset: function () { this.changed = {}; }, @@ -189,7 +189,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String} option * @param {Object} value The value for the option */ - set: function(option, value) { + set: function (option, value) { if (option === undefined) { return; } else if (typeof option == 'object') { @@ -209,7 +209,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {String/Object} option or options to update * @param {Object} [value]; */ - update: function(option, value) { + update: function (option, value) { if (option === undefined) { return; } else if (value === undefined) { @@ -238,7 +238,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * Lets the option manager know when a field is blurred so if a value * so value changing operations can continue on that field. */ - onFieldBlur: function(field, event) { + onFieldBlur: function (field, event) { if (this.focused == field) { this.focused = null; } @@ -249,7 +249,7 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * @param {Ext.form.Field} field * @private */ - onFieldChange: function(field, event) { + onFieldChange: function (field, event) { if (field.field) field = field.field; // fix for spinners this.update(field._doption, field.getValue()); }, @@ -258,16 +258,16 @@ Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { * Lets the option manager know when a field is focused so if a value changing * operation is performed it will not change the value of the field. */ - onFieldFocus: function(field, event) { + onFieldFocus: function (field, event) { this.focused = field; }, - onChange: function(option, newValue, oldValue) { + onChange: function (option, newValue, oldValue) { // If we don't have a bind there's nothing to do. if (Ext.isEmpty(this.binds[option])) return; Ext.each( this.binds[option], - function(bind) { + function (bind) { // The field is currently focused so we do not want to change it. if (bind == this.focused) return; // Set the form field to the new value. diff --git a/deluge/ui/web/js/deluge-all/OtherLimitWindow.js b/deluge/ui/web/js/deluge-all/OtherLimitWindow.js index 3e5880f..0d53d4a 100644 --- a/deluge/ui/web/js/deluge-all/OtherLimitWindow.js +++ b/deluge/ui/web/js/deluge-all/OtherLimitWindow.js @@ -20,7 +20,7 @@ Deluge.OtherLimitWindow = Ext.extend(Ext.Window, { constrainHeader: true, closeAction: 'hide', - initComponent: function() { + initComponent: function () { Deluge.OtherLimitWindow.superclass.initComponent.call(this); this.form = this.add({ xtype: 'form', @@ -53,30 +53,27 @@ Deluge.OtherLimitWindow = Ext.extend(Ext.Window, { this.afterMethod('show', this.doFocusField, this); }, - setValue: function(value) { + setValue: function (value) { this.form.getForm().setValues({ limit: value }); }, - onCancelClick: function() { + onCancelClick: function () { this.form.getForm().reset(); this.hide(); }, - onOkClick: function() { + onOkClick: function () { var config = {}; config[this.group] = this.form.getForm().getValues().limit; deluge.client.core.set_config(config, { - success: function() { + success: function () { deluge.ui.update(); }, }); this.hide(); }, - doFocusField: function() { - this.form - .getForm() - .findField('limit') - .focus(true, 10); + doFocusField: function () { + this.form.getForm().findField('limit').focus(true, 10); }, }); diff --git a/deluge/ui/web/js/deluge-all/Plugin.js b/deluge/ui/web/js/deluge-all/Plugin.js index af2cda4..26971f7 100644 --- a/deluge/ui/web/js/deluge-all/Plugin.js +++ b/deluge/ui/web/js/deluge-all/Plugin.js @@ -21,7 +21,7 @@ Deluge.Plugin = Ext.extend(Ext.util.Observable, { */ name: null, - constructor: function(config) { + constructor: function (config) { this.isDelugePlugin = true; this.addEvents({ /** @@ -43,7 +43,7 @@ Deluge.Plugin = Ext.extend(Ext.util.Observable, { * Disables the plugin, firing the "{@link #disabled}" event and * then executing the plugins clean up method onDisabled. */ - disable: function() { + disable: function () { this.fireEvent('disabled', this); if (this.onDisable) this.onDisable(); }, @@ -52,13 +52,13 @@ Deluge.Plugin = Ext.extend(Ext.util.Observable, { * Enables the plugin, firing the "{@link #enabled}" event and * then executes the plugins setup method, onEnabled. */ - enable: function() { + enable: function () { deluge.client.reloadMethods(); this.fireEvent('enable', this); if (this.onEnable) this.onEnable(); }, - registerTorrentStatus: function(key, header, options) { + registerTorrentStatus: function (key, header, options) { options = options || {}; var cc = options.colCfg || {}, sc = options.storeCfg || {}; @@ -79,23 +79,23 @@ Deluge.Plugin = Ext.extend(Ext.util.Observable, { deluge.torrents.getView().refresh(true); }, - deregisterTorrentStatus: function(key) { + deregisterTorrentStatus: function (key) { var fields = []; - Ext.each(deluge.torrents.meta.fields, function(field) { + Ext.each(deluge.torrents.meta.fields, function (field) { if (field.name != key) fields.push(field); }); deluge.torrents.meta.fields = fields; deluge.torrents.getStore().reader.onMetaChange(deluge.torrents.meta); var cols = []; - Ext.each(deluge.torrents.columns, function(col) { + Ext.each(deluge.torrents.columns, function (col) { if (col.dataIndex != key) cols.push(col); }); deluge.torrents.colModel.setConfig(cols); deluge.torrents.columns = cols; var keys = []; - Ext.each(Deluge.Keys.Grid, function(k) { + Ext.each(Deluge.Keys.Grid, function (k) { if (k == key) keys.push(k); }); Deluge.Keys.Grid = keys; diff --git a/deluge/ui/web/js/deluge-all/RemoveWindow.js b/deluge/ui/web/js/deluge-all/RemoveWindow.js index a629008..ccac2ef 100644 --- a/deluge/ui/web/js/deluge-all/RemoveWindow.js +++ b/deluge/ui/web/js/deluge-all/RemoveWindow.js @@ -27,16 +27,16 @@ Deluge.RemoveWindow = Ext.extend(Ext.Window, { bodyStyle: 'padding: 5px; padding-left: 10px;', html: 'Are you sure you wish to remove the torrent (s)?', - initComponent: function() { + initComponent: function () { Deluge.RemoveWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancel, this); this.addButton(_('Remove With Data'), this.onRemoveData, this); this.addButton(_('Remove Torrent'), this.onRemove, this); }, - remove: function(removeData) { + remove: function (removeData) { deluge.client.core.remove_torrents(this.torrentIds, removeData, { - success: function(result) { + success: function (result) { if (result == true) { console.log( 'Error(s) occured when trying to delete torrent(s).' @@ -49,25 +49,25 @@ Deluge.RemoveWindow = Ext.extend(Ext.Window, { }); }, - show: function(ids) { + show: function (ids) { Deluge.RemoveWindow.superclass.show.call(this); this.torrentIds = ids; }, - onCancel: function() { + onCancel: function () { this.hide(); this.torrentIds = null; }, - onRemove: function() { + onRemove: function () { this.remove(false); }, - onRemoveData: function() { + onRemoveData: function () { this.remove(true); }, - onRemoved: function(torrentIds) { + onRemoved: function (torrentIds) { deluge.events.fire('torrentsRemoved', torrentIds); this.hide(); deluge.ui.update(); diff --git a/deluge/ui/web/js/deluge-all/Sidebar.js b/deluge/ui/web/js/deluge-all/Sidebar.js index 74c3ecb..eb08a89 100644 --- a/deluge/ui/web/js/deluge-all/Sidebar.js +++ b/deluge/ui/web/js/deluge-all/Sidebar.js @@ -24,7 +24,7 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { // private selected: null, - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { id: 'sidebar', @@ -43,16 +43,16 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { }, // private - initComponent: function() { + initComponent: function () { Deluge.Sidebar.superclass.initComponent.call(this); deluge.events.on('disconnect', this.onDisconnect, this); }, - createFilter: function(filter, states) { + createFilter: function (filter, states) { var panel = new Deluge.FilterPanel({ filter: filter, }); - panel.on('selectionchange', function(view, nodes) { + panel.on('selectionchange', function (view, nodes) { deluge.ui.update(); }); this.add(panel); @@ -60,7 +60,7 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { this.doLayout(); this.panels[filter] = panel; - panel.header.on('click', function(header) { + panel.header.on('click', function (header) { if (!deluge.config.sidebar_multiple_filters) { deluge.ui.update(); } @@ -74,16 +74,16 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { this.fireEvent('afterfiltercreate', this, panel); }, - getFilter: function(filter) { + getFilter: function (filter) { return this.panels[filter]; }, - getFilterStates: function() { + getFilterStates: function () { var states = {}; if (deluge.config.sidebar_multiple_filters) { // Grab the filters from each of the filter panels - this.items.each(function(panel) { + this.items.each(function (panel) { var state = panel.getState(); if (state == null) return; states[panel.filterType] = state; @@ -100,12 +100,12 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { return states; }, - hasFilter: function(filter) { + hasFilter: function (filter) { return this.panels[filter] ? true : false; }, // private - onDisconnect: function() { + onDisconnect: function () { for (var filter in this.panels) { this.remove(this.panels[filter]); } @@ -113,11 +113,11 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { this.selected = null; }, - onFilterSelect: function(selModel, rowIndex, record) { + onFilterSelect: function (selModel, rowIndex, record) { deluge.ui.update(); }, - update: function(filters) { + update: function (filters) { for (var filter in filters) { var states = filters[filter]; if (Ext.getKeys(this.panels).indexOf(filter) > -1) { @@ -130,7 +130,7 @@ Deluge.Sidebar = Ext.extend(Ext.Panel, { // Perform a cleanup of fitlers that are not enabled any more. Ext.each( Ext.keys(this.panels), - function(filter) { + function (filter) { if (Ext.keys(filters).indexOf(filter) == -1) { // We need to remove the panel this.remove(this.panels[filter]); diff --git a/deluge/ui/web/js/deluge-all/Statusbar.js b/deluge/ui/web/js/deluge-all/Statusbar.js index c2327be..00ad6b2 100644 --- a/deluge/ui/web/js/deluge-all/Statusbar.js +++ b/deluge/ui/web/js/deluge-all/Statusbar.js @@ -10,7 +10,7 @@ Ext.namespace('Deluge'); Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { id: 'deluge-statusbar', @@ -22,14 +22,14 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { Deluge.Statusbar.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.Statusbar.superclass.initComponent.call(this); deluge.events.on('connect', this.onConnect, this); deluge.events.on('disconnect', this.onDisconnect, this); }, - createButtons: function() { + createButtons: function () { this.buttons = this.add( { id: 'statusbar-connections', @@ -213,7 +213,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { cls: 'x-btn-text-icon', iconCls: 'x-deluge-traffic', tooltip: _('Protocol Traffic Download/Upload'), - handler: function() { + handler: function () { deluge.preferences.show(); deluge.preferences.selectPage('Network'); }, @@ -240,7 +240,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { cls: 'x-btn-text-icon', iconCls: 'x-deluge-freespace', tooltip: _('Freespace in download folder'), - handler: function() { + handler: function () { deluge.preferences.show(); deluge.preferences.selectPage('Downloads'); }, @@ -249,7 +249,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { this.created = true; }, - onConnect: function() { + onConnect: function () { this.setStatus({ iconCls: 'x-connected', text: '', @@ -257,7 +257,7 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { if (!this.created) { this.createButtons(); } else { - Ext.each(this.buttons, function(item) { + Ext.each(this.buttons, function (item) { item.show(); item.enable(); }); @@ -265,23 +265,23 @@ Deluge.Statusbar = Ext.extend(Ext.ux.StatusBar, { this.doLayout(); }, - onDisconnect: function() { + onDisconnect: function () { this.clearStatus({ useDefaults: true }); - Ext.each(this.buttons, function(item) { + Ext.each(this.buttons, function (item) { item.hide(); item.disable(); }); this.doLayout(); }, - update: function(stats) { + update: function (stats) { if (!stats) return; function addSpeed(val) { return val + ' KiB/s'; } - var updateStat = function(name, config) { + var updateStat = function (name, config) { var item = this.items.get('statusbar-' + name); if (config.limit.value > 0) { var value = config.value.formatter diff --git a/deluge/ui/web/js/deluge-all/StatusbarMenu.js b/deluge/ui/web/js/deluge-all/StatusbarMenu.js index b988253..1365c9c 100644 --- a/deluge/ui/web/js/deluge-all/StatusbarMenu.js +++ b/deluge/ui/web/js/deluge-all/StatusbarMenu.js @@ -15,13 +15,13 @@ Ext.ns('Deluge'); * @extends Ext.menu.Menu */ Deluge.StatusbarMenu = Ext.extend(Ext.menu.Menu, { - initComponent: function() { + initComponent: function () { Deluge.StatusbarMenu.superclass.initComponent.call(this); this.otherWin = new Deluge.OtherLimitWindow( this.initialConfig.otherWin || {} ); - this.items.each(function(item) { + this.items.each(function (item) { if (item.getXType() != 'menucheckitem') return; if (item.value == 'other') { item.on('click', this.onOtherClicked, this); @@ -31,14 +31,14 @@ Deluge.StatusbarMenu = Ext.extend(Ext.menu.Menu, { }, this); }, - setValue: function(value) { + setValue: function (value) { var beenSet = false; // set the new value this.value = value = value == 0 ? -1 : value; var other = null; // uncheck all items - this.items.each(function(item) { + this.items.each(function (item) { if (item.setChecked) { item.suspendEvents(); if (item.value == value) { @@ -60,18 +60,18 @@ Deluge.StatusbarMenu = Ext.extend(Ext.menu.Menu, { other.resumeEvents(); }, - onLimitChanged: function(item, checked) { + onLimitChanged: function (item, checked) { if (!checked || item.value == 'other') return; // We do not care about unchecked or other. var config = {}; config[item.group] = item.value; deluge.client.core.set_config(config, { - success: function() { + success: function () { deluge.ui.update(); }, }); }, - onOtherClicked: function(item, e) { + onOtherClicked: function (item, e) { this.otherWin.group = item.group; this.otherWin.setValue(this.value); this.otherWin.show(); diff --git a/deluge/ui/web/js/deluge-all/Toolbar.js b/deluge/ui/web/js/deluge-all/Toolbar.js index d51818b..1ecdd75 100644 --- a/deluge/ui/web/js/deluge-all/Toolbar.js +++ b/deluge/ui/web/js/deluge-all/Toolbar.js @@ -14,7 +14,7 @@ * @extends Ext.Toolbar */ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { items: [ @@ -118,62 +118,62 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { connectedButtons: ['add', 'remove', 'pause', 'resume', 'up', 'down'], - initComponent: function() { + initComponent: function () { Deluge.Toolbar.superclass.initComponent.call(this); deluge.events.on('connect', this.onConnect, this); deluge.events.on('login', this.onLogin, this); }, - onConnect: function() { + onConnect: function () { Ext.each( this.connectedButtons, - function(buttonId) { + function (buttonId) { this.items.get(buttonId).enable(); }, this ); }, - onDisconnect: function() { + onDisconnect: function () { Ext.each( this.connectedButtons, - function(buttonId) { + function (buttonId) { this.items.get(buttonId).disable(); }, this ); }, - onLogin: function() { + onLogin: function () { this.items.get('logout').enable(); }, - onLogout: function() { + onLogout: function () { this.items.get('logout').disable(); deluge.login.logout(); }, - onConnectionManagerClick: function() { + onConnectionManagerClick: function () { deluge.connectionManager.show(); }, - onHelpClick: function() { + onHelpClick: function () { window.open('http://dev.deluge-torrent.org/wiki/UserGuide'); }, - onAboutClick: function() { + onAboutClick: function () { var about = new Deluge.about.AboutWindow(); about.show(); }, - onPreferencesClick: function() { + onPreferencesClick: function () { deluge.preferences.show(); }, - onTorrentAction: function(item) { + onTorrentAction: function (item) { var selection = deluge.torrents.getSelections(); var ids = []; - Ext.each(selection, function(record) { + Ext.each(selection, function (record) { ids.push(record.id); }); @@ -184,7 +184,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { case 'pause': case 'resume': deluge.client.core[item.id + '_torrent'](ids, { - success: function() { + success: function () { deluge.ui.update(); }, }); @@ -192,7 +192,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { case 'up': case 'down': deluge.client.core['queue_' + item.id](ids, { - success: function() { + success: function () { deluge.ui.update(); }, }); @@ -200,7 +200,7 @@ Deluge.Toolbar = Ext.extend(Ext.Toolbar, { } }, - onTorrentAdd: function() { + onTorrentAdd: function () { deluge.add.show(); }, }); diff --git a/deluge/ui/web/js/deluge-all/TorrentGrid.js b/deluge/ui/web/js/deluge-all/TorrentGrid.js index b0e0c5e..333d133 100644 --- a/deluge/ui/web/js/deluge-all/TorrentGrid.js +++ b/deluge/ui/web/js/deluge-all/TorrentGrid.js @@ -8,7 +8,7 @@ * See LICENSE for more details. */ -(function() { +(function () { /* Renderers for the Torrent Grid */ function queueRenderer(value) { return value == -1 ? '' : value + 1; @@ -17,7 +17,7 @@ return String.format( '<div class="torrent-name x-deluge-{0}">{1}</div>', r.data['state'].toLowerCase(), - value + Ext.util.Format.htmlEncode(value) ); } function torrentSpeedRenderer(value) { @@ -62,12 +62,14 @@ '<div style="background: url(' + deluge.config.base + 'tracker/{0}) no-repeat; padding-left: 20px;">{0}</div>', - value + Ext.util.Format.htmlEncode(value) ); } function etaSorter(eta) { - return eta * -1; + if (eta === 0) return Number.MAX_VALUE; + if (eta <= -1) return Number.MAX_SAFE_INTEGER; + return eta; } function dateOrNever(date) { @@ -75,7 +77,9 @@ } function timeOrInf(time) { - return time < 0 ? '∞' : ftime(time); + if (time === 0) return ''; + if (time <= -1) return '∞'; + return ftime(time); } /** @@ -320,6 +324,8 @@ { name: 'ratio', type: 'float' }, { name: 'distributed_copies', type: 'float' }, { name: 'time_added', type: 'int' }, + { name: 'last_seen_complete', type: 'int' }, + { name: 'completed_time', type: 'int' }, { name: 'tracker_host' }, { name: 'download_location' }, { name: 'total_done', type: 'int' }, @@ -337,21 +343,21 @@ key: 'a', ctrl: true, stopEvent: true, - handler: function() { + handler: function () { deluge.torrents.getSelectionModel().selectAll(); }, }, { key: [46], stopEvent: true, - handler: function() { + handler: function () { ids = deluge.torrents.getSelectedIds(); deluge.removeWindow.show(ids); }, }, ], - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { id: 'torrentGrid', @@ -376,12 +382,12 @@ Deluge.TorrentGrid.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.TorrentGrid.superclass.initComponent.call(this); deluge.events.on('torrentsRemoved', this.onTorrentsRemoved, this); deluge.events.on('disconnect', this.onDisconnect, this); - this.on('rowcontextmenu', function(grid, rowIndex, e) { + this.on('rowcontextmenu', function (grid, rowIndex, e) { e.stopEvent(); var selection = grid.getSelectionModel(); if (!selection.isSelected(rowIndex)) { @@ -397,7 +403,7 @@ * @param index {int} The row index of the torrent you wish to retrieve. * @return {Ext.data.Record} The record representing the torrent. */ - getTorrent: function(index) { + getTorrent: function (index) { return this.getStore().getAt(index); }, @@ -405,14 +411,14 @@ * Returns the currently selected record. * @ return {Array/Ext.data.Record} The record(s) representing the rows */ - getSelected: function() { + getSelected: function () { return this.getSelectionModel().getSelected(); }, /** * Returns the currently selected records. */ - getSelections: function() { + getSelections: function () { return this.getSelectionModel().getSelections(); }, @@ -420,7 +426,7 @@ * Return the currently selected torrent id. * @return {String} The currently selected id. */ - getSelectedId: function() { + getSelectedId: function () { return this.getSelectionModel().getSelected().id; }, @@ -428,15 +434,15 @@ * Return the currently selected torrent ids. * @return {Array} The currently selected ids. */ - getSelectedIds: function() { + getSelectedIds: function () { var ids = []; - Ext.each(this.getSelectionModel().getSelections(), function(r) { + Ext.each(this.getSelectionModel().getSelections(), function (r) { ids.push(r.id); }); return ids; }, - update: function(torrents, wipe) { + update: function (torrents, wipe) { var store = this.getStore(); // Need to perform a complete reload of the torrent grid. @@ -470,7 +476,7 @@ store.add(newTorrents); // Remove any torrents that should not be in the store. - store.each(function(record) { + store.each(function (record) { if (!torrents[record.id]) { store.remove(record); delete this.torrents[record.id]; @@ -484,17 +490,17 @@ }, // private - onDisconnect: function() { + onDisconnect: function () { this.getStore().removeAll(); this.torrents = {}; }, // private - onTorrentsRemoved: function(torrentIds) { + onTorrentsRemoved: function (torrentIds) { var selModel = this.getSelectionModel(); Ext.each( torrentIds, - function(torrentId) { + function (torrentId) { var record = this.getStore().getById(torrentId); if (selModel.isSelected(record)) { selModel.deselectRow(this.getStore().indexOf(record)); diff --git a/deluge/ui/web/js/deluge-all/UI.js b/deluge/ui/web/js/deluge-all/UI.js index dec4850..cc877d5 100644 --- a/deluge/ui/web/js/deluge-all/UI.js +++ b/deluge/ui/web/js/deluge-all/UI.js @@ -42,7 +42,7 @@ deluge.ui = { * @description Create all the interface components, the json-rpc client * and set up various events that the UI will utilise. */ - initialize: function() { + initialize: function () { deluge.add = new Deluge.add.AddWindow(); deluge.details = new Deluge.details.DetailsPanel(); deluge.connectionManager = new Deluge.ConnectionManager(); @@ -100,7 +100,7 @@ deluge.ui = { deluge.client.on( 'connected', - function(e) { + function (e) { deluge.login.show(); }, this, @@ -113,7 +113,7 @@ deluge.ui = { this.originalTitle = document.title; }, - checkConnection: function() { + checkConnection: function () { deluge.client.web.connected({ success: this.onConnectionSuccess, failure: this.onConnectionError, @@ -121,7 +121,7 @@ deluge.ui = { }); }, - update: function() { + update: function () { var filters = deluge.sidebar.getFilterStates(); this.oldFilters = this.filters; this.filters = filters; @@ -134,9 +134,9 @@ deluge.ui = { deluge.details.update(); }, - onConnectionError: function(error) {}, + onConnectionError: function (error) {}, - onConnectionSuccess: function(result) { + onConnectionSuccess: function (result) { deluge.statusbar.setStatus({ iconCls: 'x-deluge-statusbar icon-ok', text: _('Connection restored'), @@ -147,7 +147,7 @@ deluge.ui = { } }, - onUpdateError: function(error) { + onUpdateError: function (error) { if (this.errorCount == 2) { Ext.MessageBox.show({ title: _('Lost Connection'), @@ -169,7 +169,7 @@ deluge.ui = { * @private * Updates the various components in the interface. */ - onUpdate: function(data) { + onUpdate: function (data) { if (!data['connected']) { deluge.connectionManager.disconnect(true); return; @@ -199,7 +199,7 @@ deluge.ui = { * @private * Start the Deluge UI polling the server and update the interface. */ - onConnect: function() { + onConnect: function () { if (!this.running) { this.running = setInterval(this.update, 2000); this.update(); @@ -214,14 +214,14 @@ deluge.ui = { * @static * @private */ - onDisconnect: function() { + onDisconnect: function () { this.stop(); }, - onGotPlugins: function(plugins) { + onGotPlugins: function (plugins) { Ext.each( plugins.enabled_plugins, - function(plugin) { + function (plugin) { if (deluge.plugins[plugin]) return; deluge.client.web.get_plugin_resources(plugin, { success: this.onGotPluginResources, @@ -232,7 +232,7 @@ deluge.ui = { ); }, - onPluginEnabled: function(pluginName) { + onPluginEnabled: function (pluginName) { if (deluge.plugins[pluginName]) { deluge.plugins[pluginName].enable(); } else { @@ -243,13 +243,13 @@ deluge.ui = { } }, - onGotPluginResources: function(resources) { + onGotPluginResources: function (resources) { var scripts = Deluge.debug ? resources.debug_scripts : resources.scripts; Ext.each( scripts, - function(script) { + function (script) { Ext.ux.JSLoader({ url: deluge.config.base + script, onLoad: this.onPluginLoaded, @@ -260,11 +260,11 @@ deluge.ui = { ); }, - onPluginDisabled: function(pluginName) { + onPluginDisabled: function (pluginName) { if (deluge.plugins[pluginName]) deluge.plugins[pluginName].disable(); }, - onPluginLoaded: function(options) { + onPluginLoaded: function (options) { // This could happen if the plugin has multiple scripts if (!Deluge.hasPlugin(options.pluginName)) return; @@ -278,7 +278,7 @@ deluge.ui = { * @static * Stop the Deluge UI polling the server and clear the interface. */ - stop: function() { + stop: function () { if (this.running) { clearInterval(this.running); this.running = false; @@ -287,6 +287,6 @@ deluge.ui = { }, }; -Ext.onReady(function(e) { +Ext.onReady(function (e) { deluge.ui.initialize(); }); diff --git a/deluge/ui/web/js/deluge-all/add/AddWindow.js b/deluge/ui/web/js/deluge-all/add/AddWindow.js index 89803f3..f5f2fdf 100644 --- a/deluge/ui/web/js/deluge-all/add/AddWindow.js +++ b/deluge/ui/web/js/deluge-all/add/AddWindow.js @@ -12,7 +12,7 @@ Ext.namespace('Deluge.add'); // This override allows file upload buttons to contain icons Ext.override(Ext.ux.form.FileUploadField, { - onRender: function(ct, position) { + onRender: function (ct, position) { Ext.ux.form.FileUploadField.superclass.onRender.call( this, ct, @@ -58,26 +58,12 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { plain: true, iconCls: 'x-deluge-add-window-icon', - initComponent: function() { + initComponent: function () { Deluge.add.AddWindow.superclass.initComponent.call(this); this.addButton(_('Cancel'), this.onCancelClick, this); this.addButton(_('Add'), this.onAddClick, this); - function torrentRenderer(value, p, r) { - if (r.data['info_hash']) { - return String.format( - '<div class="x-deluge-add-torrent-name">{0}</div>', - value - ); - } else { - return String.format( - '<div class="x-deluge-add-torrent-name-loading">{0}</div>', - value - ); - } - } - this.list = new Ext.list.ListView({ store: new Ext.data.SimpleStore({ fields: [ @@ -91,8 +77,10 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { id: 'torrent', width: 150, sortable: true, - renderer: torrentRenderer, dataIndex: 'text', + tpl: new Ext.XTemplate( + '<div class="x-deluge-add-torrent-name">{text:htmlEncode}</div>' + ), }, ], stripeRows: true, @@ -147,7 +135,7 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { }, { text: _('Infohash'), - iconCls: 'icon-add-magnet', + iconCls: 'icon-magnet-add', hidden: true, disabled: true, }, @@ -168,17 +156,17 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { this.on('show', this.onShow, this); }, - clear: function() { + clear: function () { this.list.getStore().removeAll(); this.optionsPanel.clear(); // Reset upload form so handler fires when a canceled file is reselected this.fileUploadForm.reset(); }, - onAddClick: function() { + onAddClick: function () { var torrents = []; if (!this.list) return; - this.list.getStore().each(function(r) { + this.list.getStore().each(function (r) { var id = r.get('info_hash'); torrents.push({ path: this.optionsPanel.getFilename(id), @@ -187,29 +175,29 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { }, this); deluge.client.web.add_torrents(torrents, { - success: function(result) {}, + success: function (result) {}, }); this.clear(); this.hide(); }, - onCancelClick: function() { + onCancelClick: function () { this.clear(); this.hide(); }, - onFile: function() { + onFile: function () { if (!this.file) this.file = new Deluge.add.FileWindow(); this.file.show(); }, - onHide: function() { + onHide: function () { this.optionsPanel.setActiveTab(0); this.optionsPanel.files.setDisabled(true); this.optionsPanel.form.setDisabled(true); }, - onRemove: function() { + onRemove: function () { if (!this.list.getSelectionCount()) return; var torrent = this.list.getSelectedRecords()[0]; if (!torrent) return; @@ -220,7 +208,7 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { delete this.torrents[torrent.id]; }, - onSelect: function(list, selections) { + onSelect: function (list, selections) { if (selections.length) { var record = this.list.getRecord(selections[0]); this.optionsPanel.setTorrent(record.get('info_hash')); @@ -230,24 +218,25 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { } }, - onShow: function() { + onShow: function () { if (!this.url) { this.url = new Deluge.add.UrlWindow(); this.url.on('beforeadd', this.onTorrentBeforeAdd, this); this.url.on('add', this.onTorrentAdd, this); + this.url.on('addfailed', this.onTorrentAddFailed, this); } this.optionsPanel.form.getDefaults(); }, - onFileSelected: function() { + onFileSelected: function () { if (this.fileUploadForm.isValid()) { var torrentIds = []; var files = this.fileUploadForm.findField('torrentFile').value; var randomId = this.createTorrentId(); Array.prototype.forEach.call( files, - function(file, i) { + function (file, i) { // Append index for batch of unique torrentIds. var torrentId = randomId + i.toString(); torrentIds.push(torrentId); @@ -258,20 +247,21 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { url: deluge.config.base + 'upload', waitMsg: _('Uploading your torrent...'), success: this.onUploadSuccess, + failure: this.onUploadFailure, scope: this, torrentIds: torrentIds, }); } }, - onUploadSuccess: function(fp, upload) { + onUploadSuccess: function (fp, upload) { if (!upload.result.success) { this.clear(); return; } upload.result.files.forEach( - function(filename, i) { + function (filename, i) { deluge.client.web.get_torrent_info(filename, { success: this.onGotInfo, scope: this, @@ -283,18 +273,31 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { this.fileUploadForm.reset(); }, - onGotInfo: function(info, obj, response, request) { + onUploadFailure: function (form, action) { + this.hide(); + Ext.MessageBox.show({ + title: _('Error'), + msg: _('Failed to upload torrent'), + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error', + }); + this.fireEvent('addfailed', this.torrentId); + }, + + onGotInfo: function (info, obj, response, request) { info.filename = request.options.filename; torrentId = request.options.torrentId; this.onTorrentAdd(torrentId, info); }, - onTorrentBeforeAdd: function(torrentId, text) { + onTorrentBeforeAdd: function (torrentId, text) { var store = this.list.getStore(); store.loadData([[torrentId, null, text]], true); }, - onTorrentAdd: function(torrentId, info) { + onTorrentAdd: function (torrentId, info) { var r = this.list.getStore().getById(torrentId); if (!info) { Ext.MessageBox.show({ @@ -315,7 +318,15 @@ Deluge.add.AddWindow = Ext.extend(Deluge.add.Window, { } }, - onUrl: function(button, event) { + onTorrentAddFailed: function (torrentId) { + var store = this.list.getStore(); + var torrentRecord = store.getById(torrentId); + if (torrentRecord) { + store.remove(torrentRecord); + } + }, + + onUrl: function (button, event) { this.url.show(); }, }); diff --git a/deluge/ui/web/js/deluge-all/add/FilesTab.js b/deluge/ui/web/js/deluge-all/add/FilesTab.js index a433ad6..d712c02 100644 --- a/deluge/ui/web/js/deluge-all/add/FilesTab.js +++ b/deluge/ui/web/js/deluge-all/add/FilesTab.js @@ -28,13 +28,14 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { header: _('Filename'), width: 295, dataIndex: 'filename', + tpl: new Ext.XTemplate('{filename:htmlEncode}'), }, { header: _('Size'), width: 60, dataIndex: 'size', tpl: new Ext.XTemplate('{size:this.fsize}', { - fsize: function(v) { + fsize: function (v) { return fsize(v); }, }), @@ -44,7 +45,7 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { width: 65, dataIndex: 'download', tpl: new Ext.XTemplate('{download:this.format}', { - format: function(v) { + format: function (v) { return ( '<div rel="chkbox" class="x-grid3-check-col' + (v ? '-on' : '') + @@ -55,21 +56,21 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { }, ], - initComponent: function() { + initComponent: function () { Deluge.add.FilesTab.superclass.initComponent.call(this); this.on('click', this.onNodeClick, this); }, - clearFiles: function() { + clearFiles: function () { var root = this.getRootNode(); if (!root.hasChildNodes()) return; - root.cascade(function(node) { + root.cascade(function (node) { if (!node.parentNode || !node.getOwnerTree()) return; node.remove(); }); }, - setDownload: function(node, value, suppress) { + setDownload: function (node, value, suppress) { node.attributes.download = value; node.ui.updateColumns(); @@ -79,7 +80,7 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { } } else { var nodes = [node]; - node.cascade(function(n) { + node.cascade(function (n) { n.attributes.download = value; n.ui.updateColumns(); nodes.push(n); @@ -90,7 +91,7 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { } }, - onNodeClick: function(node, e) { + onNodeClick: function (node, e) { var el = new Ext.Element(e.target); if (el.getAttribute('rel') == 'chkbox') { this.setDownload(node, !node.attributes.download); diff --git a/deluge/ui/web/js/deluge-all/add/OptionsPanel.js b/deluge/ui/web/js/deluge-all/add/OptionsPanel.js index 3dfb6f8..365b001 100644 --- a/deluge/ui/web/js/deluge-all/add/OptionsPanel.js +++ b/deluge/ui/web/js/deluge-all/add/OptionsPanel.js @@ -18,7 +18,7 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { activeTab: 0, height: 265, - initComponent: function() { + initComponent: function () { Deluge.add.OptionsPanel.superclass.initComponent.call(this); this.files = this.add(new Deluge.add.FilesTab()); this.form = this.add(new Deluge.add.OptionsTab()); @@ -26,12 +26,12 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { this.files.on('fileschecked', this.onFilesChecked, this); }, - addTorrent: function(torrent) { + addTorrent: function (torrent) { this.torrents[torrent['info_hash']] = torrent; var fileIndexes = {}; this.walkFileTree( torrent['files_tree'], - function(filename, type, entry, parent) { + function (filename, type, entry, parent) { if (type != 'file') return; fileIndexes[entry.index] = entry.download; }, @@ -39,7 +39,7 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { ); var priorities = []; - Ext.each(Ext.keys(fileIndexes), function(index) { + Ext.each(Ext.keys(fileIndexes), function (index) { priorities[index] = fileIndexes[index]; }); @@ -51,26 +51,26 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { this.form.optionsManager.changeId(oldId, true); }, - clear: function() { + clear: function () { this.files.clearFiles(); this.form.optionsManager.resetAll(); }, - getFilename: function(torrentId) { + getFilename: function (torrentId) { return this.torrents[torrentId]['filename']; }, - getOptions: function(torrentId) { + getOptions: function (torrentId) { var oldId = this.form.optionsManager.changeId(torrentId, true); var options = this.form.optionsManager.get(); this.form.optionsManager.changeId(oldId, true); - Ext.each(options['file_priorities'], function(priority, index) { + Ext.each(options['file_priorities'], function (priority, index) { options['file_priorities'][index] = priority ? 1 : 0; }); return options; }, - setTorrent: function(torrentId) { + setTorrent: function (torrentId) { if (!torrentId) return; this.torrentId = torrentId; @@ -85,7 +85,7 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { if (this.torrents[torrentId]['files_tree']) { this.walkFileTree( this.torrents[torrentId]['files_tree'], - function(filename, type, entry, parentNode) { + function (filename, type, entry, parentNode) { var node = new Ext.tree.TreeNode({ download: entry.index ? priorities[entry.index] : true, filename: filename, @@ -109,7 +109,7 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { } }, - walkFileTree: function(files, callback, scope, parentNode) { + walkFileTree: function (files, callback, scope, parentNode) { for (var filename in files.contents) { var entry = files.contents[filename]; var type = entry.type; @@ -129,14 +129,13 @@ Deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { } }, - onFilesChecked: function(nodes, newValue, oldValue) { + onFilesChecked: function (nodes, newValue, oldValue) { Ext.each( nodes, - function(node) { + function (node) { if (node.attributes.fileindex < 0) return; - var priorities = this.form.optionsManager.get( - 'file_priorities' - ); + var priorities = + this.form.optionsManager.get('file_priorities'); priorities[node.attributes.fileindex] = newValue; this.form.optionsManager.update('file_priorities', priorities); }, diff --git a/deluge/ui/web/js/deluge-all/add/OptionsTab.js b/deluge/ui/web/js/deluge-all/add/OptionsTab.js index e897b17..73a8a5c 100644 --- a/deluge/ui/web/js/deluge-all/add/OptionsTab.js +++ b/deluge/ui/web/js/deluge-all/add/OptionsTab.js @@ -21,7 +21,7 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { disabled: true, labelWidth: 1, - initComponent: function() { + initComponent: function () { Deluge.add.OptionsTab.superclass.initComponent.call(this); this.optionsManager = new Deluge.MultiOptionsManager(); @@ -174,7 +174,7 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { ); }, - getDefaults: function() { + getDefaults: function () { var keys = [ 'add_paused', 'pre_allocate_storage', @@ -190,7 +190,7 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { ]; deluge.client.core.get_config_values(keys, { - success: function(config) { + success: function (config) { var options = { file_priorities: [], add_paused: config.add_paused, diff --git a/deluge/ui/web/js/deluge-all/add/UrlWindow.js b/deluge/ui/web/js/deluge-all/add/UrlWindow.js index d3a9a69..caf2250 100644 --- a/deluge/ui/web/js/deluge-all/add/UrlWindow.js +++ b/deluge/ui/web/js/deluge-all/add/UrlWindow.js @@ -22,7 +22,7 @@ Deluge.add.UrlWindow = Ext.extend(Deluge.add.Window, { bodyStyle: 'padding: 10px 5px;', iconCls: 'x-deluge-add-url-window-icon', - initComponent: function() { + initComponent: function () { Deluge.add.UrlWindow.superclass.initComponent.call(this); this.addButton(_('Add'), this.onAddClick, this); @@ -50,7 +50,7 @@ Deluge.add.UrlWindow = Ext.extend(Deluge.add.Window, { this.cookieField.on('specialkey', this.onAdd, this); }, - onAddClick: function(field, e) { + onAddClick: function (field, e) { if ( (field.id == 'url' || field.id == 'cookies') && e.getKey() != e.ENTER @@ -72,6 +72,7 @@ Deluge.add.UrlWindow = Ext.extend(Deluge.add.Window, { } else { deluge.client.web.download_torrent_from_url(url, cookies, { success: this.onDownload, + failure: this.onDownloadFailed, scope: this, torrentId: torrentId, }); @@ -82,16 +83,29 @@ Deluge.add.UrlWindow = Ext.extend(Deluge.add.Window, { this.fireEvent('beforeadd', torrentId, url); }, - onDownload: function(filename, obj, resp, req) { + onDownload: function (filename, obj, resp, req) { deluge.client.web.get_torrent_info(filename, { success: this.onGotInfo, + failure: this.onDownloadFailed, scope: this, filename: filename, torrentId: req.options.torrentId, }); }, - onGotInfo: function(info, obj, response, request) { + onDownloadFailed: function (obj, resp, req) { + Ext.MessageBox.show({ + title: _('Error'), + msg: _('Failed to download torrent'), + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error', + }); + this.fireEvent('addfailed', req.options.torrentId); + }, + + onGotInfo: function (info, obj, response, request) { info['filename'] = request.options.filename; this.fireEvent('add', request.options.torrentId, info); }, diff --git a/deluge/ui/web/js/deluge-all/add/Window.js b/deluge/ui/web/js/deluge-all/add/Window.js index 206b3ee..20851e7 100644 --- a/deluge/ui/web/js/deluge-all/add/Window.js +++ b/deluge/ui/web/js/deluge-all/add/Window.js @@ -15,15 +15,15 @@ Ext.ns('Deluge.add'); * Base class for an add Window */ Deluge.add.Window = Ext.extend(Ext.Window, { - initComponent: function() { + initComponent: function () { Deluge.add.Window.superclass.initComponent.call(this); - this.addEvents('beforeadd', 'add'); + this.addEvents('beforeadd', 'add', 'addfailed'); }, /** * Create an id for the torrent before we have any info about it. */ - createTorrentId: function() { + createTorrentId: function () { return new Date().getTime().toString(); }, }); diff --git a/deluge/ui/web/js/deluge-all/data/SortTypes.js b/deluge/ui/web/js/deluge-all/data/SortTypes.js index 199f895..ac915d1 100644 --- a/deluge/ui/web/js/deluge-all/data/SortTypes.js +++ b/deluge/ui/web/js/deluge-all/data/SortTypes.js @@ -27,11 +27,11 @@ Deluge.data.SortTypes = { return ((+d[1] * 256 + (+d[2])) * 256 + (+d[3])) * 256 + (+d[4]); }, - asQueuePosition: function(value) { + asQueuePosition: function (value) { return value > -1 ? value : Number.MAX_VALUE; }, - asName: function(value) { + asName: function (value) { return String(value).toLowerCase(); }, }; diff --git a/deluge/ui/web/js/deluge-all/details/DetailsPanel.js b/deluge/ui/web/js/deluge-all/details/DetailsPanel.js index 1c51de4..3f28b25 100644 --- a/deluge/ui/web/js/deluge-all/details/DetailsPanel.js +++ b/deluge/ui/web/js/deluge-all/details/DetailsPanel.js @@ -16,7 +16,7 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { id: 'torrentDetails', activeTab: 0, - initComponent: function() { + initComponent: function () { Deluge.details.DetailsPanel.superclass.initComponent.call(this); this.add(new Deluge.details.StatusTab()); this.add(new Deluge.details.DetailsTab()); @@ -25,8 +25,8 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { this.add(new Deluge.details.OptionsTab()); }, - clear: function() { - this.items.each(function(panel) { + clear: function () { + this.items.each(function (panel) { if (panel.clear) { panel.clear.defer(100, panel); panel.disable(); @@ -34,14 +34,14 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { }); }, - update: function(tab) { + update: function (tab) { var torrent = deluge.torrents.getSelected(); if (!torrent) { this.clear(); return; } - this.items.each(function(tab) { + this.items.each(function (tab) { if (tab.disabled) tab.enable(); }); @@ -52,7 +52,7 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { /* Event Handlers */ // We need to add the events in onRender since Deluge.Torrents has not been created yet. - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.DetailsPanel.superclass.onRender.call( this, ct, @@ -64,18 +64,18 @@ Deluge.details.DetailsPanel = Ext.extend(Ext.TabPanel, { deluge.torrents.getSelectionModel().on( 'selectionchange', - function(selModel) { + function (selModel) { if (!selModel.hasSelection()) this.clear(); }, this ); }, - onTabChange: function(panel, tab) { + onTabChange: function (panel, tab) { this.update(tab); }, - onTorrentsClick: function(grid, rowIndex, e) { + onTorrentsClick: function (grid, rowIndex, e) { this.update(); }, }); diff --git a/deluge/ui/web/js/deluge-all/details/DetailsTab.js b/deluge/ui/web/js/deluge-all/details/DetailsTab.js index 84929ae..f1da178 100644 --- a/deluge/ui/web/js/deluge-all/details/DetailsTab.js +++ b/deluge/ui/web/js/deluge-all/details/DetailsTab.js @@ -18,7 +18,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { oldData: {}, - initComponent: function() { + initComponent: function () { Deluge.details.DetailsTab.superclass.initComponent.call(this); this.addItem('torrent_name', _('Name:')); this.addItem('hash', _('Hash:')); @@ -31,7 +31,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { this.addItem('creator', _('Created By:')); }, - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.DetailsTab.superclass.onRender.call(this, ct, position); this.body.setStyle('padding', '10px'); this.dl = Ext.DomHelper.append(this.body, { tag: 'dl' }, true); @@ -41,7 +41,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { } }, - addItem: function(id, label) { + addItem: function (id, label) { if (!this.rendered) { this.queuedItems[id] = label; } else { @@ -50,7 +50,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { }, // private - doAddItem: function(id, label) { + doAddItem: function (id, label) { Ext.DomHelper.append(this.dl, { tag: 'dt', cls: id, html: label }); this.fields[id] = Ext.DomHelper.append( this.dl, @@ -59,7 +59,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { ); }, - clear: function() { + clear: function () { if (!this.fields) return; for (var k in this.fields) { this.fields[k].dom.innerHTML = ''; @@ -67,7 +67,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { this.oldData = {}; }, - update: function(torrentId) { + update: function (torrentId) { deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Details, { success: this.onRequestComplete, scope: this, @@ -75,7 +75,7 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { }); }, - onRequestComplete: function(torrent, request, response, options) { + onRequestComplete: function (torrent, request, response, options) { var data = { torrent_name: torrent.name, hash: options.options.torrentId, @@ -91,7 +91,9 @@ Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { for (var field in this.fields) { if (!Ext.isDefined(data[field])) continue; // This is a field we are not responsible for. if (data[field] == this.oldData[field]) continue; - this.fields[field].dom.innerHTML = Ext.escapeHTML(data[field]); + this.fields[field].dom.innerHTML = Ext.util.Format.htmlEncode( + data[field] + ); } this.oldData = data; }, diff --git a/deluge/ui/web/js/deluge-all/details/FilesTab.js b/deluge/ui/web/js/deluge-all/details/FilesTab.js index 3a212fa..60de832 100644 --- a/deluge/ui/web/js/deluge-all/details/FilesTab.js +++ b/deluge/ui/web/js/deluge-all/details/FilesTab.js @@ -18,13 +18,14 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { header: _('Filename'), width: 330, dataIndex: 'filename', + tpl: new Ext.XTemplate('{filename:htmlEncode}'), }, { header: _('Size'), width: 150, dataIndex: 'size', tpl: new Ext.XTemplate('{size:this.fsize}', { - fsize: function(v) { + fsize: function (v) { return fsize(v); }, }), @@ -34,7 +35,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { header: _('Progress'), width: 150, dataIndex: 'progress', - renderer: function(v) { + renderer: function (v) { var progress = v * 100; return Deluge.progressBar( progress, @@ -54,11 +55,11 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { '{priority:this.getName}' + '</div></tpl>', { - getClass: function(v) { + getClass: function (v) { return FILE_PRIORITY_CSS[v]; }, - getName: function(v) { + getName: function (v) { return _(FILE_PRIORITY[v]); }, } @@ -68,15 +69,15 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { selModel: new Ext.tree.MultiSelectionModel(), - initComponent: function() { + initComponent: function () { Deluge.details.FilesTab.superclass.initComponent.call(this); this.setRootNode(new Ext.tree.TreeNode({ text: _('Files') })); }, - clear: function() { + clear: function () { var root = this.getRootNode(); if (!root.hasChildNodes()) return; - root.cascade(function(node) { + root.cascade(function (node) { var parentNode = node.parentNode; if (!parentNode) return; if (!parentNode.ownerTree) return; @@ -84,7 +85,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { }); }, - createFileTree: function(files) { + createFileTree: function (files) { function walk(files, parentNode) { for (var file in files.contents) { var item = files.contents[file]; @@ -123,7 +124,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { root.firstChild.expand(); }, - update: function(torrentId) { + update: function (torrentId) { if (this.torrentId != torrentId) { this.clear(); this.torrentId = torrentId; @@ -136,7 +137,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { }); }, - updateFileTree: function(files) { + updateFileTree: function (files) { function walk(files, parentNode) { for (var file in files.contents) { var item = files.contents[file]; @@ -153,7 +154,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { walk(files, this.getRootNode()); }, - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.FilesTab.superclass.onRender.call(this, ct, position); deluge.menus.filePriorities.on('itemclick', this.onItemClick, this); this.on('contextmenu', this.onContextMenu, this); @@ -162,7 +163,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { }); }, - onContextMenu: function(node, e) { + onContextMenu: function (node, e) { e.stopEvent(); var selModel = this.getSelectionModel(); if (selModel.getSelectedNodes().length < 2) { @@ -172,14 +173,14 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { deluge.menus.filePriorities.showAt(e.getPoint()); }, - onItemClick: function(baseItem, e) { + onItemClick: function (baseItem, e) { switch (baseItem.id) { case 'expandAll': this.expandAll(); break; default: var indexes = {}; - var walk = function(node) { + var walk = function (node) { if (Ext.isEmpty(node.attributes.fileIndex)) return; indexes[node.attributes.fileIndex] = node.attributes.priority; @@ -187,9 +188,9 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { this.getRootNode().cascade(walk); var nodes = this.getSelectionModel().getSelectedNodes(); - Ext.each(nodes, function(node) { + Ext.each(nodes, function (node) { if (!node.isLeaf()) { - var setPriorities = function(node) { + var setPriorities = function (node) { if (Ext.isEmpty(node.attributes.fileIndex)) return; indexes[node.attributes.fileIndex] = baseItem.filePriority; @@ -211,8 +212,8 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { [this.torrentId], { file_priorities: priorities }, { - success: function() { - Ext.each(nodes, function(node) { + success: function () { + Ext.each(nodes, function (node) { node.setColumnValue(3, baseItem.filePriority); }); }, @@ -223,7 +224,7 @@ Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { } }, - onRequestComplete: function(files, options) { + onRequestComplete: function (files, options) { if (!this.getRootNode().hasChildNodes()) { this.createFileTree(files); } else { diff --git a/deluge/ui/web/js/deluge-all/details/OptionsTab.js b/deluge/ui/web/js/deluge-all/details/OptionsTab.js index b11486b..7e59cba 100644 --- a/deluge/ui/web/js/deluge-all/details/OptionsTab.js +++ b/deluge/ui/web/js/deluge-all/details/OptionsTab.js @@ -9,7 +9,7 @@ */ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { autoScroll: true, @@ -30,7 +30,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { Deluge.details.OptionsTab.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.details.OptionsTab.superclass.initComponent.call(this); (this.fieldsets = {}), (this.fields = {}); @@ -339,7 +339,7 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { }); }, - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.OptionsTab.superclass.onRender.call(this, ct, position); // This is another hack I think, so keep an eye out here when upgrading. @@ -348,17 +348,17 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { this.doLayout(); }, - clear: function() { + clear: function () { if (this.torrentId == null) return; this.torrentId = null; this.optionsManager.changeId(null); }, - reset: function() { + reset: function () { if (this.torrentId) this.optionsManager.reset(); }, - update: function(torrentId) { + update: function (torrentId) { if (this.torrentId && !torrentId) this.clear(); // we want to clear the pane if we get a null torrent torrentIds if (!torrentId) return; // We do not care about null torrentIds. @@ -373,33 +373,33 @@ Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { }); }, - onApply: function() { + onApply: function () { var changed = this.optionsManager.getDirty(); deluge.client.core.set_torrent_options([this.torrentId], changed, { - success: function() { + success: function () { this.optionsManager.commit(); }, scope: this, }); }, - onEditTrackers: function() { + onEditTrackers: function () { deluge.editTrackers.show(); }, - onMoveCompletedChecked: function(checkbox, checked) { + onMoveCompletedChecked: function (checkbox, checked) { this.fields.move_completed_path.setDisabled(!checked); if (!checked) return; this.fields.move_completed_path.focus(); }, - onStopRatioChecked: function(checkbox, checked) { + onStopRatioChecked: function (checkbox, checked) { this.fields.remove_at_ratio.setDisabled(!checked); this.fields.stop_ratio.setDisabled(!checked); }, - onRequestComplete: function(torrent, options) { + onRequestComplete: function (torrent, options) { this.fields['private'].setValue(torrent['private']); this.fields['private'].setDisabled(true); delete torrent['private']; diff --git a/deluge/ui/web/js/deluge-all/details/PeersTab.js b/deluge/ui/web/js/deluge-all/details/PeersTab.js index 515e533..a191963 100644 --- a/deluge/ui/web/js/deluge-all/details/PeersTab.js +++ b/deluge/ui/web/js/deluge-all/details/PeersTab.js @@ -8,13 +8,13 @@ * See LICENSE for more details. */ -(function() { +(function () { function flagRenderer(value) { if (!value.replace(' ', '').replace(' ', '')) { return ''; } return String.format( - '<img src="{0}flag/{1}" />', + '<img alt="{1}" title="{1}" src="{0}flag/{1}" />', deluge.config.base, value ); @@ -40,7 +40,7 @@ // fast way to figure out if we have a peer already. peers: {}, - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { title: _('Peers'), @@ -73,7 +73,7 @@ header: _('Client'), width: 125, sortable: true, - renderer: fplain, + renderer: 'htmlEncode', dataIndex: 'client', }, { @@ -107,19 +107,19 @@ Deluge.details.PeersTab.superclass.constructor.call(this, config); }, - clear: function() { + clear: function () { this.getStore().removeAll(); this.peers = {}; }, - update: function(torrentId) { + update: function (torrentId) { deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Peers, { success: this.onRequestComplete, scope: this, }); }, - onRequestComplete: function(torrent, options) { + onRequestComplete: function (torrent, options) { if (!torrent) return; var store = this.getStore(); @@ -129,7 +129,7 @@ // Go through the peers updating and creating peer records Ext.each( torrent.peers, - function(peer) { + function (peer) { if (this.peers[peer.ip]) { var record = store.getById(peer.ip); record.beginEdit(); @@ -150,7 +150,7 @@ store.add(newPeers); // Remove any peers that should not be left in the store. - store.each(function(record) { + store.each(function (record) { if (!addresses[record.id]) { store.remove(record); delete this.peers[record.id]; diff --git a/deluge/ui/web/js/deluge-all/details/StatusTab.js b/deluge/ui/web/js/deluge-all/details/StatusTab.js index a8753bb..6055161 100644 --- a/deluge/ui/web/js/deluge-all/details/StatusTab.js +++ b/deluge/ui/web/js/deluge-all/details/StatusTab.js @@ -17,7 +17,7 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { title: _('Status'), autoScroll: true, - onRender: function(ct, position) { + onRender: function (ct, position) { Deluge.details.StatusTab.superclass.onRender.call(this, ct, position); this.progressBar = this.add({ @@ -33,7 +33,7 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { width: 1000, listeners: { render: { - fn: function(panel) { + fn: function (panel) { panel.load({ url: deluge.config.base + 'render/tab_status.html', text: _('Loading') + '...', @@ -48,14 +48,14 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { }); }, - clear: function() { + clear: function () { this.progressBar.updateProgress(0, ' '); for (var k in this.fields) { this.fields[k].innerHTML = ''; } }, - update: function(torrentId) { + update: function (torrentId) { if (!this.fields) this.getFields(); deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Status, { success: this.onRequestComplete, @@ -63,18 +63,18 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { }); }, - onPanelUpdate: function(el, response) { + onPanelUpdate: function (el, response) { this.fields = {}; Ext.each( Ext.query('dd', this.status.body.dom), - function(field) { + function (field) { this.fields[field.className] = field; }, this ); }, - onRequestComplete: function(status) { + onRequestComplete: function (status) { seeds = status.total_seeds > -1 ? status.num_seeds + ' (' + status.total_seeds + ')' diff --git a/deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js b/deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js index 031104c..8c32da5 100644 --- a/deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js +++ b/deluge/ui/web/js/deluge-all/preferences/BandwidthPage.js @@ -14,7 +14,7 @@ Ext.namespace('Deluge.preferences'); * @extends Ext.form.FormPanel */ Deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { border: false, @@ -28,7 +28,7 @@ Deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, { Deluge.preferences.Bandwidth.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.preferences.Bandwidth.superclass.initComponent.call(this); var om = deluge.preferences.getOptionsManager(); @@ -117,8 +117,7 @@ Deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, { border: false, title: '', defaultType: 'checkbox', - style: - 'padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;', + style: 'padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;', autoHeight: true, }); om.bind( diff --git a/deluge/ui/web/js/deluge-all/preferences/CachePage.js b/deluge/ui/web/js/deluge-all/preferences/CachePage.js index 2c84c7b..bd5acd8 100644 --- a/deluge/ui/web/js/deluge-all/preferences/CachePage.js +++ b/deluge/ui/web/js/deluge-all/preferences/CachePage.js @@ -19,7 +19,7 @@ Deluge.preferences.Cache = Ext.extend(Ext.form.FormPanel, { header: false, layout: 'form', - initComponent: function() { + initComponent: function () { Deluge.preferences.Cache.superclass.initComponent.call(this); var om = deluge.preferences.getOptionsManager(); diff --git a/deluge/ui/web/js/deluge-all/preferences/DaemonPage.js b/deluge/ui/web/js/deluge-all/preferences/DaemonPage.js index 38f5750..1787826 100644 --- a/deluge/ui/web/js/deluge-all/preferences/DaemonPage.js +++ b/deluge/ui/web/js/deluge-all/preferences/DaemonPage.js @@ -19,7 +19,7 @@ Deluge.preferences.Daemon = Ext.extend(Ext.form.FormPanel, { header: false, layout: 'form', - initComponent: function() { + initComponent: function () { Deluge.preferences.Daemon.superclass.initComponent.call(this); var om = deluge.preferences.getOptionsManager(); diff --git a/deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js b/deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js index bba5e47..04ffd15 100644 --- a/deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js +++ b/deluge/ui/web/js/deluge-all/preferences/DownloadsPage.js @@ -14,7 +14,7 @@ Ext.namespace('Deluge.preferences'); * @extends Ext.form.FormPanel */ Deluge.preferences.Downloads = Ext.extend(Ext.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { border: false, @@ -29,7 +29,7 @@ Deluge.preferences.Downloads = Ext.extend(Ext.FormPanel, { Deluge.preferences.Downloads.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.preferences.Downloads.superclass.initComponent.call(this); var optMan = deluge.preferences.getOptionsManager(); diff --git a/deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js b/deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js index af5ad51..1bcf95e 100644 --- a/deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js +++ b/deluge/ui/web/js/deluge-all/preferences/EncryptionPage.js @@ -18,7 +18,7 @@ Deluge.preferences.Encryption = Ext.extend(Ext.form.FormPanel, { title: _('Encryption'), header: false, - initComponent: function() { + initComponent: function () { Deluge.preferences.Encryption.superclass.initComponent.call(this); var optMan = deluge.preferences.getOptionsManager(); diff --git a/deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js b/deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js index c394664..9aefce3 100644 --- a/deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js +++ b/deluge/ui/web/js/deluge-all/preferences/InstallPluginWindow.js @@ -26,7 +26,7 @@ Deluge.preferences.InstallPluginWindow = Ext.extend(Ext.Window, { modal: true, plain: true, - initComponent: function() { + initComponent: function () { Deluge.preferences.InstallPluginWindow.superclass.initComponent.call( this ); @@ -53,7 +53,7 @@ Deluge.preferences.InstallPluginWindow = Ext.extend(Ext.Window, { }); }, - onInstall: function(field, e) { + onInstall: function (field, e) { this.form.getForm().submit({ url: deluge.config.base + 'upload', waitMsg: _('Uploading your plugin...'), @@ -62,11 +62,11 @@ Deluge.preferences.InstallPluginWindow = Ext.extend(Ext.Window, { }); }, - onUploadPlugin: function(info, obj, response, request) { + onUploadPlugin: function (info, obj, response, request) { this.fireEvent('pluginadded'); }, - onUploadSuccess: function(fp, upload) { + onUploadSuccess: function (fp, upload) { this.hide(); if (upload.result.success) { var filename = this.form.getForm().getFieldValues().file; diff --git a/deluge/ui/web/js/deluge-all/preferences/InterfacePage.js b/deluge/ui/web/js/deluge-all/preferences/InterfacePage.js index f5b0290..b6b76eb 100644 --- a/deluge/ui/web/js/deluge-all/preferences/InterfacePage.js +++ b/deluge/ui/web/js/deluge-all/preferences/InterfacePage.js @@ -19,7 +19,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { header: false, layout: 'form', - initComponent: function() { + initComponent: function () { Deluge.preferences.Interface.superclass.initComponent.call(this); var om = (this.optionsManager = new Deluge.OptionsManager()); @@ -189,7 +189,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { ); }, - onApply: function() { + onApply: function () { var changed = this.optionsManager.getDirty(); if (!Ext.isObjectEmpty(changed)) { deluge.client.web.set_config(changed, { @@ -211,7 +211,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { no: _('Close'), }, multiline: false, - fn: function(btnText) { + fn: function (btnText) { if (btnText === 'yes') location.reload(); }, icon: Ext.MessageBox.QUESTION, @@ -223,21 +223,21 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { } }, - onOk: function() { + onOk: function () { this.onApply(); }, - onGotConfig: function(config) { + onGotConfig: function (config) { this.optionsManager.set(config); }, - onGotLanguages: function(info, obj, response, request) { + onGotLanguages: function (info, obj, response, request) { info.unshift(['', _('System Default')]); this.language.store.loadData(info); this.language.setValue(this.optionsManager.get('language')); }, - onPasswordChange: function() { + onPasswordChange: function () { var newPassword = this.newPassword.getValue(); if (newPassword != this.confirmPassword.getValue()) { Ext.MessageBox.show({ @@ -253,7 +253,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { var oldPassword = this.oldPassword.getValue(); deluge.client.auth.change_password(oldPassword, newPassword, { - success: function(result) { + success: function (result) { if (!result) { Ext.MessageBox.show({ title: _('Password'), @@ -282,11 +282,11 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { }); }, - onSetConfig: function() { + onSetConfig: function () { this.optionsManager.commit(); }, - onPageShow: function() { + onPageShow: function () { deluge.client.web.get_config({ success: this.onGotConfig, scope: this, @@ -297,7 +297,7 @@ Deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { }); }, - onSSLCheck: function(e, checked) { + onSSLCheck: function (e, checked) { this.pkeyField.setDisabled(!checked); this.certField.setDisabled(!checked); }, diff --git a/deluge/ui/web/js/deluge-all/preferences/NetworkPage.js b/deluge/ui/web/js/deluge-all/preferences/NetworkPage.js index 651ba4f..5ba98e7 100644 --- a/deluge/ui/web/js/deluge-all/preferences/NetworkPage.js +++ b/deluge/ui/web/js/deluge-all/preferences/NetworkPage.js @@ -11,7 +11,7 @@ Ext.namespace('Deluge.preferences'); // custom Vtype for vtype:'IPAddress' Ext.apply(Ext.form.VTypes, { - IPAddress: function(v) { + IPAddress: function (v) { return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v); }, IPAddressText: 'Must be a numeric IP address', @@ -28,7 +28,7 @@ Deluge.preferences.Network = Ext.extend(Ext.form.FormPanel, { title: _('Network'), header: false, - initComponent: function() { + initComponent: function () { Deluge.preferences.Network.superclass.initComponent.call(this); var optMan = deluge.preferences.getOptionsManager(); @@ -71,7 +71,7 @@ Deluge.preferences.Network = Ext.extend(Ext.form.FormPanel, { height: 22, listeners: { check: { - fn: function(e, checked) { + fn: function (e, checked) { this.listenPort.setDisabled(checked); }, scope: this, @@ -133,7 +133,7 @@ Deluge.preferences.Network = Ext.extend(Ext.form.FormPanel, { height: 22, listeners: { check: { - fn: function(e, checked) { + fn: function (e, checked) { this.outgoingPorts.setDisabled(checked); }, scope: this, diff --git a/deluge/ui/web/js/deluge-all/preferences/OtherPage.js b/deluge/ui/web/js/deluge-all/preferences/OtherPage.js index 1538203..607da22 100644 --- a/deluge/ui/web/js/deluge-all/preferences/OtherPage.js +++ b/deluge/ui/web/js/deluge-all/preferences/OtherPage.js @@ -14,7 +14,7 @@ Ext.namespace('Deluge.preferences'); * @extends Ext.form.FormPanel */ Deluge.preferences.Other = Ext.extend(Ext.form.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { border: false, @@ -27,7 +27,7 @@ Deluge.preferences.Other = Ext.extend(Ext.form.FormPanel, { Deluge.preferences.Other.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.preferences.Other.superclass.initComponent.call(this); var optMan = deluge.preferences.getOptionsManager(); diff --git a/deluge/ui/web/js/deluge-all/preferences/PluginsPage.js b/deluge/ui/web/js/deluge-all/preferences/PluginsPage.js index e22fc7f..f771d96 100644 --- a/deluge/ui/web/js/deluge-all/preferences/PluginsPage.js +++ b/deluge/ui/web/js/deluge-all/preferences/PluginsPage.js @@ -40,7 +40,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { '</dl>' ), - initComponent: function() { + initComponent: function () { Deluge.preferences.Plugins.superclass.initComponent.call(this); this.defaultValues = { version: '', @@ -50,7 +50,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { }; this.pluginTemplate.compile(); - var checkboxRenderer = function(v, p, record) { + var checkboxRenderer = function (v, p, record) { p.css += ' x-grid3-check-col-td'; return ( '<div class="x-grid3-check-col' + (v ? '-on' : '') + '"> </div>' @@ -72,7 +72,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { width: 0.2, sortable: true, tpl: new Ext.XTemplate('{enabled:this.getCheckbox}', { - getCheckbox: function(v) { + getCheckbox: function (v) { return ( '<div class="x-grid3-check-col' + (v ? '-on' : '') + @@ -141,23 +141,23 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { deluge.events.on('PluginEnabledEvent', this.onPluginEnabled, this); }, - disablePlugin: function(plugin) { + disablePlugin: function (plugin) { deluge.client.core.disable_plugin(plugin); }, - enablePlugin: function(plugin) { + enablePlugin: function (plugin) { deluge.client.core.enable_plugin(plugin); }, - setInfo: function(plugin) { + setInfo: function (plugin) { if (!this.pluginInfo.rendered) return; var values = plugin || this.defaultValues; this.pluginInfo.body.dom.innerHTML = this.pluginTemplate.apply(values); }, - updatePlugins: function() { - var onGotAvailablePlugins = function(plugins) { - this.availablePlugins = plugins.sort(function(a, b) { + updatePlugins: function () { + var onGotAvailablePlugins = function (plugins) { + this.availablePlugins = plugins.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); @@ -167,7 +167,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { }); }; - var onGotEnabledPlugins = function(plugins) { + var onGotEnabledPlugins = function (plugins) { this.enabledPlugins = plugins; this.onGotPlugins(); }; @@ -178,11 +178,11 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { }); }, - updatePluginsGrid: function() { + updatePluginsGrid: function () { var plugins = []; Ext.each( this.availablePlugins, - function(plugin) { + function (plugin) { if (this.enabledPlugins.indexOf(plugin) > -1) { plugins.push([true, plugin]); } else { @@ -194,7 +194,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { this.list.getStore().loadData(plugins); }, - onNodeClick: function(dv, index, node, e) { + onNodeClick: function (dv, index, node, e) { var el = new Ext.Element(e.target); if (el.getAttribute('rel') != 'chkbox') return; @@ -209,16 +209,16 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { } }, - onFindMorePlugins: function() { + onFindMorePlugins: function () { window.open('http://dev.deluge-torrent.org/wiki/Plugins'); }, - onGotPlugins: function() { + onGotPlugins: function () { this.setInfo(); this.updatePluginsGrid(); }, - onGotPluginInfo: function(info) { + onGotPluginInfo: function (info) { var values = { author: info['Author'], version: info['Version'], @@ -230,7 +230,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { delete info; }, - onInstallPluginWindow: function() { + onInstallPluginWindow: function () { if (!this.installWindow) { this.installWindow = new Deluge.preferences.InstallPluginWindow(); this.installWindow.on('pluginadded', this.onPluginInstall, this); @@ -238,7 +238,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { this.installWindow.show(); }, - onPluginEnabled: function(pluginName) { + onPluginEnabled: function (pluginName) { var index = this.list.getStore().find('plugin', pluginName); if (index == -1) return; var plugin = this.list.getStore().getAt(index); @@ -246,7 +246,7 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { plugin.commit(); }, - onPluginDisabled: function(pluginName) { + onPluginDisabled: function (pluginName) { var index = this.list.getStore().find('plugin', pluginName); if (index == -1) return; var plugin = this.list.getStore().getAt(index); @@ -254,11 +254,11 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { plugin.commit(); }, - onPluginInstall: function() { + onPluginInstall: function () { this.updatePlugins(); }, - onPluginSelect: function(dv, selections) { + onPluginSelect: function (dv, selections) { if (selections.length == 0) return; var r = dv.getRecords(selections)[0]; deluge.client.web.get_plugin_info(r.get('plugin'), { @@ -267,11 +267,11 @@ Deluge.preferences.Plugins = Ext.extend(Ext.Panel, { }); }, - onPreferencesShow: function() { + onPreferencesShow: function () { this.updatePlugins(); }, - onPluginInfoRender: function(ct, position) { + onPluginInfoRender: function (ct, position) { this.setInfo(); }, }); diff --git a/deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js b/deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js index 1bc98a8..4cfed01 100644 --- a/deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js +++ b/deluge/ui/web/js/deluge-all/preferences/PreferencesWindow.js @@ -36,7 +36,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { pages: {}, - initComponent: function() { + initComponent: function () { Deluge.preferences.PreferencesWindow.superclass.initComponent.call( this ); @@ -46,7 +46,6 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { columns: [ { id: 'name', - renderer: fplain, dataIndex: 'name', }, ], @@ -94,7 +93,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { this.initPages(); }, - initPages: function() { + initPages: function () { deluge.preferences = this; this.addPage(new Deluge.preferences.Downloads()); this.addPage(new Deluge.preferences.Network()); @@ -109,7 +108,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { this.addPage(new Deluge.preferences.Plugins()); }, - onApply: function(e) { + onApply: function (e) { var changed = this.optionsManager.getDirty(); if (!Ext.isObjectEmpty(changed)) { // Workaround for only displaying single listen port but still pass array to core. @@ -134,7 +133,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { * Return the options manager for the preferences window. * @returns {Deluge.OptionsManager} the options manager */ - getOptionsManager: function() { + getOptionsManager: function () { return this.optionsManager; }, @@ -142,7 +141,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { * Adds a page to the preferences window. * @param {Mixed} page */ - addPage: function(page) { + addPage: function (page) { var store = this.list.getStore(); var name = page.title; store.add([new PreferencesRecord({ name: name })]); @@ -157,7 +156,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { * Removes a preferences page from the window. * @param {mixed} name */ - removePage: function(page) { + removePage: function (page) { var name = page.title; var store = this.list.getStore(); store.removeAt(store.find('name', name)); @@ -169,7 +168,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { * Select which preferences page is displayed. * @param {String} page The page name to change to */ - selectPage: function(page) { + selectPage: function (page) { if (this.pages[page].index < 0) { this.pages[page].index = this.configPanel.items.indexOf( this.pages[page] @@ -179,7 +178,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { }, // private - doSelectPage: function(page) { + doSelectPage: function (page) { if (this.pages[page].index < 0) { this.pages[page].index = this.configPanel.items.indexOf( this.pages[page] @@ -190,23 +189,23 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { }, // private - onGotConfig: function(config) { + onGotConfig: function (config) { this.getOptionsManager().set(config); }, // private - onPageSelect: function(list, selections) { + onPageSelect: function (list, selections) { var r = list.getRecord(selections[0]); this.doSelectPage(r.get('name')); }, // private - onSetConfig: function() { + onSetConfig: function () { this.getOptionsManager().commit(); }, // private - onAfterRender: function() { + onAfterRender: function () { if (!this.list.getSelectionCount()) { this.list.select(0); } @@ -214,7 +213,7 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { }, // private - onShow: function() { + onShow: function () { if (!deluge.client.core) return; deluge.client.core.get_config({ success: this.onGotConfig, @@ -223,12 +222,12 @@ Deluge.preferences.PreferencesWindow = Ext.extend(Ext.Window, { }, // private - onClose: function() { + onClose: function () { this.hide(); }, // private - onOk: function() { + onOk: function () { var changed = this.optionsManager.getDirty(); if (!Ext.isObjectEmpty(changed)) { deluge.client.core.set_config(changed, { diff --git a/deluge/ui/web/js/deluge-all/preferences/ProxyField.js b/deluge/ui/web/js/deluge-all/preferences/ProxyField.js index 6d500ba..d3bb0bf 100644 --- a/deluge/ui/web/js/deluge-all/preferences/ProxyField.js +++ b/deluge/ui/web/js/deluge-all/preferences/ProxyField.js @@ -18,7 +18,7 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { autoHeight: true, labelWidth: 70, - initComponent: function() { + initComponent: function () { Deluge.preferences.ProxyField.superclass.initComponent.call(this); this.proxyType = this.add({ xtype: 'combo', @@ -145,11 +145,11 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { this.setting = false; }, - getName: function() { + getName: function () { return this.initialConfig.name; }, - getValue: function() { + getValue: function () { return { type: this.proxyType.getValue(), hostname: this.hostname.getValue(), @@ -165,7 +165,7 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { }, // Set the values of the proxies - setValue: function(value) { + setValue: function (value) { this.setting = true; this.proxyType.setValue(value['type']); var index = this.proxyType.getStore().find('id', value['type']); @@ -185,7 +185,7 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { this.setting = false; }, - onFieldChange: function(field, newValue, oldValue) { + onFieldChange: function (field, newValue, oldValue) { if (this.setting) return; var newValues = this.getValue(); var oldValues = Ext.apply({}, newValues); @@ -194,7 +194,7 @@ Deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { this.fireEvent('change', this, newValues, oldValues); }, - onTypeSelect: function(combo, record, index) { + onTypeSelect: function (combo, record, index) { var typeId = record.get('id'); if (typeId > 0) { this.hostname.show(); diff --git a/deluge/ui/web/js/deluge-all/preferences/ProxyPage.js b/deluge/ui/web/js/deluge-all/preferences/ProxyPage.js index 4d3c402..2dc4cae 100644 --- a/deluge/ui/web/js/deluge-all/preferences/ProxyPage.js +++ b/deluge/ui/web/js/deluge-all/preferences/ProxyPage.js @@ -14,7 +14,7 @@ Ext.namespace('Deluge.preferences'); * @extends Ext.form.FormPanel */ Deluge.preferences.Proxy = Ext.extend(Ext.form.FormPanel, { - constructor: function(config) { + constructor: function (config) { config = Ext.apply( { border: false, @@ -28,7 +28,7 @@ Deluge.preferences.Proxy = Ext.extend(Ext.form.FormPanel, { Deluge.preferences.Proxy.superclass.constructor.call(this, config); }, - initComponent: function() { + initComponent: function () { Deluge.preferences.Proxy.superclass.initComponent.call(this); this.proxy = this.add( new Deluge.preferences.ProxyField({ @@ -40,19 +40,19 @@ Deluge.preferences.Proxy = Ext.extend(Ext.form.FormPanel, { deluge.preferences.getOptionsManager().bind('proxy', this.proxy); }, - getValue: function() { + getValue: function () { return { proxy: this.proxy.getValue(), }; }, - setValue: function(value) { + setValue: function (value) { for (var proxy in value) { this[proxy].setValue(value[proxy]); } }, - onProxyChange: function(field, newValue, oldValue) { + onProxyChange: function (field, newValue, oldValue) { var newValues = this.getValue(); var oldValues = Ext.apply({}, newValues); oldValues[field.getName()] = oldValue; diff --git a/deluge/ui/web/js/deluge-all/preferences/QueuePage.js b/deluge/ui/web/js/deluge-all/preferences/QueuePage.js index db2da7c..c7b47c5 100644 --- a/deluge/ui/web/js/deluge-all/preferences/QueuePage.js +++ b/deluge/ui/web/js/deluge-all/preferences/QueuePage.js @@ -19,7 +19,7 @@ Deluge.preferences.Queue = Ext.extend(Ext.form.FormPanel, { header: false, layout: 'form', - initComponent: function() { + initComponent: function () { Deluge.preferences.Queue.superclass.initComponent.call(this); var om = deluge.preferences.getOptionsManager(); @@ -227,7 +227,7 @@ Deluge.preferences.Queue = Ext.extend(Ext.form.FormPanel, { om.bind('remove_seed_at_ratio', this.removeAtRatio); }, - onStopRatioCheck: function(e, checked) { + onStopRatioCheck: function (e, checked) { this.stopRatio.setDisabled(!checked); this.removeAtRatio.setDisabled(!checked); }, diff --git a/deluge/ui/web/js/extjs/ext-extensions-debug.js b/deluge/ui/web/js/extjs/ext-extensions-debug.js index a5b4a60..8a05e61 100644 --- a/deluge/ui/web/js/extjs/ext-extensions-debug.js +++ b/deluge/ui/web/js/extjs/ext-extensions-debug.js @@ -50,7 +50,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { autoSize: Ext.emptyFn, // private - initComponent: function() { + initComponent: function () { Ext.ux.form.FileUploadField.superclass.initComponent.call(this); this.addEvents( @@ -66,7 +66,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { }, // private - onRender: function(ct, position) { + onRender: function (ct, position) { Ext.ux.form.FileUploadField.superclass.onRender.call( this, ct, @@ -97,30 +97,30 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { this.resizeEl = this.positionEl = this.wrap; }, - bindListeners: function() { + bindListeners: function () { this.fileInput.on({ scope: this, - mouseenter: function() { + mouseenter: function () { this.button.addClass(['x-btn-over', 'x-btn-focus']); }, - mouseleave: function() { + mouseleave: function () { this.button.removeClass([ 'x-btn-over', 'x-btn-focus', 'x-btn-click', ]); }, - mousedown: function() { + mousedown: function () { this.button.addClass('x-btn-click'); }, - mouseup: function() { + mouseup: function () { this.button.removeClass([ 'x-btn-over', 'x-btn-focus', 'x-btn-click', ]); }, - change: function() { + change: function () { var value = this.fileInput.dom.files; // Fallback to value. if (!value) value = this.fileInput.dom.value; @@ -130,7 +130,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { }); }, - createFileInput: function() { + createFileInput: function () { this.fileInput = this.wrap.createChild({ id: this.getFileInputId(), name: this.name || this.getId(), @@ -142,7 +142,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { this.fileInput.dom.multiple = this.multiple; }, - reset: function() { + reset: function () { if (this.rendered) { this.fileInput.remove(); this.createFileInput(); @@ -152,12 +152,12 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { }, // private - getFileInputId: function() { + getFileInputId: function () { return this.id + '-file'; }, // private - onResize: function(w, h) { + onResize: function (w, h) { Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h); this.wrap.setWidth(w); @@ -172,23 +172,23 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { }, // private - onDestroy: function() { + onDestroy: function () { Ext.ux.form.FileUploadField.superclass.onDestroy.call(this); Ext.destroy(this.fileInput, this.button, this.wrap); }, - onDisable: function() { + onDisable: function () { Ext.ux.form.FileUploadField.superclass.onDisable.call(this); this.doDisable(true); }, - onEnable: function() { + onEnable: function () { Ext.ux.form.FileUploadField.superclass.onEnable.call(this); this.doDisable(false); }, // private - doDisable: function(disabled) { + doDisable: function (disabled) { this.fileInput.dom.disabled = disabled; this.button.setDisabled(disabled); }, @@ -197,7 +197,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { preFocus: Ext.emptyFn, // private - alignErrorIcon: function() { + alignErrorIcon: function () { this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); }, }); @@ -218,8 +218,8 @@ Ext.form.FileUploadField = Ext.ux.form.FileUploadField; // Allow radiogroups to be treated as a single form element. Ext.override(Ext.form.RadioGroup, { - afterRender: function() { - this.items.each(function(i) { + afterRender: function () { + this.items.each(function (i) { this.relayEvents(i, ['check']); }, this); if (this.lazyValue) { @@ -230,21 +230,21 @@ Ext.override(Ext.form.RadioGroup, { Ext.form.RadioGroup.superclass.afterRender.call(this); }, - getName: function() { + getName: function () { return this.items.first().getName(); }, - getValue: function() { + getValue: function () { return this.items.first().getGroupValue(); }, - setValue: function(v) { + setValue: function (v) { if (!this.items.each) { this.value = v; this.lazyValue = true; return; } - this.items.each(function(item) { + this.items.each(function (item) { if (item.rendered) { var checked = item.el.getValue() == String(v); item.el.dom.checked = checked; @@ -277,7 +277,7 @@ Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, { onBlur: Ext.emptyFn, adjustSize: Ext.BoxComponent.prototype.adjustSize, - constructor: function(config) { + constructor: function (config) { var spinnerConfig = Ext.copyTo( {}, config, @@ -299,23 +299,23 @@ Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, { }, // private - getResizeEl: function() { + getResizeEl: function () { return this.wrap; }, // private - getPositionEl: function() { + getPositionEl: function () { return this.wrap; }, // private - alignErrorIcon: function() { + alignErrorIcon: function () { if (this.wrap) { this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); } }, - validateBlur: function() { + validateBlur: function () { return true; }, }); @@ -362,7 +362,7 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { colCfg: {}, // private - onRender: function(ct, position) { + onRender: function (ct, position) { if (!this.el) { var panelCfg = { cls: this.groupCls, @@ -419,9 +419,8 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { // Generate the column configs with the correct width setting for (var i = 0; i < numCols; i++) { var cc = Ext.apply({ items: [] }, colCfg); - cc[ - this.columns[i] <= 1 ? 'columnWidth' : 'width' - ] = this.columns[i]; + cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = + this.columns[i]; if (this.defaults) { cc.defaults = Ext.apply( cc.defaults || {}, @@ -470,14 +469,14 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { } } - var fields = this.panel.findBy(function(c) { + var fields = this.panel.findBy(function (c) { return c.isFormField; }, this); this.items = new Ext.util.MixedCollection(); this.items.addAll(fields); - this.items.each(function(field) { + this.items.each(function (field) { field.on('spin', this.onFieldChange, this); field.on('change', this.onFieldChange, this); }, this); @@ -498,45 +497,45 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { Ext.ux.form.SpinnerGroup.superclass.onRender.call(this, ct, position); }, - onFieldChange: function(spinner) { + onFieldChange: function (spinner) { this.fireEvent('change', this, this.getValue()); }, initValue: Ext.emptyFn, - getValue: function() { + getValue: function () { var value = [this.items.getCount()]; - this.items.each(function(item, i) { + this.items.each(function (item, i) { value[i] = Number(item.getValue()); }); return value; }, - getRawValue: function() { + getRawValue: function () { var value = [this.items.getCount()]; - this.items.each(function(item, i) { + this.items.each(function (item, i) { value[i] = Number(item.getRawValue()); }); return value; }, - setValue: function(value) { + setValue: function (value) { if (!this.rendered) { this.value = value; this.lazyValueSet = true; } else { - this.items.each(function(item, i) { + this.items.each(function (item, i) { item.setValue(value[i]); }); } }, - setRawValue: function(value) { + setRawValue: function (value) { if (!this.rendered) { this.rawValue = value; this.lazyRawValueSet = true; } else { - this.items.each(function(item, i) { + this.items.each(function (item, i) { item.setRawValue(value[i]); }); } @@ -566,7 +565,7 @@ Ext.namespace('Ext.ux.form'); Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { cls: 'x-toggle-field', - initComponent: function() { + initComponent: function () { Ext.ux.form.ToggleField.superclass.initComponent.call(this); this.toggle = new Ext.form.Checkbox(); @@ -577,7 +576,7 @@ Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { }); }, - onRender: function(ct, position) { + onRender: function (ct, position) { if (!this.el) { this.panel = new Ext.Panel({ cls: this.groupCls, @@ -595,16 +594,13 @@ Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { this.panel.add(this.input); this.panel.doLayout(); - this.toggle - .getEl() - .parent() - .setStyle('padding-right', '10px'); + this.toggle.getEl().parent().setStyle('padding-right', '10px'); } Ext.ux.form.ToggleField.superclass.onRender.call(this, ct, position); }, // private - onResize: function(w, h) { + onResize: function (w, h) { this.panel.setSize(w, h); this.panel.doLayout(); @@ -613,7 +609,7 @@ Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { this.input.setSize(inputWidth, h); }, - onToggleCheck: function(toggle, checked) { + onToggleCheck: function (toggle, checked) { this.input.setDisabled(!checked); }, }); @@ -666,7 +662,7 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { */ cleanDelay: 500, - initTemplates: function() { + initTemplates: function () { Ext.ux.grid.BufferView.superclass.initTemplates.call(this); var ts = this.templates; // empty div to act as a place holder for a row @@ -688,23 +684,23 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { ts.rowBody.compile(); }, - getStyleRowHeight: function() { + getStyleRowHeight: function () { return Ext.isBorderBox ? this.rowHeight + this.borderHeight : this.rowHeight; }, - getCalculatedRowHeight: function() { + getCalculatedRowHeight: function () { return this.rowHeight + this.borderHeight; }, - getVisibleRowCount: function() { + getVisibleRowCount: function () { var rh = this.getCalculatedRowHeight(), visibleHeight = this.scroller.dom.clientHeight; return visibleHeight < 1 ? 0 : Math.ceil(visibleHeight / rh); }, - getVisibleRows: function() { + getVisibleRows: function () { var count = this.getVisibleRowCount(), sc = this.scroller.dom.scrollTop, start = @@ -717,7 +713,7 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { }; }, - doRender: function(cs, rs, ds, startRow, colCount, stripe, onlyBody) { + doRender: function (cs, rs, ds, startRow, colCount, stripe, onlyBody) { var ts = this.templates, ct = ts.cell, rt = ts.row, @@ -782,18 +778,18 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { return buf.join(''); }, - isRowRendered: function(index) { + isRowRendered: function (index) { var row = this.getRow(index); return row && row.childNodes.length > 0; }, - syncScroll: function() { + syncScroll: function () { Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments); this.update(); }, // a (optionally) buffered method to update contents of gridview - update: function() { + update: function () { if (this.scrollDelay) { if (!this.renderTask) { this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this); @@ -804,14 +800,14 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { } }, - onRemove: function(ds, record, index, isUpdate) { + onRemove: function (ds, record, index, isUpdate) { Ext.ux.grid.BufferView.superclass.onRemove.apply(this, arguments); if (isUpdate !== true) { this.update(); } }, - doUpdate: function() { + doUpdate: function () { if (this.getVisibleRowCount() > 0) { var g = this.grid, cm = g.colModel, @@ -839,14 +835,14 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { }, // a buffered method to clean rows - clean: function() { + clean: function () { if (!this.cleanTask) { this.cleanTask = new Ext.util.DelayedTask(this.doClean, this); } this.cleanTask.delay(this.cleanDelay); }, - doClean: function() { + doClean: function () { if (this.getVisibleRowCount() > 0) { var vr = this.getVisibleRows(); vr.first -= this.cacheSize; @@ -869,7 +865,7 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { } }, - removeTask: function(name) { + removeTask: function (name) { var task = this[name]; if (task && task.cancel) { task.cancel(); @@ -877,13 +873,13 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { } }, - destroy: function() { + destroy: function () { this.removeTask('cleanTask'); this.removeTask('renderTask'); Ext.ux.grid.BufferView.superclass.destroy.call(this); }, - layout: function() { + layout: function () { Ext.ux.grid.BufferView.superclass.layout.call(this); this.update(); }, @@ -902,7 +898,7 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { // remove spaces for hidden elements and make show(), hide(), enable() and disable() act on // the label. don't use hideLabel with this. Ext.override(Ext.layout.FormLayout, { - renderItem: function(c, position, target) { + renderItem: function (c, position, target) { if ( c && !c.rendered && @@ -942,7 +938,7 @@ Ext.override(Ext.layout.FormLayout, { * @author Damien Churchill <damoxc@gmail.com> */ Ext.override(Ext.tree.MultiSelectionModel, { - onNodeClick: function(node, e) { + onNodeClick: function (node, e) { if (e.ctrlKey && this.isSelected(node)) { this.unselect(node); } else if (e.shiftKey && !this.isSelected(node)) { @@ -963,7 +959,7 @@ Ext.override(Ext.tree.MultiSelectionModel, { } // Select all the nodes - parentNode.eachChild(function(n) { + parentNode.eachChild(function (n) { var i = parentNode.indexOf(n); if (fi < i && i < li) { this.select(n, e, true, true); @@ -977,7 +973,7 @@ Ext.override(Ext.tree.MultiSelectionModel, { } }, - select: function(node, e, keepExisting, suppressEvent) { + select: function (node, e, keepExisting, suppressEvent) { if (keepExisting !== true) { this.clearSelections(true); } @@ -1023,7 +1019,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { columnsText: 'Columns', - initComponent: function() { + initComponent: function () { if (!this.root) { this.root = new Ext.tree.AsyncTreeNode({ text: 'Root' }); } @@ -1095,7 +1091,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - initColumns: function() { + initColumns: function () { var cs = this.columns, len = cs.length, columns = [], @@ -1124,7 +1120,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { this.columns = columns; }, - onRender: function() { + onRender: function () { Ext.tree.TreePanel.superclass.onRender.apply(this, arguments); this.el.addClass('x-treegrid'); @@ -1173,7 +1169,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - setRootNode: function(node) { + setRootNode: function (node) { node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI; node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node); if (this.innerCt) { @@ -1184,7 +1180,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { return node; }, - clearInnerCt: function() { + clearInnerCt: function () { if (Ext.isIE) { var dom = this.innerCt.dom; while (dom.firstChild) { @@ -1195,7 +1191,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - initEvents: function() { + initEvents: function () { Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments); this.mon(this.innerBody, 'scroll', this.syncScroll, this); @@ -1207,7 +1203,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }); }, - onResize: function(w, h) { + onResize: function (w, h) { Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments); var bd = this.innerBody.dom; @@ -1231,7 +1227,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { this.setScrollOffset(sw); } else { var me = this; - setTimeout(function() { + setTimeout(function () { me.setScrollOffset( bd.offsetWidth - bd.clientWidth > 10 ? sw : 0 ); @@ -1240,7 +1236,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - updateColumnWidths: function() { + updateColumnWidths: function () { var cols = this.columns, colCount = cols.length, groups = this.outerCt.query('colgroup'), @@ -1279,7 +1275,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { this.syncHeaderScroll(); }, - getVisibleColumns: function() { + getVisibleColumns: function () { var columns = [], cs = this.columns, len = cs.length, @@ -1293,7 +1289,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { return columns; }, - getTotalColumnWidth: function() { + getTotalColumnWidth: function () { var total = 0; for ( var i = 0, cs = this.getVisibleColumns(), len = cs.length; @@ -1305,13 +1301,13 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { return total; }, - setScrollOffset: function(scrollOffset) { + setScrollOffset: function (scrollOffset) { this.scrollOffset = scrollOffset; this.updateColumnWidths(); }, // private - handleHdDown: function(e, t) { + handleHdDown: function (e, t) { var hd = e.getTarget('.x-treegrid-hd'); if (hd && Ext.fly(t).hasClass('x-grid3-hd-btn')) { @@ -1329,7 +1325,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { this.hmenu.on( 'hide', - function() { + function () { Ext.fly(hd).removeClass('x-grid3-hd-menu-open'); }, this, @@ -1344,7 +1340,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }, // private - handleHdOver: function(e, t) { + handleHdOver: function (e, t) { var hd = e.getTarget('.x-treegrid-hd'); if (hd && !this.headersDisabled) { index = this.findHeaderIndex(hd); @@ -1362,7 +1358,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }, // private - handleHdOut: function(e, t) { + handleHdOut: function (e, t) { var hd = e.getTarget('.x-treegrid-hd'); if (hd && (!Ext.isIE || !e.within(hd, true))) { this.activeHdRef = null; @@ -1371,7 +1367,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - findHeaderIndex: function(hd) { + findHeaderIndex: function (hd) { hd = hd.dom || hd; var cs = hd.parentNode.childNodes; for (var i = 0, c; (c = cs[i]); i++) { @@ -1383,7 +1379,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }, // private - beforeColMenuShow: function() { + beforeColMenuShow: function () { var cols = this.columns, colCount = cols.length, i, @@ -1406,7 +1402,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }, // private - handleHdMenuClick: function(item) { + handleHdMenuClick: function (item) { var index = this.hdCtxIndex, id = item.getItemId(); @@ -1427,7 +1423,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { return true; }, - setColumnVisible: function(index, visible) { + setColumnVisible: function (index, visible) { this.columns[index].hidden = !visible; this.updateColumnWidths(); }, @@ -1435,26 +1431,26 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { /** * Scrolls the grid to the top */ - scrollToTop: function() { + scrollToTop: function () { this.innerBody.dom.scrollTop = 0; this.innerBody.dom.scrollLeft = 0; }, // private - syncScroll: function() { + syncScroll: function () { this.syncHeaderScroll(); var mb = this.innerBody.dom; this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop); }, // private - syncHeaderScroll: function() { + syncHeaderScroll: function () { var mb = this.innerBody.dom; this.innerHd.dom.scrollLeft = mb.scrollLeft; this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore) }, - registerNode: function(n) { + registerNode: function (n) { Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n); if (!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) { n.ui = new Ext.ux.tree.TreeGridNodeUI(n); @@ -1480,17 +1476,17 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { */ minWidth: 14, - constructor: function(config) { + constructor: function (config) { Ext.apply(this, config); Ext.tree.ColumnResizer.superclass.constructor.call(this); }, - init: function(tree) { + init: function (tree) { this.tree = tree; tree.on('render', this.initEvents, this); }, - initEvents: function(tree) { + initEvents: function (tree) { tree.mon(tree.innerHd, 'mousemove', this.handleHdMove, this); this.tracker = new Ext.dd.DragTracker({ onBeforeStart: this.onBeforeStart.createDelegate(this), @@ -1504,7 +1500,7 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { tree.on('beforedestroy', this.tracker.destroy, this.tracker); }, - handleHdMove: function(e, t) { + handleHdMove: function (e, t) { var hw = 5, x = e.getPageX(), hd = e.getTarget('.x-treegrid-hd', 3, true); @@ -1539,12 +1535,12 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { } }, - onBeforeStart: function(e) { + onBeforeStart: function (e) { this.dragHd = this.activeHd; return !!this.dragHd; }, - onStart: function(e) { + onStart: function (e) { this.dragHeadersDisabled = this.tree.headersDisabled; this.tree.headersDisabled = true; this.proxy = this.tree.body.createChild({ cls: 'x-treegrid-resizer' }); @@ -1563,14 +1559,14 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { this.tree.innerBody.translatePoints(this.hdX).left; }, - onDrag: function(e) { + onDrag: function (e) { var cursorX = this.tracker.getXY()[0]; this.proxy.setWidth( (cursorX - this.hdX).constrain(this.minWidth, this.maxWidth) ); }, - onEnd: function(e) { + onEnd: function (e) { var nw = this.proxy.getWidth(), tree = this.tree, disabled = this.dragHeadersDisabled; @@ -1581,7 +1577,7 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { tree.columns[this.hdIndex].width = nw; tree.updateColumnWidths(); - setTimeout(function() { + setTimeout(function () { tree.headersDisabled = disabled; }, 100); }, @@ -1592,9 +1588,9 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { * licensing@sencha.com * http://www.sencha.com/license */ -(function() { +(function () { Ext.override(Ext.list.Column, { - init: function() { + init: function () { var types = Ext.data.Types, st = this.sortType; @@ -1637,7 +1633,7 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { * @extends Ext.tree.TreeLoader */ Ext.ux.tree.TreeGridLoader = Ext.extend(Ext.tree.TreeLoader, { - createNode: function(attr) { + createNode: function (attr) { if (!attr.uiProvider) { attr.uiProvider = Ext.ux.tree.TreeGridNodeUI; } @@ -1657,7 +1653,7 @@ Ext.ux.tree.TreeGridLoader = Ext.extend(Ext.tree.TreeLoader, { Ext.ux.tree.TreeGridNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { isTreeGridNodeUI: true, - renderElements: function(n, a, targetNode, bulkRender) { + renderElements: function (n, a, targetNode, bulkRender) { var t = n.getOwnerTree(), cols = t.columns, c = cols[0], @@ -1757,7 +1753,7 @@ Ext.ux.tree.TreeGridNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { }, // private - animExpand: function(cb) { + animExpand: function (cb) { this.ctNode.style.display = ''; Ext.ux.tree.TreeGridNodeUI.superclass.animExpand.call(this, cb); }, @@ -1767,7 +1763,7 @@ Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { isTreeGridNodeUI: true, // private - render: function() { + render: function () { if (!this.rendered) { this.wrap = this.ctNode = this.node.ownerTree.innerCt.dom; this.node.expanded = true; @@ -1777,13 +1773,13 @@ Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { // weird table-layout: fixed issue in webkit var ct = this.ctNode; ct.style.tableLayout = null; - (function() { + (function () { ct.style.tableLayout = 'fixed'; }.defer(1)); } }, - destroy: function() { + destroy: function () { if (this.elNode) { Ext.dd.Registry.unregister(this.elNode.id); } @@ -1804,7 +1800,7 @@ Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { */ Ext.override(Ext.ux.tree.TreeGridNodeUI, { - updateColumns: function() { + updateColumns: function () { if (!this.rendered) return; var a = this.node.attributes, @@ -1827,7 +1823,7 @@ Ext.override(Ext.ux.tree.TreeGridNodeUI, { }, }); Ext.tree.RenderColumn = Ext.extend(Ext.tree.Column, { - constructor: function(c) { + constructor: function (c) { c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':this.format}'); c.tpl.format = c.renderer; c.tpl.col = this; @@ -1877,7 +1873,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { */ sortDescText: 'Sort Descending', - constructor: function(tree, config) { + constructor: function (tree, config) { if (!Ext.isObject(config)) { config = { property: tree.columns[0].dataIndex || 'text', @@ -1895,7 +1891,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { tree.ddAppendOnly = true; var me = this; - this.defaultSortFn = function(n1, n2) { + this.defaultSortFn = function (n1, n2) { var desc = me.dir && me.dir.toLowerCase() == 'desc', prop = me.property || 'text', sortType = me.sortType, @@ -1938,7 +1934,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { tree.on('headermenuclick', this.onHeaderMenuClick, this); }, - onAfterTreeRender: function() { + onAfterTreeRender: function () { if (this.tree.hmenu) { this.tree.hmenu.insert( 0, @@ -1957,14 +1953,14 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { this.updateSortIcon(0, 'asc'); }, - onHeaderMenuClick: function(c, id, index) { + onHeaderMenuClick: function (c, id, index) { if (id === 'asc' || id === 'desc') { this.onHeaderClick(c, null, index); return false; } }, - onHeaderClick: function(c, el, i) { + onHeaderClick: function (c, el, i) { if (c && !this.tree.headersDisabled) { var me = this; @@ -1976,7 +1972,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { : this.caseSensitive; me.sortFn = c.sortFn || this.defaultSortFn; - this.tree.root.cascade(function(n) { + this.tree.root.cascade(function (n) { if (!n.isLeaf()) { me.updateSort(me.tree, n); } @@ -1987,13 +1983,13 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { }, // private - updateSortIcon: function(col, dir) { + updateSortIcon: function (col, dir) { var sc = this.sortClasses, hds = this.tree.innerHd.select('td').removeClass(sc); hds.item(col).addClass(sc[dir == 'desc' ? 1 : 0]); }, }); -Ext.ux.JSLoader = function(options) { +Ext.ux.JSLoader = function (options) { Ext.ux.JSLoader.scripts[++Ext.ux.JSLoader.index] = { url: options.url, success: true, @@ -2007,7 +2003,7 @@ Ext.ux.JSLoader = function(options) { Ext.Ajax.request({ url: options.url, scriptIndex: Ext.ux.JSLoader.index, - success: function(response, options) { + success: function (response, options) { var script = Ext.ux.JSLoader.scripts[options.scriptIndex]; try { eval(response.responseText); @@ -2019,7 +2015,7 @@ Ext.ux.JSLoader = function(options) { script.onLoad.call(script.scope, script.options); } }, - failure: function(response, options) { + failure: function (response, options) { var script = Ext.ux.JSLoader.scripts[options.scriptIndex]; script.success = false; script.onError(script.options, response.status); @@ -2028,7 +2024,7 @@ Ext.ux.JSLoader = function(options) { }; Ext.ux.JSLoader.index = 0; Ext.ux.JSLoader.scripts = []; -Ext.ux.JSLoader.stdError = function(options, e) { +Ext.ux.JSLoader.stdError = function (options, e) { window.alert( 'Error loading script:\n\n' + options.url + '\n\nstatus: ' + e ); @@ -2053,13 +2049,13 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { defaultValue: 0, accelerate: false, - constructor: function(config) { + constructor: function (config) { Ext.ux.Spinner.superclass.constructor.call(this, config); Ext.apply(this, config); this.mimicing = false; }, - init: function(field) { + init: function (field) { this.field = field; field.afterMethod('onRender', this.doRender, this); @@ -2071,7 +2067,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { field.beforeMethod('onDestroy', this.doDestroy, this); }, - doRender: function(ct, position) { + doRender: function (ct, position) { var el = (this.el = this.field.getEl()); var f = this.field; @@ -2116,7 +2112,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.initSpinner(); }, - doAfterRender: function() { + doAfterRender: function () { var y; if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) { this.el.position(); @@ -2124,14 +2120,14 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { } }, - doEnable: function() { + doEnable: function () { if (this.wrap) { this.disabled = false; this.wrap.removeClass(this.field.disabledClass); } }, - doDisable: function() { + doDisable: function () { if (this.wrap) { this.disabled = true; this.wrap.addClass(this.field.disabledClass); @@ -2139,14 +2135,14 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { } }, - doResize: function(w, h) { + doResize: function (w, h) { if (typeof w == 'number') { this.el.setWidth(w - this.trigger.getWidth()); } this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth()); }, - doFocus: function() { + doFocus: function () { if (!this.mimicing) { this.wrap.addClass('x-trigger-wrap-focus'); this.mimicing = true; @@ -2163,21 +2159,21 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, // private - checkTab: function(e) { + checkTab: function (e) { if (e.getKey() == e.TAB) { this.triggerBlur(); } }, // private - mimicBlur: function(e) { + mimicBlur: function (e) { if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) { this.triggerBlur(); } }, // private - triggerBlur: function() { + triggerBlur: function () { this.mimicing = false; Ext.get(Ext.isIE ? document.body : document).un( 'mousedown', @@ -2190,12 +2186,12 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.field.onBlur.call(this.field); }, - initTrigger: function() { + initTrigger: function () { this.trigger.addClassOnOver('x-form-trigger-over'); this.trigger.addClassOnClick('x-form-trigger-click'); }, - initSpinner: function() { + initSpinner: function () { this.field.addEvents({ spin: true, spinup: true, @@ -2203,22 +2199,22 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }); this.keyNav = new Ext.KeyNav(this.el, { - up: function(e) { + up: function (e) { e.preventDefault(); this.onSpinUp(); }, - down: function(e) { + down: function (e) { e.preventDefault(); this.onSpinDown(); }, - pageUp: function(e) { + pageUp: function (e) { e.preventDefault(); this.onSpinUpAlternate(); }, - pageDown: function(e) { + pageDown: function (e) { e.preventDefault(); this.onSpinDownAlternate(); }, @@ -2252,7 +2248,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.dd.onDrag = this.onDrag.createDelegate(this); }, - onMouseOver: function() { + onMouseOver: function () { if (this.disabled) { return; } @@ -2265,12 +2261,12 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onMouseOut: function() { + onMouseOut: function () { this.trigger.removeClass(this.tmpHoverClass); }, //private - onMouseMove: function() { + onMouseMove: function () { if (this.disabled) { return; } @@ -2285,7 +2281,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onMouseDown: function() { + onMouseDown: function () { if (this.disabled) { return; } @@ -2298,12 +2294,12 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onMouseUp: function() { + onMouseUp: function () { this.trigger.removeClass(this.tmpClickClass); }, //private - onTriggerClick: function() { + onTriggerClick: function () { if (this.disabled || this.el.dom.readOnly) { return; } @@ -2313,7 +2309,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - getMiddle: function() { + getMiddle: function () { var t = this.trigger.getTop(); var h = this.trigger.getHeight(); var middle = t + h / 2; @@ -2322,7 +2318,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { //private //checks if control is allowed to spin - isSpinnable: function() { + isSpinnable: function () { if (this.disabled || this.el.dom.readOnly) { Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly return false; @@ -2330,7 +2326,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { return true; }, - handleMouseWheel: function(e) { + handleMouseWheel: function (e) { //disable scrolling when not focused if (this.wrap.hasClass('x-trigger-wrap-focus') == false) { return; @@ -2347,18 +2343,18 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - startDrag: function() { + startDrag: function () { this.proxy.show(); this._previousY = Ext.fly(this.dd.getDragEl()).getTop(); }, //private - endDrag: function() { + endDrag: function () { this.proxy.hide(); }, //private - onDrag: function() { + onDrag: function () { if (this.disabled) { return; } @@ -2379,7 +2375,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onSpinUp: function() { + onSpinUp: function () { if (this.isSpinnable() == false) { return; } @@ -2394,7 +2390,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onSpinDown: function() { + onSpinDown: function () { if (this.isSpinnable() == false) { return; } @@ -2409,7 +2405,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onSpinUpAlternate: function() { + onSpinUpAlternate: function () { if (this.isSpinnable() == false) { return; } @@ -2419,7 +2415,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onSpinDownAlternate: function() { + onSpinDownAlternate: function () { if (this.isSpinnable() == false) { return; } @@ -2428,7 +2424,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.field.fireEvent('spindown', this); }, - spin: function(down, alternate) { + spin: function (down, alternate) { var v = parseFloat(this.field.getValue()); var incr = alternate == true @@ -2441,7 +2437,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.field.setRawValue(v); }, - fixBoundries: function(value) { + fixBoundries: function (value) { var v = value; if (this.field.minValue != undefined && v < this.field.minValue) { @@ -2455,7 +2451,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, // private - fixPrecision: function(value) { + fixPrecision: function (value) { var nan = isNaN(value); if ( !this.field.allowDecimals || @@ -2470,7 +2466,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { ); }, - doDestroy: function() { + doDestroy: function () { if (this.trigger) { this.trigger.remove(); } @@ -2692,7 +2688,7 @@ sb.setStatus({ activeThreadId: 0, // private - initComponent: function() { + initComponent: function () { if (this.statusAlign == 'right') { this.cls += ' x-status-right'; } @@ -2700,7 +2696,7 @@ sb.setStatus({ }, // private - afterRender: function() { + afterRender: function () { Ext.ux.StatusBar.superclass.afterRender.call(this); var right = this.statusAlign == 'right'; @@ -2769,7 +2765,7 @@ statusBar.setStatus({ </code></pre> * @return {Ext.ux.StatusBar} this */ - setStatus: function(o) { + setStatus: function (o) { o = o || {}; if (typeof o == 'string') { @@ -2816,7 +2812,7 @@ statusBar.setStatus({ * </ul> * @return {Ext.ux.StatusBar} this */ - clearStatus: function(o) { + clearStatus: function (o) { o = o || {}; if (o.threadId && o.threadId !== this.activeThreadId) { @@ -2839,7 +2835,7 @@ statusBar.setStatus({ remove: false, useDisplay: true, scope: this, - callback: function() { + callback: function () { this.setStatus({ text: text, iconCls: iconCls, @@ -2865,7 +2861,7 @@ statusBar.setStatus({ * @param {String} text (optional) The text to set (defaults to '') * @return {Ext.ux.StatusBar} this */ - setText: function(text) { + setText: function (text) { this.activeThreadId++; this.text = text || ''; if (this.rendered) { @@ -2878,7 +2874,7 @@ statusBar.setStatus({ * Returns the current status text. * @return {String} The status text */ - getText: function() { + getText: function () { return this.text; }, @@ -2888,7 +2884,7 @@ statusBar.setStatus({ * @param {String} iconCls (optional) The icon class to set (defaults to '', and any current icon class is removed) * @return {Ext.ux.StatusBar} this */ - setIcon: function(cls) { + setIcon: function (cls) { this.activeThreadId++; cls = cls || ''; @@ -2917,7 +2913,7 @@ statusBar.setStatus({ * {@link #busyIconCls} will be used in conjunction with all of the default options for {@link #setStatus}. * @return {Ext.ux.StatusBar} this */ - showBusy: function(o) { + showBusy: function (o) { if (typeof o == 'string') { o = { text: o }; } diff --git a/deluge/ui/web/js/extjs/ext-extensions.js b/deluge/ui/web/js/extjs/ext-extensions.js new file mode 100644 index 0000000..e6cb62c --- /dev/null +++ b/deluge/ui/web/js/extjs/ext-extensions.js @@ -0,0 +1,84 @@ +Ext.ns("Ext.ux.form"); +Ext.ux.form.FileUploadField=Ext.extend(Ext.form.TextField,{buttonText:"Browse...",buttonOnly:!1,buttonOffset:3,multiple:!1,readOnly:!0,autoSize:Ext.emptyFn,initComponent:function(){Ext.ux.form.FileUploadField.superclass.initComponent.call(this);this.addEvents("fileselected")},onRender:function(a,b){Ext.ux.form.FileUploadField.superclass.onRender.call(this,a,b);this.wrap=this.el.wrap({cls:"x-form-field-wrap x-form-file-wrap"});this.el.addClass("x-form-file-text");this.el.dom.removeAttribute("name");this.createFileInput(); +var c=Ext.applyIf(this.buttonCfg||{},{text:this.buttonText});this.button=new Ext.Button(Ext.apply(c,{renderTo:this.wrap,cls:"x-form-file-btn"+(c.iconCls?" x-btn-icon":"")}));this.buttonOnly&&(this.el.hide(),this.wrap.setWidth(this.button.getEl().getWidth()));this.bindListeners();this.resizeEl=this.positionEl=this.wrap},bindListeners:function(){this.fileInput.on({scope:this,mouseenter:function(){this.button.addClass(["x-btn-over","x-btn-focus"])},mouseleave:function(){this.button.removeClass(["x-btn-over", +"x-btn-focus","x-btn-click"])},mousedown:function(){this.button.addClass("x-btn-click")},mouseup:function(){this.button.removeClass(["x-btn-over","x-btn-focus","x-btn-click"])},change:function(){var a=this.fileInput.dom.files;a||(a=this.fileInput.dom.value);this.setValue(a);this.fireEvent("fileselected",this,a)}})},createFileInput:function(){this.fileInput=this.wrap.createChild({id:this.getFileInputId(),name:this.name||this.getId(),cls:"x-form-file",tag:"input",type:"file",size:1});this.fileInput.dom.multiple= +this.multiple},reset:function(){this.rendered&&(this.fileInput.remove(),this.createFileInput(),this.bindListeners());Ext.ux.form.FileUploadField.superclass.reset.call(this)},getFileInputId:function(){return this.id+"-file"},onResize:function(a,b){Ext.ux.form.FileUploadField.superclass.onResize.call(this,a,b);this.wrap.setWidth(a);this.buttonOnly||(a=this.wrap.getWidth()-this.button.getEl().getWidth()-this.buttonOffset,this.el.setWidth(a))},onDestroy:function(){Ext.ux.form.FileUploadField.superclass.onDestroy.call(this); +Ext.destroy(this.fileInput,this.button,this.wrap)},onDisable:function(){Ext.ux.form.FileUploadField.superclass.onDisable.call(this);this.doDisable(!0)},onEnable:function(){Ext.ux.form.FileUploadField.superclass.onEnable.call(this);this.doDisable(!1)},doDisable:function(a){this.fileInput.dom.disabled=a;this.button.setDisabled(a)},preFocus:Ext.emptyFn,alignErrorIcon:function(){this.errorIcon.alignTo(this.wrap,"tl-tr",[2,0])}});Ext.reg("fileuploadfield",Ext.ux.form.FileUploadField); +Ext.form.FileUploadField=Ext.ux.form.FileUploadField; +Ext.override(Ext.form.RadioGroup,{afterRender:function(){this.items.each(function(a){this.relayEvents(a,["check"])},this);this.lazyValue&&(this.setValue(this.value),delete this.value,delete this.lazyValue);Ext.form.RadioGroup.superclass.afterRender.call(this)},getName:function(){return this.items.first().getName()},getValue:function(){return this.items.first().getGroupValue()},setValue:function(a){this.items.each?this.items.each(function(b){if(b.rendered){var c=b.el.getValue()==String(a);b.el.dom.checked= +c;b.el.dom.defaultChecked=c;b.wrap[c?"addClass":"removeClass"](b.checkedCls)}}):(this.value=a,this.lazyValue=!0)}});Ext.ns("Ext.ux.form"); +Ext.ux.form.SpinnerField=Ext.extend(Ext.form.NumberField,{actionMode:"wrap",deferHeight:!0,autoSize:Ext.emptyFn,onBlur:Ext.emptyFn,adjustSize:Ext.BoxComponent.prototype.adjustSize,constructor:function(a){var b=Ext.copyTo({},a,"incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass"),b=this.spinner=new Ext.ux.Spinner(b),b=a.plugins?Ext.isArray(a.plugins)?a.plugins.push(b):[a.plugins,b]:b;Ext.ux.form.SpinnerField.superclass.constructor.call(this,Ext.apply(a,{plugins:b}))}, +getResizeEl:function(){return this.wrap},getPositionEl:function(){return this.wrap},alignErrorIcon:function(){this.wrap&&this.errorIcon.alignTo(this.wrap,"tl-tr",[2,0])},validateBlur:function(){return!0}});Ext.reg("spinnerfield",Ext.ux.form.SpinnerField);Ext.form.SpinnerField=Ext.ux.form.SpinnerField;Ext.override(Ext.ux.form.SpinnerField,{onBlur:Ext.form.Field.prototype.onBlur});Ext.ns("Ext.ux.form"); +Ext.ux.form.SpinnerGroup=Ext.extend(Ext.form.CheckboxGroup,{defaultType:"spinnerfield",anchor:"98%",groupCls:"x-form-spinner-group",colCfg:{},onRender:function(a,b){if(!this.el){var c={cls:this.groupCls,layout:"column",border:!1,renderTo:a},d=Ext.apply({defaultType:this.defaultType,layout:"form",border:!1,labelWidth:60,defaults:{hideLabel:!0,anchor:"60%"}},this.colCfg);if(this.items[0].items){Ext.apply(c,{layoutConfig:{columns:this.items.length},defaults:this.defaults,items:this.items});for(var e= +0,g=this.items.length;e<g;e++)Ext.applyIf(this.items[e],d)}else{var f,h=[];"string"==typeof this.columns&&(this.columns=this.items.length);if(!Ext.isArray(this.columns)){f=[];for(e=0;e<this.columns;e++)f.push(0.01*(100/this.columns));this.columns=f}f=this.columns.length;for(e=0;e<f;e++)g=Ext.apply({items:[]},d),g[1>=this.columns[e]?"columnWidth":"width"]=this.columns[e],this.defaults&&(g.defaults=Ext.apply(g.defaults||{},this.defaults)),h.push(g);if(this.vertical)for(var d=Math.ceil(this.items.length/ +f),k=0,e=0,g=this.items.length;e<g;e++)0<e&&0==e%d&&k++,this.items[e].fieldLabel&&(this.items[e].hideLabel=!1),h[k].items.push(this.items[e]);else{e=0;for(g=this.items.length;e<g;e++)d=e%f,this.items[e].fieldLabel&&(this.items[e].hideLabel=!1),h[d].items.push(this.items[e])}Ext.apply(c,{layoutConfig:{columns:f},items:h})}this.panel=new Ext.Panel(c);this.el=this.panel.getEl();this.forId&&this.itemCls&&(c=this.el.up(this.itemCls).child("label",!0))&&c.setAttribute("htmlFor",this.forId);c=this.panel.findBy(function(a){return a.isFormField}, +this);this.items=new Ext.util.MixedCollection;this.items.addAll(c);this.items.each(function(a){a.on("spin",this.onFieldChange,this);a.on("change",this.onFieldChange,this)},this);this.lazyValueSet&&(this.setValue(this.value),delete this.value,delete this.lazyValueSet);this.lazyRawValueSet&&(this.setRawValue(this.rawValue),delete this.rawValue,delete this.lazyRawValueSet)}Ext.ux.form.SpinnerGroup.superclass.onRender.call(this,a,b)},onFieldChange:function(a){this.fireEvent("change",this,this.getValue())}, +initValue:Ext.emptyFn,getValue:function(){var a=[this.items.getCount()];this.items.each(function(b,c){a[c]=Number(b.getValue())});return a},getRawValue:function(){var a=[this.items.getCount()];this.items.each(function(b,c){a[c]=Number(b.getRawValue())});return a},setValue:function(a){this.rendered?this.items.each(function(b,c){b.setValue(a[c])}):(this.value=a,this.lazyValueSet=!0)},setRawValue:function(a){this.rendered?this.items.each(function(b,c){b.setRawValue(a[c])}):(this.rawValue=a,this.lazyRawValueSet= +!0)}});Ext.reg("spinnergroup",Ext.ux.form.SpinnerGroup);Ext.namespace("Ext.ux.form"); +Ext.ux.form.ToggleField=Ext.extend(Ext.form.Field,{cls:"x-toggle-field",initComponent:function(){Ext.ux.form.ToggleField.superclass.initComponent.call(this);this.toggle=new Ext.form.Checkbox;this.toggle.on("check",this.onToggleCheck,this);this.input=new Ext.form.TextField({disabled:!0})},onRender:function(a,b){this.el||(this.panel=new Ext.Panel({cls:this.groupCls,layout:"table",layoutConfig:{columns:2},border:!1,renderTo:a}),this.panel.ownerCt=this,this.el=this.panel.getEl(),this.panel.add(this.toggle), +this.panel.add(this.input),this.panel.doLayout(),this.toggle.getEl().parent().setStyle("padding-right","10px"));Ext.ux.form.ToggleField.superclass.onRender.call(this,a,b)},onResize:function(a,b){this.panel.setSize(a,b);this.panel.doLayout();var c=a-this.toggle.getSize().width-25;this.input.setSize(c,b)},onToggleCheck:function(a,b){this.input.setDisabled(!b)}});Ext.reg("togglefield",Ext.ux.form.ToggleField);Ext.ns("Ext.ux.grid"); +Ext.ux.grid.BufferView=Ext.extend(Ext.grid.GridView,{rowHeight:19,borderHeight:2,scrollDelay:100,cacheSize:20,cleanDelay:500,initTemplates:function(){Ext.ux.grid.BufferView.superclass.initTemplates.call(this);var a=this.templates;a.rowHolder=new Ext.Template('<div class="x-grid3-row {alt}" style="{tstyle}"></div>');a.rowHolder.disableFormats=!0;a.rowHolder.compile();a.rowBody=new Ext.Template('<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',"<tbody><tr>{cells}</tr>", +this.enableRowBody?'<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>':"","</tbody></table>");a.rowBody.disableFormats=!0;a.rowBody.compile()},getStyleRowHeight:function(){return Ext.isBorderBox?this.rowHeight+this.borderHeight:this.rowHeight},getCalculatedRowHeight:function(){return this.rowHeight+this.borderHeight},getVisibleRowCount:function(){var a=this.getCalculatedRowHeight(), +b=this.scroller.dom.clientHeight;return 1>b?0:Math.ceil(b/a)},getVisibleRows:function(){var a=this.getVisibleRowCount(),b=this.scroller.dom.scrollTop,b=0===b?0:Math.floor(b/this.getCalculatedRowHeight())-1;return{first:Math.max(b,0),last:Math.min(b+a+2,this.ds.getCount()-1)}},doRender:function(a,b,c,d,e,g,f){for(var h=this.templates,k=h.cell,l=h.row,t=h.rowBody,p=e-1,u=this.getStyleRowHeight(),y=this.getVisibleRows(),x=[],n,m={},q={tstyle:"width:"+this.getTotalWidth()+";height:"+u+"px;"},r,w=0,A= +b.length;w<A;w++){r=b[w];var u=[],v=w+d,z=v>=y.first&&v<=y.last;if(z)for(var s=0;s<e;s++){n=a[s];m.id=n.id;m.css=0===s?"x-grid3-cell-first ":s==p?"x-grid3-cell-last ":"";m.attr=m.cellAttr="";m.value=n.renderer(r.data[n.name],m,r,v,s,c);m.style=n.style;if(void 0===m.value||""===m.value)m.value=" ";r.dirty&&"undefined"!==typeof r.modified[n.name]&&(m.css+=" x-grid3-dirty-cell");u[u.length]=k.apply(m)}n=[];g&&0===(v+1)%2&&(n[0]="x-grid3-row-alt");r.dirty&&(n[1]=" x-grid3-dirty-row");q.cols=e;this.getRowClass&& +(n[2]=this.getRowClass(r,v,q,c));q.alt=n.join(" ");q.cells=u.join("");x[x.length]=!z?h.rowHolder.apply(q):f?t.apply(q):l.apply(q)}return x.join("")},isRowRendered:function(a){return(a=this.getRow(a))&&0<a.childNodes.length},syncScroll:function(){Ext.ux.grid.BufferView.superclass.syncScroll.apply(this,arguments);this.update()},update:function(){this.scrollDelay?(this.renderTask||(this.renderTask=new Ext.util.DelayedTask(this.doUpdate,this)),this.renderTask.delay(this.scrollDelay)):this.doUpdate()}, +onRemove:function(a,b,c,d){Ext.ux.grid.BufferView.superclass.onRemove.apply(this,arguments);!0!==d&&this.update()},doUpdate:function(){if(0<this.getVisibleRowCount()){for(var a=this.grid,b=a.colModel,c=a.store,d=this.getColumnData(),e=this.getVisibleRows(),g,f=e.first;f<=e.last;f++)if(!this.isRowRendered(f)&&(g=this.getRow(f))){var h=this.doRender(d,[c.getAt(f)],c,f,b.getColumnCount(),a.stripeRows,!0);g.innerHTML=h}this.clean()}},clean:function(){this.cleanTask||(this.cleanTask=new Ext.util.DelayedTask(this.doClean, +this));this.cleanTask.delay(this.cleanDelay)},doClean:function(){if(0<this.getVisibleRowCount()){var a=this.getVisibleRows();a.first-=this.cacheSize;a.last+=this.cacheSize;var b=0,c=this.getRows();0>=a.first&&(b=a.last+1);for(var d=this.ds.getCount();b<d;b++)if((b<a.first||b>a.last)&&c[b].innerHTML)c[b].innerHTML=""}},removeTask:function(a){var b=this[a];b&&b.cancel&&(b.cancel(),this[a]=null)},destroy:function(){this.removeTask("cleanTask");this.removeTask("renderTask");Ext.ux.grid.BufferView.superclass.destroy.call(this)}, +layout:function(){Ext.ux.grid.BufferView.superclass.layout.call(this);this.update()}}); +Ext.override(Ext.layout.FormLayout,{renderItem:function(a,b,c){if(a&&!a.rendered&&(a.isFormField||a.fieldLabel)&&"hidden"!=a.inputType){var d=this.getTemplateArgs(a);"number"==typeof b&&(b=c.dom.childNodes[b]||null);a.formItem=b?this.fieldTpl.insertBefore(b,d,!0):this.fieldTpl.append(c,d,!0);a.actionMode="formItem";a.render("x-form-el-"+a.id);a.container=a.formItem;a.actionMode="container"}else Ext.layout.FormLayout.superclass.renderItem.apply(this,arguments)}}); +Ext.override(Ext.tree.MultiSelectionModel,{onNodeClick:function(a,b){if(b.ctrlKey&&this.isSelected(a))this.unselect(a);else if(b.shiftKey&&!this.isSelected(a)){var c=a.parentNode;if(this.lastSelNode.parentNode.id==c.id){var d=c.indexOf(a),e=c.indexOf(this.lastSelNode);this.select(this.lastSelNode,b,!1,!0);d>e&&(d+=e,e=d-e,d-=e);c.eachChild(function(a){var f=c.indexOf(a);d<f&&f<e&&this.select(a,b,!0,!0)},this);this.select(a,b,!0)}}else this.select(a,b,b.ctrlKey)},select:function(a,b,c,d){!0!==c&&this.clearSelections(!0); +if(this.isSelected(a))return this.lastSelNode=a;this.selNodes.push(a);this.lastSelNode=this.selMap[a.id]=a;a.ui.onSelectedChange(!0);!0!==d&&this.fireEvent("selectionchange",this,this.selNodes);return a}});Ext.ns("Ext.ux.tree"); +Ext.ux.tree.TreeGrid=Ext.extend(Ext.tree.TreePanel,{rootVisible:!1,useArrows:!0,lines:!1,borderWidth:Ext.isBorderBox?0:2,cls:"x-treegrid",columnResize:!0,enableSort:!0,reserveScrollOffset:!0,enableHdMenu:!0,columnsText:"Columns",initComponent:function(){this.root||(this.root=new Ext.tree.AsyncTreeNode({text:"Root"}));var a=this.loader;a?Ext.isObject(a)&&!a.load&&(a=new Ext.ux.tree.TreeGridLoader(a)):a=new Ext.ux.tree.TreeGridLoader({dataUrl:this.dataUrl,requestMethod:this.requestMethod,store:this.store}); +this.loader=a;Ext.ux.tree.TreeGrid.superclass.initComponent.call(this);this.initColumns();this.enableSort&&(this.treeGridSorter=new Ext.ux.tree.TreeGridSorter(this,this.enableSort));this.columnResize&&(this.colResizer=new Ext.tree.ColumnResizer(this.columnResize),this.colResizer.init(this));this.internalTpl||(this.internalTpl=new Ext.XTemplate('<div class="x-grid3-header">','<div class="x-treegrid-header-inner">','<div class="x-grid3-header-offset">','<table style="table-layout: fixed;" cellspacing="0" cellpadding="0" border="0"><colgroup><tpl for="columns"><col /></tpl></colgroup>', +'<thead><tr class="x-grid3-hd-row">','<tpl for="columns">','<td class="x-grid3-hd x-grid3-cell x-treegrid-hd" style="text-align: {align};" id="',this.id,'-xlhd-{#}">','<div class="x-grid3-hd-inner x-treegrid-hd-inner" unselectable="on">',this.enableHdMenu?'<a class="x-grid3-hd-btn" href="#"></a>':"",'{header}<img class="x-grid3-sort-icon" src="',Ext.BLANK_IMAGE_URL,'" />',"</div>","</td></tpl>","</tr></thead>","</table>","</div></div>","</div>",'<div class="x-treegrid-root-node">','<table class="x-treegrid-root-table" cellpadding="0" cellspacing="0" style="table-layout: fixed;"></table>', +"</div>"));this.colgroupTpl||(this.colgroupTpl=new Ext.XTemplate('<colgroup><tpl for="columns"><col style="width: {width}px"/></tpl></colgroup>'))},initColumns:function(){var a=this.columns,b=a.length,c=[],d,e;for(d=0;d<b;d++)e=a[d],e.isColumn||(e.xtype=e.xtype?/^tg/.test(e.xtype)?e.xtype:"tg"+e.xtype:"tgcolumn",e=Ext.create(e)),e.init(this),c.push(e),!1!==this.enableSort&&!1!==e.sortable&&(this.enableSort=e.sortable=!0);this.columns=c},onRender:function(){Ext.tree.TreePanel.superclass.onRender.apply(this, +arguments);this.el.addClass("x-treegrid");this.outerCt=this.body.createChild({cls:"x-tree-root-ct x-treegrid-ct "+(this.useArrows?"x-tree-arrows":this.lines?"x-tree-lines":"x-tree-no-lines")});this.internalTpl.overwrite(this.outerCt,{columns:this.columns});this.mainHd=Ext.get(this.outerCt.dom.firstChild);this.innerHd=Ext.get(this.mainHd.dom.firstChild);this.innerBody=Ext.get(this.outerCt.dom.lastChild);this.innerCt=Ext.get(this.innerBody.dom.firstChild);this.colgroupTpl.insertFirst(this.innerCt,{columns:this.columns}); +this.hideHeaders?this.el.child(".x-grid3-header").setDisplayed("none"):!1!==this.enableHdMenu&&(this.hmenu=new Ext.menu.Menu({id:this.id+"-hctx"}),!1!==this.enableColumnHide&&(this.colMenu=new Ext.menu.Menu({id:this.id+"-hcols-menu"}),this.colMenu.on({scope:this,beforeshow:this.beforeColMenuShow,itemclick:this.handleHdMenuClick}),this.hmenu.add({itemId:"columns",hideOnClick:!1,text:this.columnsText,menu:this.colMenu,iconCls:"x-cols-icon"})),this.hmenu.on("itemclick",this.handleHdMenuClick,this))}, +setRootNode:function(a){a.attributes.uiProvider=Ext.ux.tree.TreeGridRootNodeUI;a=Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this,a);this.innerCt&&this.colgroupTpl.insertFirst(this.innerCt,{columns:this.columns});return a},clearInnerCt:function(){if(Ext.isIE)for(var a=this.innerCt.dom;a.firstChild;)a.removeChild(a.firstChild);else Ext.ux.tree.TreeGrid.superclass.clearInnerCt.call(this)},initEvents:function(){Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this,arguments);this.mon(this.innerBody, +"scroll",this.syncScroll,this);this.mon(this.innerHd,"click",this.handleHdDown,this);this.mon(this.mainHd,{scope:this,mouseover:this.handleHdOver,mouseout:this.handleHdOut})},onResize:function(a,b){Ext.ux.tree.TreeGrid.superclass.onResize.apply(this,arguments);var c=this.innerBody.dom,d=this.innerHd.dom;if(c&&(Ext.isNumber(b)&&(c.style.height=this.body.getHeight(!0)-d.offsetHeight+"px"),Ext.isNumber(a))){var e=Ext.num(this.scrollOffset,Ext.getScrollBarWidth());if(this.reserveScrollOffset||10<c.offsetWidth- +c.clientWidth)this.setScrollOffset(e);else{var g=this;setTimeout(function(){g.setScrollOffset(10<c.offsetWidth-c.clientWidth?e:0)},10)}}},updateColumnWidths:function(){var a=this.columns,b=a.length,c=this.outerCt.query("colgroup"),d=c.length,e,g,f,h;for(f=0;f<b;f++){e=a[f];for(h=0;h<d;h++)g=c[h],g.childNodes[f].style.width=(e.hidden?0:e.width)+"px"}f=0;c=this.innerHd.query("td");for(len=c.length;f<len;f++)e=Ext.fly(c[f]),a[f]&&a[f].hidden?e.addClass("x-treegrid-hd-hidden"):e.removeClass("x-treegrid-hd-hidden"); +a=this.getTotalColumnWidth();Ext.fly(this.innerHd.dom.firstChild).setWidth(a+(this.scrollOffset||0));this.outerCt.select("table").setWidth(a);this.syncHeaderScroll()},getVisibleColumns:function(){var a=[],b=this.columns,c=b.length,d;for(d=0;d<c;d++)b[d].hidden||a.push(b[d]);return a},getTotalColumnWidth:function(){for(var a=0,b=0,c=this.getVisibleColumns(),d=c.length;b<d;b++)a+=c[b].width;return a},setScrollOffset:function(a){this.scrollOffset=a;this.updateColumnWidths()},handleHdDown:function(a, +b){var c=a.getTarget(".x-treegrid-hd");if(c&&Ext.fly(b).hasClass("x-grid3-hd-btn")){var d=this.hmenu.items,e=this.columns,g=this.findHeaderIndex(c),e=e[g];a.stopEvent();Ext.fly(c).addClass("x-grid3-hd-menu-open");this.hdCtxIndex=g;this.fireEvent("headerbuttonclick",d,e,c,g);this.hmenu.on("hide",function(){Ext.fly(c).removeClass("x-grid3-hd-menu-open")},this,{single:!0});this.hmenu.show(b,"tl-bl?")}else c&&(g=this.findHeaderIndex(c),this.fireEvent("headerclick",this.columns[g],c,g))},handleHdOver:function(a, +b){var c=a.getTarget(".x-treegrid-hd");if(c&&!this.headersDisabled){index=this.findHeaderIndex(c);this.activeHdRef=b;this.activeHdIndex=index;var d=Ext.get(c);this.activeHdRegion=d.getRegion();d.addClass("x-grid3-hd-over");if(this.activeHdBtn=d.child(".x-grid3-hd-btn"))this.activeHdBtn.dom.style.height=c.firstChild.offsetHeight-1+"px"}},handleHdOut:function(a,b){var c=a.getTarget(".x-treegrid-hd");if(c&&(!Ext.isIE||!a.within(c,!0)))this.activeHdRef=null,Ext.fly(c).removeClass("x-grid3-hd-over"),c.style.cursor= +""},findHeaderIndex:function(a){a=a.dom||a;for(var b=a.parentNode.childNodes,c=0,d;d=b[c];c++)if(d==a)return c;return-1},beforeColMenuShow:function(){var a=this.columns,b=a.length,c,d;this.colMenu.removeAll();for(c=1;c<b;c++)d=a[c],!1!==d.hideable&&this.colMenu.add(new Ext.menu.CheckItem({itemId:"col-"+c,text:d.header,checked:!d.hidden,hideOnClick:!1,disabled:!1===d.hideable}))},handleHdMenuClick:function(a){var b=this.hdCtxIndex,c=a.getItemId();!1!==this.fireEvent("headermenuclick",this.columns[b], +c,b)&&(b=c.substr(4),0<b&&this.columns[b]&&this.setColumnVisible(b,!a.checked));return!0},setColumnVisible:function(a,b){this.columns[a].hidden=!b;this.updateColumnWidths()},scrollToTop:function(){this.innerBody.dom.scrollTop=0;this.innerBody.dom.scrollLeft=0},syncScroll:function(){this.syncHeaderScroll();var a=this.innerBody.dom;this.fireEvent("bodyscroll",a.scrollLeft,a.scrollTop)},syncHeaderScroll:function(){var a=this.innerBody.dom;this.innerHd.dom.scrollLeft=a.scrollLeft;this.innerHd.dom.scrollLeft= +a.scrollLeft},registerNode:function(a){Ext.ux.tree.TreeGrid.superclass.registerNode.call(this,a);!a.uiProvider&&(!a.isRoot&&!a.ui.isTreeGridNodeUI)&&(a.ui=new Ext.ux.tree.TreeGridNodeUI(a))}});Ext.reg("treegrid",Ext.ux.tree.TreeGrid); +Ext.tree.ColumnResizer=Ext.extend(Ext.util.Observable,{minWidth:14,constructor:function(a){Ext.apply(this,a);Ext.tree.ColumnResizer.superclass.constructor.call(this)},init:function(a){this.tree=a;a.on("render",this.initEvents,this)},initEvents:function(a){a.mon(a.innerHd,"mousemove",this.handleHdMove,this);this.tracker=new Ext.dd.DragTracker({onBeforeStart:this.onBeforeStart.createDelegate(this),onStart:this.onStart.createDelegate(this),onDrag:this.onDrag.createDelegate(this),onEnd:this.onEnd.createDelegate(this), +tolerance:3,autoStart:300});this.tracker.initEl(a.innerHd);a.on("beforedestroy",this.tracker.destroy,this.tracker)},handleHdMove:function(a,b){var c=a.getPageX(),d=a.getTarget(".x-treegrid-hd",3,!0);if(d){var e=d.getRegion(),g=d.dom.style,f=d.dom.parentNode;if(5>=c-e.left&&d.dom!==f.firstChild){for(c=d.dom.previousSibling;c&&Ext.fly(c).hasClass("x-treegrid-hd-hidden");)c=c.previousSibling;c&&(this.activeHd=Ext.get(c),g.cursor=Ext.isWebKit?"e-resize":"col-resize")}else if(5>=e.right-c){for(c=d.dom;c&& +Ext.fly(c).hasClass("x-treegrid-hd-hidden");)c=c.previousSibling;c&&(this.activeHd=Ext.get(c),g.cursor=Ext.isWebKit?"w-resize":"col-resize")}else delete this.activeHd,g.cursor=""}},onBeforeStart:function(a){this.dragHd=this.activeHd;return!!this.dragHd},onStart:function(a){this.dragHeadersDisabled=this.tree.headersDisabled;this.tree.headersDisabled=!0;this.proxy=this.tree.body.createChild({cls:"x-treegrid-resizer"});this.proxy.setHeight(this.tree.body.getHeight());a=this.tracker.getXY()[0];this.hdX= +this.dragHd.getX();this.hdIndex=this.tree.findHeaderIndex(this.dragHd);this.proxy.setX(this.hdX);this.proxy.setWidth(a-this.hdX);this.maxWidth=this.tree.outerCt.getWidth()-this.tree.innerBody.translatePoints(this.hdX).left},onDrag:function(a){a=this.tracker.getXY()[0];this.proxy.setWidth((a-this.hdX).constrain(this.minWidth,this.maxWidth))},onEnd:function(a){a=this.proxy.getWidth();var b=this.tree,c=this.dragHeadersDisabled;this.proxy.remove();delete this.dragHd;b.columns[this.hdIndex].width=a;b.updateColumnWidths(); +setTimeout(function(){b.headersDisabled=c},100)}}); +(function(){Ext.override(Ext.list.Column,{init:function(){var a=Ext.data.Types,b=this.sortType;this.type?Ext.isString(this.type)&&(this.type=Ext.data.Types[this.type.toUpperCase()]||a.AUTO):this.type=a.AUTO;Ext.isString(b)?this.sortType=Ext.data.SortTypes[b]:Ext.isEmpty(b)&&(this.sortType=this.type.sortType)}});Ext.tree.Column=Ext.extend(Ext.list.Column,{});Ext.tree.NumberColumn=Ext.extend(Ext.list.NumberColumn,{});Ext.tree.DateColumn=Ext.extend(Ext.list.DateColumn,{});Ext.tree.BooleanColumn=Ext.extend(Ext.list.BooleanColumn, +{});Ext.reg("tgcolumn",Ext.tree.Column);Ext.reg("tgnumbercolumn",Ext.tree.NumberColumn);Ext.reg("tgdatecolumn",Ext.tree.DateColumn);Ext.reg("tgbooleancolumn",Ext.tree.BooleanColumn)})();Ext.ux.tree.TreeGridLoader=Ext.extend(Ext.tree.TreeLoader,{createNode:function(a){a.uiProvider||(a.uiProvider=Ext.ux.tree.TreeGridNodeUI);return Ext.tree.TreeLoader.prototype.createNode.call(this,a)}}); +Ext.ux.tree.TreeGridNodeUI=Ext.extend(Ext.tree.TreeNodeUI,{isTreeGridNodeUI:!0,renderElements:function(a,b,c,d){var e=a.getOwnerTree(),g=e.columns,f=g[0],h,k,l;this.indentMarkup=a.parentNode?a.parentNode.ui.getChildIndent():"";k=['<tbody class="x-tree-node">','<tr ext:tree-node-id="',a.id,'" class="x-tree-node-el x-tree-node-leaf ',b.cls,'">','<td class="x-treegrid-col">','<span class="x-tree-node-indent">',this.indentMarkup,"</span>",'<img src="',this.emptyIcon,'" class="x-tree-ec-icon x-tree-elbow" />', +'<img src="',b.icon||this.emptyIcon,'" class="x-tree-node-icon',b.icon?" x-tree-node-inline-icon":"",b.iconCls?" "+b.iconCls:"",'" unselectable="on" />','<a hidefocus="on" class="x-tree-node-anchor" href="',b.href?b.href:"#",'" tabIndex="1" ',b.hrefTarget?' target="'+b.hrefTarget+'"':"",">",'<span unselectable="on">',f.tpl?f.tpl.apply(b):b[f.dataIndex]||f.text,"</span></a>","</td>"];h=1;for(l=g.length;h<l;h++)f=g[h],k.push('<td class="x-treegrid-col ',f.cls?f.cls:"",'">','<div unselectable="on" class="x-treegrid-text"', +f.align?' style="text-align: '+f.align+';"':"",">",f.tpl?f.tpl.apply(b):b[f.dataIndex],"</div>","</td>");k.push('</tr><tr class="x-tree-node-ct"><td colspan="',g.length,'">','<table class="x-treegrid-node-ct-table" cellpadding="0" cellspacing="0" style="table-layout: fixed; display: none; width: ',e.innerCt.getWidth(),'px;"><colgroup>');h=0;for(l=g.length;h<l;h++)k.push('<col style="width: ',g[h].hidden?0:g[h].width,'px;" />');k.push("</colgroup></table></td></tr></tbody>");!0!==d&&a.nextSibling&& +a.nextSibling.ui.getEl()?this.wrap=Ext.DomHelper.insertHtml("beforeBegin",a.nextSibling.ui.getEl(),k.join("")):this.wrap=Ext.DomHelper.insertHtml("beforeEnd",c,k.join(""));this.elNode=this.wrap.childNodes[0];this.ctNode=this.wrap.childNodes[1].firstChild.firstChild;a=this.elNode.firstChild.childNodes;this.indentNode=a[0];this.ecNode=a[1];this.iconNode=a[2];this.anchor=a[3];this.textNode=a[3].firstChild},animExpand:function(a){this.ctNode.style.display="";Ext.ux.tree.TreeGridNodeUI.superclass.animExpand.call(this, +a)}});Ext.ux.tree.TreeGridRootNodeUI=Ext.extend(Ext.tree.TreeNodeUI,{isTreeGridNodeUI:!0,render:function(){this.rendered||(this.wrap=this.ctNode=this.node.ownerTree.innerCt.dom,this.node.expanded=!0);if(Ext.isWebKit){var a=this.ctNode;a.style.tableLayout=null;(function(){a.style.tableLayout="fixed"}).defer(1)}},destroy:function(){this.elNode&&Ext.dd.Registry.unregister(this.elNode.id);delete this.node},collapse:Ext.emptyFn,expand:Ext.emptyFn}); +Ext.override(Ext.ux.tree.TreeGridNodeUI,{updateColumns:function(){if(this.rendered){var a=this.node.attributes,b=this.node.getOwnerTree().columns,c=b[0];this.anchor.firstChild.innerHTML=c.tpl?c.tpl.apply(a):a[c.dataIndex]||c.text;i=1;for(len=b.length;i<len;i++)c=b[i],this.elNode.childNodes[i].firstChild.innerHTML=c.tpl?c.tpl.apply(a):a[c.dataIndex]||c.text}}}); +Ext.tree.RenderColumn=Ext.extend(Ext.tree.Column,{constructor:function(a){a.tpl=a.tpl||new Ext.XTemplate("{"+a.dataIndex+":this.format}");a.tpl.format=a.renderer;a.tpl.col=this;Ext.tree.RenderColumn.superclass.constructor.call(this,a)}});Ext.reg("tgrendercolumn",Ext.tree.RenderColumn);Ext.ns("Ext.ux.tree"); +Ext.ux.tree.TreeGridSorter=Ext.extend(Ext.tree.TreeSorter,{sortClasses:["sort-asc","sort-desc"],sortAscText:"Sort Ascending",sortDescText:"Sort Descending",constructor:function(a,b){Ext.isObject(b)||(b={property:a.columns[0].dataIndex||"text",folderSort:!0});Ext.ux.tree.TreeGridSorter.superclass.constructor.apply(this,arguments);this.tree=a;a.on("headerclick",this.onHeaderClick,this);a.ddAppendOnly=!0;var c=this;this.defaultSortFn=function(a,b){var g=c.dir&&"desc"==c.dir.toLowerCase(),f=c.property|| +"text",h=c.sortType,k=!0===c.caseSensitive,l=c.leafAttr||"leaf",t=a.attributes,p=b.attributes;if(c.folderSort){if(t[l]&&!p[l])return 1;if(!t[l]&&p[l])return-1}l=t[f];f=p[f];p=h?h(l):k?l:l.toUpperCase();v2=h?h(f):k?f:f.toUpperCase();return p<v2?g?1:-1:p>v2?g?-1:1:0};a.on("afterrender",this.onAfterTreeRender,this,{single:!0});a.on("headermenuclick",this.onHeaderMenuClick,this)},onAfterTreeRender:function(){this.tree.hmenu&&this.tree.hmenu.insert(0,{itemId:"asc",text:this.sortAscText,cls:"xg-hmenu-sort-asc"}, +{itemId:"desc",text:this.sortDescText,cls:"xg-hmenu-sort-desc"});this.updateSortIcon(0,"asc")},onHeaderMenuClick:function(a,b,c){if("asc"===b||"desc"===b)return this.onHeaderClick(a,null,c),!1},onHeaderClick:function(a,b,c){if(a&&!this.tree.headersDisabled){var d=this;d.property=a.dataIndex;d.dir=a.dir="desc"===a.dir?"asc":"desc";d.sortType=a.sortType;d.caseSensitive===Ext.isBoolean(a.caseSensitive)?a.caseSensitive:this.caseSensitive;d.sortFn=a.sortFn||this.defaultSortFn;this.tree.root.cascade(function(a){a.isLeaf()|| +d.updateSort(d.tree,a)});this.updateSortIcon(c,a.dir)}},updateSortIcon:function(a,b){var c=this.sortClasses;this.tree.innerHd.select("td").removeClass(c).item(a).addClass(c["desc"==b?1:0])}}); +Ext.ux.JSLoader=function(a){Ext.ux.JSLoader.scripts[++Ext.ux.JSLoader.index]={url:a.url,success:!0,jsLoadObj:null,options:a,onLoad:a.onLoad||Ext.emptyFn,onError:a.onError||Ext.ux.JSLoader.stdError,scope:a.scope||this};Ext.Ajax.request({url:a.url,scriptIndex:Ext.ux.JSLoader.index,success:function(a,c){var d=Ext.ux.JSLoader.scripts[c.scriptIndex];try{eval(a.responseText)}catch(e){d.success=!1,d.onError(d.options,e)}d.success&&d.onLoad.call(d.scope,d.options)},failure:function(a,c){var d=Ext.ux.JSLoader.scripts[c.scriptIndex]; +d.success=!1;d.onError(d.options,a.status)}})};Ext.ux.JSLoader.index=0;Ext.ux.JSLoader.scripts=[];Ext.ux.JSLoader.stdError=function(a,b){window.alert("Error loading script:\n\n"+a.url+"\n\nstatus: "+b)}; +Ext.ux.Spinner=Ext.extend(Ext.util.Observable,{incrementValue:1,alternateIncrementValue:5,triggerClass:"x-form-spinner-trigger",splitterClass:"x-form-spinner-splitter",alternateKey:Ext.EventObject.shiftKey,defaultValue:0,accelerate:!1,constructor:function(a){Ext.ux.Spinner.superclass.constructor.call(this,a);Ext.apply(this,a);this.mimicing=!1},init:function(a){this.field=a;a.afterMethod("onRender",this.doRender,this);a.afterMethod("onEnable",this.doEnable,this);a.afterMethod("onDisable",this.doDisable, +this);a.afterMethod("afterRender",this.doAfterRender,this);a.afterMethod("onResize",this.doResize,this);a.afterMethod("onFocus",this.doFocus,this);a.beforeMethod("onDestroy",this.doDestroy,this)},doRender:function(a,b){var c=this.el=this.field.getEl(),d=this.field;d.wrap?this.wrap=d.wrap.addClass("x-form-field-wrap"):d.wrap=this.wrap=c.wrap({cls:"x-form-field-wrap"});this.trigger=this.wrap.createChild({tag:"img",src:Ext.BLANK_IMAGE_URL,cls:"x-form-trigger "+this.triggerClass});d.width||this.wrap.setWidth(c.getWidth()+ +this.trigger.getWidth());this.splitter=this.wrap.createChild({tag:"div",cls:this.splitterClass,style:"width:13px; height:2px;"});this.splitter.setRight(Ext.isIE?1:2).setTop(10).show();this.proxy=this.trigger.createProxy("",this.splitter,!0);this.proxy.addClass("x-form-spinner-proxy");this.proxy.setStyle("left","0px");this.proxy.setSize(14,1);this.proxy.hide();this.dd=new Ext.dd.DDProxy(this.splitter.dom.id,"SpinnerDrag",{dragElId:this.proxy.id});this.initTrigger();this.initSpinner()},doAfterRender:function(){var a; +if(Ext.isIE&&this.el.getY()!=(a=this.trigger.getY()))this.el.position(),this.el.setY(a)},doEnable:function(){this.wrap&&(this.disabled=!1,this.wrap.removeClass(this.field.disabledClass))},doDisable:function(){this.wrap&&(this.disabled=!0,this.wrap.addClass(this.field.disabledClass),this.el.removeClass(this.field.disabledClass))},doResize:function(a,b){"number"==typeof a&&this.el.setWidth(a-this.trigger.getWidth());this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth())},doFocus:function(){this.mimicing|| +(this.wrap.addClass("x-trigger-wrap-focus"),this.mimicing=!0,Ext.get(Ext.isIE?document.body:document).on("mousedown",this.mimicBlur,this,{delay:10}),this.el.on("keydown",this.checkTab,this))},checkTab:function(a){a.getKey()==a.TAB&&this.triggerBlur()},mimicBlur:function(a){!this.wrap.contains(a.target)&&this.field.validateBlur(a)&&this.triggerBlur()},triggerBlur:function(){this.mimicing=!1;Ext.get(Ext.isIE?document.body:document).un("mousedown",this.mimicBlur,this);this.el.un("keydown",this.checkTab, +this);this.field.beforeBlur();this.wrap.removeClass("x-trigger-wrap-focus");this.field.onBlur.call(this.field)},initTrigger:function(){this.trigger.addClassOnOver("x-form-trigger-over");this.trigger.addClassOnClick("x-form-trigger-click")},initSpinner:function(){this.field.addEvents({spin:!0,spinup:!0,spindown:!0});this.keyNav=new Ext.KeyNav(this.el,{up:function(a){a.preventDefault();this.onSpinUp()},down:function(a){a.preventDefault();this.onSpinDown()},pageUp:function(a){a.preventDefault();this.onSpinUpAlternate()}, +pageDown:function(a){a.preventDefault();this.onSpinDownAlternate()},scope:this});this.repeater=new Ext.util.ClickRepeater(this.trigger,{accelerate:this.accelerate});this.field.mon(this.repeater,"click",this.onTriggerClick,this,{preventDefault:!0});this.field.mon(this.trigger,{mouseover:this.onMouseOver,mouseout:this.onMouseOut,mousemove:this.onMouseMove,mousedown:this.onMouseDown,mouseup:this.onMouseUp,scope:this,preventDefault:!0});this.field.mon(this.wrap,"mousewheel",this.handleMouseWheel,this); +this.dd.setXConstraint(0,0,10);this.dd.setYConstraint(1500,1500,10);this.dd.endDrag=this.endDrag.createDelegate(this);this.dd.startDrag=this.startDrag.createDelegate(this);this.dd.onDrag=this.onDrag.createDelegate(this)},onMouseOver:function(){if(!this.disabled){var a=this.getMiddle();this.tmpHoverClass=Ext.EventObject.getPageY()<a?"x-form-spinner-overup":"x-form-spinner-overdown";this.trigger.addClass(this.tmpHoverClass)}},onMouseOut:function(){this.trigger.removeClass(this.tmpHoverClass)},onMouseMove:function(){if(!this.disabled){var a= +this.getMiddle();Ext.EventObject.getPageY()>a&&"x-form-spinner-overup"==this.tmpHoverClass||Ext.EventObject.getPageY()}},onMouseDown:function(){if(!this.disabled){var a=this.getMiddle();this.tmpClickClass=Ext.EventObject.getPageY()<a?"x-form-spinner-clickup":"x-form-spinner-clickdown";this.trigger.addClass(this.tmpClickClass)}},onMouseUp:function(){this.trigger.removeClass(this.tmpClickClass)},onTriggerClick:function(){if(!this.disabled&&!this.el.dom.readOnly){var a=this.getMiddle();this["onSpin"+ +(Ext.EventObject.getPageY()<a?"Up":"Down")]()}},getMiddle:function(){var a=this.trigger.getTop(),b=this.trigger.getHeight();return a+b/2},isSpinnable:function(){return this.disabled||this.el.dom.readOnly?(Ext.EventObject.preventDefault(),!1):!0},handleMouseWheel:function(a){if(!1!=this.wrap.hasClass("x-trigger-wrap-focus")){var b=a.getWheelDelta();0<b?(this.onSpinUp(),a.stopEvent()):0>b&&(this.onSpinDown(),a.stopEvent())}},startDrag:function(){this.proxy.show();this._previousY=Ext.fly(this.dd.getDragEl()).getTop()}, +endDrag:function(){this.proxy.hide()},onDrag:function(){if(!this.disabled){var a=Ext.fly(this.dd.getDragEl()).getTop(),b="";this._previousY>a&&(b="Up");this._previousY<a&&(b="Down");if(""!=b)this["onSpin"+b]();this._previousY=a}},onSpinUp:function(){if(!1!=this.isSpinnable())if(!0==Ext.EventObject.shiftKey)this.onSpinUpAlternate();else this.spin(!1,!1),this.field.fireEvent("spin",this),this.field.fireEvent("spinup",this)},onSpinDown:function(){if(!1!=this.isSpinnable())if(!0==Ext.EventObject.shiftKey)this.onSpinDownAlternate(); +else this.spin(!0,!1),this.field.fireEvent("spin",this),this.field.fireEvent("spindown",this)},onSpinUpAlternate:function(){!1!=this.isSpinnable()&&(this.spin(!1,!0),this.field.fireEvent("spin",this),this.field.fireEvent("spinup",this))},onSpinDownAlternate:function(){!1!=this.isSpinnable()&&(this.spin(!0,!0),this.field.fireEvent("spin",this),this.field.fireEvent("spindown",this))},spin:function(a,b){var c=parseFloat(this.field.getValue()),d=!0==b?this.alternateIncrementValue:this.incrementValue; +!0==a?c-=d:c+=d;c=isNaN(c)?this.defaultValue:c;c=this.fixBoundries(c);this.field.setRawValue(c)},fixBoundries:function(a){void 0!=this.field.minValue&&a<this.field.minValue&&(a=this.field.minValue);void 0!=this.field.maxValue&&a>this.field.maxValue&&(a=this.field.maxValue);return this.fixPrecision(a)},fixPrecision:function(a){var b=isNaN(a);return!this.field.allowDecimals||-1==this.field.decimalPrecision||b||!a?b?"":a:parseFloat(parseFloat(a).toFixed(this.field.decimalPrecision))},doDestroy:function(){this.trigger&& +this.trigger.remove();this.wrap&&(this.wrap.remove(),delete this.field.wrap);this.splitter&&this.splitter.remove();this.dd&&(this.dd.unreg(),this.dd=null);this.proxy&&this.proxy.remove();this.repeater&&this.repeater.purgeListeners();this.mimicing&&Ext.get(Ext.isIE?document.body:document).un("mousedown",this.mimicBlur,this)}});Ext.form.Spinner=Ext.ux.Spinner; +Ext.ux.StatusBar=Ext.extend(Ext.Toolbar,{cls:"x-statusbar",busyIconCls:"x-status-busy",busyText:"Loading...",autoClear:5E3,emptyText:" ",activeThreadId:0,initComponent:function(){"right"==this.statusAlign&&(this.cls+=" x-status-right");Ext.ux.StatusBar.superclass.initComponent.call(this)},afterRender:function(){Ext.ux.StatusBar.superclass.afterRender.call(this);var a="right"==this.statusAlign;this.currIconCls=this.iconCls||this.defaultIconCls;this.statusEl=new Ext.Toolbar.TextItem({cls:"x-status-text "+ +(this.currIconCls||""),text:this.text||this.defaultText||""});a?(this.add("->"),this.add(this.statusEl)):(this.insert(0,this.statusEl),this.insert(1,"->"));this.doLayout()},setStatus:function(a){a=a||{};"string"==typeof a&&(a={text:a});void 0!==a.text&&this.setText(a.text);void 0!==a.iconCls&&this.setIcon(a.iconCls);if(a.clear){a=a.clear;var b=this.autoClear,c={useDefaults:!0,anim:!0};"object"==typeof a?(a=Ext.applyIf(a,c),a.wait&&(b=a.wait)):"number"==typeof a?(b=a,a=c):"boolean"==typeof a&&(a=c); +a.threadId=this.activeThreadId;this.clearStatus.defer(b,this,[a])}return this},clearStatus:function(a){a=a||{};if(a.threadId&&a.threadId!==this.activeThreadId)return this;var b=a.useDefaults?this.defaultText:this.emptyText,c=a.useDefaults?this.defaultIconCls?this.defaultIconCls:"":"";a.anim?this.statusEl.el.fadeOut({remove:!1,useDisplay:!0,scope:this,callback:function(){this.setStatus({text:b,iconCls:c});this.statusEl.el.show()}}):(this.statusEl.hide(),this.setStatus({text:b,iconCls:c}),this.statusEl.show()); +return this},setText:function(a){this.activeThreadId++;this.text=a||"";this.rendered&&this.statusEl.setText(this.text);return this},getText:function(){return this.text},setIcon:function(a){this.activeThreadId++;a=a||"";this.rendered?(this.currIconCls&&(this.statusEl.removeClass(this.currIconCls),this.currIconCls=null),0<a.length&&(this.statusEl.addClass(a),this.currIconCls=a)):this.currIconCls=a;return this},showBusy:function(a){"string"==typeof a&&(a={text:a});a=Ext.applyIf(a||{},{text:this.busyText, +iconCls:this.busyIconCls});return this.setStatus(a)}});Ext.reg("statusbar",Ext.ux.StatusBar); diff --git a/deluge/ui/web/js/extjs/ext-extensions/JSLoader.js b/deluge/ui/web/js/extjs/ext-extensions/JSLoader.js index 9631fd8..e4691b0 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/JSLoader.js +++ b/deluge/ui/web/js/extjs/ext-extensions/JSLoader.js @@ -1,4 +1,4 @@ -Ext.ux.JSLoader = function(options) { +Ext.ux.JSLoader = function (options) { Ext.ux.JSLoader.scripts[++Ext.ux.JSLoader.index] = { url: options.url, success: true, @@ -12,7 +12,7 @@ Ext.ux.JSLoader = function(options) { Ext.Ajax.request({ url: options.url, scriptIndex: Ext.ux.JSLoader.index, - success: function(response, options) { + success: function (response, options) { var script = Ext.ux.JSLoader.scripts[options.scriptIndex]; try { eval(response.responseText); @@ -24,7 +24,7 @@ Ext.ux.JSLoader = function(options) { script.onLoad.call(script.scope, script.options); } }, - failure: function(response, options) { + failure: function (response, options) { var script = Ext.ux.JSLoader.scripts[options.scriptIndex]; script.success = false; script.onError(script.options, response.status); @@ -33,7 +33,7 @@ Ext.ux.JSLoader = function(options) { }; Ext.ux.JSLoader.index = 0; Ext.ux.JSLoader.scripts = []; -Ext.ux.JSLoader.stdError = function(options, e) { +Ext.ux.JSLoader.stdError = function (options, e) { window.alert( 'Error loading script:\n\n' + options.url + '\n\nstatus: ' + e ); diff --git a/deluge/ui/web/js/extjs/ext-extensions/Spinner.js b/deluge/ui/web/js/extjs/ext-extensions/Spinner.js index ff272d2..ccef895 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/Spinner.js +++ b/deluge/ui/web/js/extjs/ext-extensions/Spinner.js @@ -18,13 +18,13 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { defaultValue: 0, accelerate: false, - constructor: function(config) { + constructor: function (config) { Ext.ux.Spinner.superclass.constructor.call(this, config); Ext.apply(this, config); this.mimicing = false; }, - init: function(field) { + init: function (field) { this.field = field; field.afterMethod('onRender', this.doRender, this); @@ -36,7 +36,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { field.beforeMethod('onDestroy', this.doDestroy, this); }, - doRender: function(ct, position) { + doRender: function (ct, position) { var el = (this.el = this.field.getEl()); var f = this.field; @@ -81,7 +81,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.initSpinner(); }, - doAfterRender: function() { + doAfterRender: function () { var y; if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) { this.el.position(); @@ -89,14 +89,14 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { } }, - doEnable: function() { + doEnable: function () { if (this.wrap) { this.disabled = false; this.wrap.removeClass(this.field.disabledClass); } }, - doDisable: function() { + doDisable: function () { if (this.wrap) { this.disabled = true; this.wrap.addClass(this.field.disabledClass); @@ -104,14 +104,14 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { } }, - doResize: function(w, h) { + doResize: function (w, h) { if (typeof w == 'number') { this.el.setWidth(w - this.trigger.getWidth()); } this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth()); }, - doFocus: function() { + doFocus: function () { if (!this.mimicing) { this.wrap.addClass('x-trigger-wrap-focus'); this.mimicing = true; @@ -128,21 +128,21 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, // private - checkTab: function(e) { + checkTab: function (e) { if (e.getKey() == e.TAB) { this.triggerBlur(); } }, // private - mimicBlur: function(e) { + mimicBlur: function (e) { if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) { this.triggerBlur(); } }, // private - triggerBlur: function() { + triggerBlur: function () { this.mimicing = false; Ext.get(Ext.isIE ? document.body : document).un( 'mousedown', @@ -155,12 +155,12 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.field.onBlur.call(this.field); }, - initTrigger: function() { + initTrigger: function () { this.trigger.addClassOnOver('x-form-trigger-over'); this.trigger.addClassOnClick('x-form-trigger-click'); }, - initSpinner: function() { + initSpinner: function () { this.field.addEvents({ spin: true, spinup: true, @@ -168,22 +168,22 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }); this.keyNav = new Ext.KeyNav(this.el, { - up: function(e) { + up: function (e) { e.preventDefault(); this.onSpinUp(); }, - down: function(e) { + down: function (e) { e.preventDefault(); this.onSpinDown(); }, - pageUp: function(e) { + pageUp: function (e) { e.preventDefault(); this.onSpinUpAlternate(); }, - pageDown: function(e) { + pageDown: function (e) { e.preventDefault(); this.onSpinDownAlternate(); }, @@ -217,7 +217,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.dd.onDrag = this.onDrag.createDelegate(this); }, - onMouseOver: function() { + onMouseOver: function () { if (this.disabled) { return; } @@ -230,12 +230,12 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onMouseOut: function() { + onMouseOut: function () { this.trigger.removeClass(this.tmpHoverClass); }, //private - onMouseMove: function() { + onMouseMove: function () { if (this.disabled) { return; } @@ -250,7 +250,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onMouseDown: function() { + onMouseDown: function () { if (this.disabled) { return; } @@ -263,12 +263,12 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onMouseUp: function() { + onMouseUp: function () { this.trigger.removeClass(this.tmpClickClass); }, //private - onTriggerClick: function() { + onTriggerClick: function () { if (this.disabled || this.el.dom.readOnly) { return; } @@ -278,7 +278,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - getMiddle: function() { + getMiddle: function () { var t = this.trigger.getTop(); var h = this.trigger.getHeight(); var middle = t + h / 2; @@ -287,7 +287,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { //private //checks if control is allowed to spin - isSpinnable: function() { + isSpinnable: function () { if (this.disabled || this.el.dom.readOnly) { Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly return false; @@ -295,7 +295,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { return true; }, - handleMouseWheel: function(e) { + handleMouseWheel: function (e) { //disable scrolling when not focused if (this.wrap.hasClass('x-trigger-wrap-focus') == false) { return; @@ -312,18 +312,18 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - startDrag: function() { + startDrag: function () { this.proxy.show(); this._previousY = Ext.fly(this.dd.getDragEl()).getTop(); }, //private - endDrag: function() { + endDrag: function () { this.proxy.hide(); }, //private - onDrag: function() { + onDrag: function () { if (this.disabled) { return; } @@ -344,7 +344,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onSpinUp: function() { + onSpinUp: function () { if (this.isSpinnable() == false) { return; } @@ -359,7 +359,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onSpinDown: function() { + onSpinDown: function () { if (this.isSpinnable() == false) { return; } @@ -374,7 +374,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onSpinUpAlternate: function() { + onSpinUpAlternate: function () { if (this.isSpinnable() == false) { return; } @@ -384,7 +384,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, //private - onSpinDownAlternate: function() { + onSpinDownAlternate: function () { if (this.isSpinnable() == false) { return; } @@ -393,7 +393,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.field.fireEvent('spindown', this); }, - spin: function(down, alternate) { + spin: function (down, alternate) { var v = parseFloat(this.field.getValue()); var incr = alternate == true @@ -406,7 +406,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { this.field.setRawValue(v); }, - fixBoundries: function(value) { + fixBoundries: function (value) { var v = value; if (this.field.minValue != undefined && v < this.field.minValue) { @@ -420,7 +420,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { }, // private - fixPrecision: function(value) { + fixPrecision: function (value) { var nan = isNaN(value); if ( !this.field.allowDecimals || @@ -435,7 +435,7 @@ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { ); }, - doDestroy: function() { + doDestroy: function () { if (this.trigger) { this.trigger.remove(); } diff --git a/deluge/ui/web/js/extjs/ext-extensions/StatusBar.js b/deluge/ui/web/js/extjs/ext-extensions/StatusBar.js index a12b8f9..1f6a5f0 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/StatusBar.js +++ b/deluge/ui/web/js/extjs/ext-extensions/StatusBar.js @@ -183,7 +183,7 @@ sb.setStatus({ activeThreadId: 0, // private - initComponent: function() { + initComponent: function () { if (this.statusAlign == 'right') { this.cls += ' x-status-right'; } @@ -191,7 +191,7 @@ sb.setStatus({ }, // private - afterRender: function() { + afterRender: function () { Ext.ux.StatusBar.superclass.afterRender.call(this); var right = this.statusAlign == 'right'; @@ -260,7 +260,7 @@ statusBar.setStatus({ </code></pre> * @return {Ext.ux.StatusBar} this */ - setStatus: function(o) { + setStatus: function (o) { o = o || {}; if (typeof o == 'string') { @@ -307,7 +307,7 @@ statusBar.setStatus({ * </ul> * @return {Ext.ux.StatusBar} this */ - clearStatus: function(o) { + clearStatus: function (o) { o = o || {}; if (o.threadId && o.threadId !== this.activeThreadId) { @@ -330,7 +330,7 @@ statusBar.setStatus({ remove: false, useDisplay: true, scope: this, - callback: function() { + callback: function () { this.setStatus({ text: text, iconCls: iconCls, @@ -356,7 +356,7 @@ statusBar.setStatus({ * @param {String} text (optional) The text to set (defaults to '') * @return {Ext.ux.StatusBar} this */ - setText: function(text) { + setText: function (text) { this.activeThreadId++; this.text = text || ''; if (this.rendered) { @@ -369,7 +369,7 @@ statusBar.setStatus({ * Returns the current status text. * @return {String} The status text */ - getText: function() { + getText: function () { return this.text; }, @@ -379,7 +379,7 @@ statusBar.setStatus({ * @param {String} iconCls (optional) The icon class to set (defaults to '', and any current icon class is removed) * @return {Ext.ux.StatusBar} this */ - setIcon: function(cls) { + setIcon: function (cls) { this.activeThreadId++; cls = cls || ''; @@ -408,7 +408,7 @@ statusBar.setStatus({ * {@link #busyIconCls} will be used in conjunction with all of the default options for {@link #setStatus}. * @return {Ext.ux.StatusBar} this */ - showBusy: function(o) { + showBusy: function (o) { if (typeof o == 'string') { o = { text: o }; } diff --git a/deluge/ui/web/js/extjs/ext-extensions/form/FileUploadField.js b/deluge/ui/web/js/extjs/ext-extensions/form/FileUploadField.js index ca15073..3ab2347 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/form/FileUploadField.js +++ b/deluge/ui/web/js/extjs/ext-extensions/form/FileUploadField.js @@ -50,7 +50,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { autoSize: Ext.emptyFn, // private - initComponent: function() { + initComponent: function () { Ext.ux.form.FileUploadField.superclass.initComponent.call(this); this.addEvents( @@ -66,7 +66,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { }, // private - onRender: function(ct, position) { + onRender: function (ct, position) { Ext.ux.form.FileUploadField.superclass.onRender.call( this, ct, @@ -97,30 +97,30 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { this.resizeEl = this.positionEl = this.wrap; }, - bindListeners: function() { + bindListeners: function () { this.fileInput.on({ scope: this, - mouseenter: function() { + mouseenter: function () { this.button.addClass(['x-btn-over', 'x-btn-focus']); }, - mouseleave: function() { + mouseleave: function () { this.button.removeClass([ 'x-btn-over', 'x-btn-focus', 'x-btn-click', ]); }, - mousedown: function() { + mousedown: function () { this.button.addClass('x-btn-click'); }, - mouseup: function() { + mouseup: function () { this.button.removeClass([ 'x-btn-over', 'x-btn-focus', 'x-btn-click', ]); }, - change: function() { + change: function () { var value = this.fileInput.dom.files; // Fallback to value. if (!value) value = this.fileInput.dom.value; @@ -130,7 +130,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { }); }, - createFileInput: function() { + createFileInput: function () { this.fileInput = this.wrap.createChild({ id: this.getFileInputId(), name: this.name || this.getId(), @@ -142,7 +142,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { this.fileInput.dom.multiple = this.multiple; }, - reset: function() { + reset: function () { if (this.rendered) { this.fileInput.remove(); this.createFileInput(); @@ -152,12 +152,12 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { }, // private - getFileInputId: function() { + getFileInputId: function () { return this.id + '-file'; }, // private - onResize: function(w, h) { + onResize: function (w, h) { Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h); this.wrap.setWidth(w); @@ -172,23 +172,23 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { }, // private - onDestroy: function() { + onDestroy: function () { Ext.ux.form.FileUploadField.superclass.onDestroy.call(this); Ext.destroy(this.fileInput, this.button, this.wrap); }, - onDisable: function() { + onDisable: function () { Ext.ux.form.FileUploadField.superclass.onDisable.call(this); this.doDisable(true); }, - onEnable: function() { + onEnable: function () { Ext.ux.form.FileUploadField.superclass.onEnable.call(this); this.doDisable(false); }, // private - doDisable: function(disabled) { + doDisable: function (disabled) { this.fileInput.dom.disabled = disabled; this.button.setDisabled(disabled); }, @@ -197,7 +197,7 @@ Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { preFocus: Ext.emptyFn, // private - alignErrorIcon: function() { + alignErrorIcon: function () { this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); }, }); diff --git a/deluge/ui/web/js/extjs/ext-extensions/form/RadioGroupFix.js b/deluge/ui/web/js/extjs/ext-extensions/form/RadioGroupFix.js index 134e7a1..416c098 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/form/RadioGroupFix.js +++ b/deluge/ui/web/js/extjs/ext-extensions/form/RadioGroupFix.js @@ -10,8 +10,8 @@ // Allow radiogroups to be treated as a single form element. Ext.override(Ext.form.RadioGroup, { - afterRender: function() { - this.items.each(function(i) { + afterRender: function () { + this.items.each(function (i) { this.relayEvents(i, ['check']); }, this); if (this.lazyValue) { @@ -22,21 +22,21 @@ Ext.override(Ext.form.RadioGroup, { Ext.form.RadioGroup.superclass.afterRender.call(this); }, - getName: function() { + getName: function () { return this.items.first().getName(); }, - getValue: function() { + getValue: function () { return this.items.first().getGroupValue(); }, - setValue: function(v) { + setValue: function (v) { if (!this.items.each) { this.value = v; this.lazyValue = true; return; } - this.items.each(function(item) { + this.items.each(function (item) { if (item.rendered) { var checked = item.el.getValue() == String(v); item.el.dom.checked = checked; diff --git a/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerField.js b/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerField.js index d14f320..890fb5b 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerField.js +++ b/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerField.js @@ -19,7 +19,7 @@ Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, { onBlur: Ext.emptyFn, adjustSize: Ext.BoxComponent.prototype.adjustSize, - constructor: function(config) { + constructor: function (config) { var spinnerConfig = Ext.copyTo( {}, config, @@ -41,23 +41,23 @@ Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, { }, // private - getResizeEl: function() { + getResizeEl: function () { return this.wrap; }, // private - getPositionEl: function() { + getPositionEl: function () { return this.wrap; }, // private - alignErrorIcon: function() { + alignErrorIcon: function () { if (this.wrap) { this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); } }, - validateBlur: function() { + validateBlur: function () { return true; }, }); diff --git a/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js b/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js index eafc4e1..ee761aa 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js +++ b/deluge/ui/web/js/extjs/ext-extensions/form/SpinnerGroup.js @@ -23,7 +23,7 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { colCfg: {}, // private - onRender: function(ct, position) { + onRender: function (ct, position) { if (!this.el) { var panelCfg = { cls: this.groupCls, @@ -80,9 +80,8 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { // Generate the column configs with the correct width setting for (var i = 0; i < numCols; i++) { var cc = Ext.apply({ items: [] }, colCfg); - cc[ - this.columns[i] <= 1 ? 'columnWidth' : 'width' - ] = this.columns[i]; + cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = + this.columns[i]; if (this.defaults) { cc.defaults = Ext.apply( cc.defaults || {}, @@ -131,14 +130,14 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { } } - var fields = this.panel.findBy(function(c) { + var fields = this.panel.findBy(function (c) { return c.isFormField; }, this); this.items = new Ext.util.MixedCollection(); this.items.addAll(fields); - this.items.each(function(field) { + this.items.each(function (field) { field.on('spin', this.onFieldChange, this); field.on('change', this.onFieldChange, this); }, this); @@ -159,45 +158,45 @@ Ext.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { Ext.ux.form.SpinnerGroup.superclass.onRender.call(this, ct, position); }, - onFieldChange: function(spinner) { + onFieldChange: function (spinner) { this.fireEvent('change', this, this.getValue()); }, initValue: Ext.emptyFn, - getValue: function() { + getValue: function () { var value = [this.items.getCount()]; - this.items.each(function(item, i) { + this.items.each(function (item, i) { value[i] = Number(item.getValue()); }); return value; }, - getRawValue: function() { + getRawValue: function () { var value = [this.items.getCount()]; - this.items.each(function(item, i) { + this.items.each(function (item, i) { value[i] = Number(item.getRawValue()); }); return value; }, - setValue: function(value) { + setValue: function (value) { if (!this.rendered) { this.value = value; this.lazyValueSet = true; } else { - this.items.each(function(item, i) { + this.items.each(function (item, i) { item.setValue(value[i]); }); } }, - setRawValue: function(value) { + setRawValue: function (value) { if (!this.rendered) { this.rawValue = value; this.lazyRawValueSet = true; } else { - this.items.each(function(item, i) { + this.items.each(function (item, i) { item.setRawValue(value[i]); }); } diff --git a/deluge/ui/web/js/extjs/ext-extensions/form/ToggleField.js b/deluge/ui/web/js/extjs/ext-extensions/form/ToggleField.js index 27eebf3..eb60c3b 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/form/ToggleField.js +++ b/deluge/ui/web/js/extjs/ext-extensions/form/ToggleField.js @@ -21,7 +21,7 @@ Ext.namespace('Ext.ux.form'); Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { cls: 'x-toggle-field', - initComponent: function() { + initComponent: function () { Ext.ux.form.ToggleField.superclass.initComponent.call(this); this.toggle = new Ext.form.Checkbox(); @@ -32,7 +32,7 @@ Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { }); }, - onRender: function(ct, position) { + onRender: function (ct, position) { if (!this.el) { this.panel = new Ext.Panel({ cls: this.groupCls, @@ -50,16 +50,13 @@ Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { this.panel.add(this.input); this.panel.doLayout(); - this.toggle - .getEl() - .parent() - .setStyle('padding-right', '10px'); + this.toggle.getEl().parent().setStyle('padding-right', '10px'); } Ext.ux.form.ToggleField.superclass.onRender.call(this, ct, position); }, // private - onResize: function(w, h) { + onResize: function (w, h) { this.panel.setSize(w, h); this.panel.doLayout(); @@ -68,7 +65,7 @@ Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { this.input.setSize(inputWidth, h); }, - onToggleCheck: function(toggle, checked) { + onToggleCheck: function (toggle, checked) { this.input.setDisabled(!checked); }, }); diff --git a/deluge/ui/web/js/extjs/ext-extensions/grid/BufferView.js b/deluge/ui/web/js/extjs/ext-extensions/grid/BufferView.js index e9f0e0c..0fce3b4 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/grid/BufferView.js +++ b/deluge/ui/web/js/extjs/ext-extensions/grid/BufferView.js @@ -46,7 +46,7 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { */ cleanDelay: 500, - initTemplates: function() { + initTemplates: function () { Ext.ux.grid.BufferView.superclass.initTemplates.call(this); var ts = this.templates; // empty div to act as a place holder for a row @@ -68,23 +68,23 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { ts.rowBody.compile(); }, - getStyleRowHeight: function() { + getStyleRowHeight: function () { return Ext.isBorderBox ? this.rowHeight + this.borderHeight : this.rowHeight; }, - getCalculatedRowHeight: function() { + getCalculatedRowHeight: function () { return this.rowHeight + this.borderHeight; }, - getVisibleRowCount: function() { + getVisibleRowCount: function () { var rh = this.getCalculatedRowHeight(), visibleHeight = this.scroller.dom.clientHeight; return visibleHeight < 1 ? 0 : Math.ceil(visibleHeight / rh); }, - getVisibleRows: function() { + getVisibleRows: function () { var count = this.getVisibleRowCount(), sc = this.scroller.dom.scrollTop, start = @@ -97,7 +97,7 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { }; }, - doRender: function(cs, rs, ds, startRow, colCount, stripe, onlyBody) { + doRender: function (cs, rs, ds, startRow, colCount, stripe, onlyBody) { var ts = this.templates, ct = ts.cell, rt = ts.row, @@ -162,18 +162,18 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { return buf.join(''); }, - isRowRendered: function(index) { + isRowRendered: function (index) { var row = this.getRow(index); return row && row.childNodes.length > 0; }, - syncScroll: function() { + syncScroll: function () { Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments); this.update(); }, // a (optionally) buffered method to update contents of gridview - update: function() { + update: function () { if (this.scrollDelay) { if (!this.renderTask) { this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this); @@ -184,14 +184,14 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { } }, - onRemove: function(ds, record, index, isUpdate) { + onRemove: function (ds, record, index, isUpdate) { Ext.ux.grid.BufferView.superclass.onRemove.apply(this, arguments); if (isUpdate !== true) { this.update(); } }, - doUpdate: function() { + doUpdate: function () { if (this.getVisibleRowCount() > 0) { var g = this.grid, cm = g.colModel, @@ -219,14 +219,14 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { }, // a buffered method to clean rows - clean: function() { + clean: function () { if (!this.cleanTask) { this.cleanTask = new Ext.util.DelayedTask(this.doClean, this); } this.cleanTask.delay(this.cleanDelay); }, - doClean: function() { + doClean: function () { if (this.getVisibleRowCount() > 0) { var vr = this.getVisibleRows(); vr.first -= this.cacheSize; @@ -249,7 +249,7 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { } }, - removeTask: function(name) { + removeTask: function (name) { var task = this[name]; if (task && task.cancel) { task.cancel(); @@ -257,13 +257,13 @@ Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { } }, - destroy: function() { + destroy: function () { this.removeTask('cleanTask'); this.removeTask('renderTask'); Ext.ux.grid.BufferView.superclass.destroy.call(this); }, - layout: function() { + layout: function () { Ext.ux.grid.BufferView.superclass.layout.call(this); this.update(); }, diff --git a/deluge/ui/web/js/extjs/ext-extensions/layout/FormLayoutFix.js b/deluge/ui/web/js/extjs/ext-extensions/layout/FormLayoutFix.js index 14ac55a..3ccce2b 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/layout/FormLayoutFix.js +++ b/deluge/ui/web/js/extjs/ext-extensions/layout/FormLayoutFix.js @@ -12,7 +12,7 @@ // remove spaces for hidden elements and make show(), hide(), enable() and disable() act on // the label. don't use hideLabel with this. Ext.override(Ext.layout.FormLayout, { - renderItem: function(c, position, target) { + renderItem: function (c, position, target) { if ( c && !c.rendered && diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/MultiSelectionModelFix.js b/deluge/ui/web/js/extjs/ext-extensions/tree/MultiSelectionModelFix.js index 979bd2c..ba26a72 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/MultiSelectionModelFix.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/MultiSelectionModelFix.js @@ -13,7 +13,7 @@ * @author Damien Churchill <damoxc@gmail.com> */ Ext.override(Ext.tree.MultiSelectionModel, { - onNodeClick: function(node, e) { + onNodeClick: function (node, e) { if (e.ctrlKey && this.isSelected(node)) { this.unselect(node); } else if (e.shiftKey && !this.isSelected(node)) { @@ -34,7 +34,7 @@ Ext.override(Ext.tree.MultiSelectionModel, { } // Select all the nodes - parentNode.eachChild(function(n) { + parentNode.eachChild(function (n) { var i = parentNode.indexOf(n); if (fi < i && i < li) { this.select(n, e, true, true); @@ -48,7 +48,7 @@ Ext.override(Ext.tree.MultiSelectionModel, { } }, - select: function(node, e, keepExisting, suppressEvent) { + select: function (node, e, keepExisting, suppressEvent) { if (keepExisting !== true) { this.clearSelections(true); } diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGrid.js b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGrid.js index d3d5fc3..7a74360 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGrid.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGrid.js @@ -26,7 +26,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { columnsText: 'Columns', - initComponent: function() { + initComponent: function () { if (!this.root) { this.root = new Ext.tree.AsyncTreeNode({ text: 'Root' }); } @@ -98,7 +98,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - initColumns: function() { + initColumns: function () { var cs = this.columns, len = cs.length, columns = [], @@ -127,7 +127,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { this.columns = columns; }, - onRender: function() { + onRender: function () { Ext.tree.TreePanel.superclass.onRender.apply(this, arguments); this.el.addClass('x-treegrid'); @@ -176,7 +176,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - setRootNode: function(node) { + setRootNode: function (node) { node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI; node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node); if (this.innerCt) { @@ -187,7 +187,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { return node; }, - clearInnerCt: function() { + clearInnerCt: function () { if (Ext.isIE) { var dom = this.innerCt.dom; while (dom.firstChild) { @@ -198,7 +198,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - initEvents: function() { + initEvents: function () { Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments); this.mon(this.innerBody, 'scroll', this.syncScroll, this); @@ -210,7 +210,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }); }, - onResize: function(w, h) { + onResize: function (w, h) { Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments); var bd = this.innerBody.dom; @@ -234,7 +234,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { this.setScrollOffset(sw); } else { var me = this; - setTimeout(function() { + setTimeout(function () { me.setScrollOffset( bd.offsetWidth - bd.clientWidth > 10 ? sw : 0 ); @@ -243,7 +243,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - updateColumnWidths: function() { + updateColumnWidths: function () { var cols = this.columns, colCount = cols.length, groups = this.outerCt.query('colgroup'), @@ -282,7 +282,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { this.syncHeaderScroll(); }, - getVisibleColumns: function() { + getVisibleColumns: function () { var columns = [], cs = this.columns, len = cs.length, @@ -296,7 +296,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { return columns; }, - getTotalColumnWidth: function() { + getTotalColumnWidth: function () { var total = 0; for ( var i = 0, cs = this.getVisibleColumns(), len = cs.length; @@ -308,13 +308,13 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { return total; }, - setScrollOffset: function(scrollOffset) { + setScrollOffset: function (scrollOffset) { this.scrollOffset = scrollOffset; this.updateColumnWidths(); }, // private - handleHdDown: function(e, t) { + handleHdDown: function (e, t) { var hd = e.getTarget('.x-treegrid-hd'); if (hd && Ext.fly(t).hasClass('x-grid3-hd-btn')) { @@ -332,7 +332,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { this.hmenu.on( 'hide', - function() { + function () { Ext.fly(hd).removeClass('x-grid3-hd-menu-open'); }, this, @@ -347,7 +347,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }, // private - handleHdOver: function(e, t) { + handleHdOver: function (e, t) { var hd = e.getTarget('.x-treegrid-hd'); if (hd && !this.headersDisabled) { index = this.findHeaderIndex(hd); @@ -365,7 +365,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }, // private - handleHdOut: function(e, t) { + handleHdOut: function (e, t) { var hd = e.getTarget('.x-treegrid-hd'); if (hd && (!Ext.isIE || !e.within(hd, true))) { this.activeHdRef = null; @@ -374,7 +374,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { } }, - findHeaderIndex: function(hd) { + findHeaderIndex: function (hd) { hd = hd.dom || hd; var cs = hd.parentNode.childNodes; for (var i = 0, c; (c = cs[i]); i++) { @@ -386,7 +386,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }, // private - beforeColMenuShow: function() { + beforeColMenuShow: function () { var cols = this.columns, colCount = cols.length, i, @@ -409,7 +409,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { }, // private - handleHdMenuClick: function(item) { + handleHdMenuClick: function (item) { var index = this.hdCtxIndex, id = item.getItemId(); @@ -430,7 +430,7 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { return true; }, - setColumnVisible: function(index, visible) { + setColumnVisible: function (index, visible) { this.columns[index].hidden = !visible; this.updateColumnWidths(); }, @@ -438,26 +438,26 @@ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { /** * Scrolls the grid to the top */ - scrollToTop: function() { + scrollToTop: function () { this.innerBody.dom.scrollTop = 0; this.innerBody.dom.scrollLeft = 0; }, // private - syncScroll: function() { + syncScroll: function () { this.syncHeaderScroll(); var mb = this.innerBody.dom; this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop); }, // private - syncHeaderScroll: function() { + syncHeaderScroll: function () { var mb = this.innerBody.dom; this.innerHd.dom.scrollLeft = mb.scrollLeft; this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore) }, - registerNode: function(n) { + registerNode: function (n) { Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n); if (!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) { n.ui = new Ext.ux.tree.TreeGridNodeUI(n); diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridColumnResizer.js b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridColumnResizer.js index 870172e..de73608 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridColumnResizer.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridColumnResizer.js @@ -15,17 +15,17 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { */ minWidth: 14, - constructor: function(config) { + constructor: function (config) { Ext.apply(this, config); Ext.tree.ColumnResizer.superclass.constructor.call(this); }, - init: function(tree) { + init: function (tree) { this.tree = tree; tree.on('render', this.initEvents, this); }, - initEvents: function(tree) { + initEvents: function (tree) { tree.mon(tree.innerHd, 'mousemove', this.handleHdMove, this); this.tracker = new Ext.dd.DragTracker({ onBeforeStart: this.onBeforeStart.createDelegate(this), @@ -39,7 +39,7 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { tree.on('beforedestroy', this.tracker.destroy, this.tracker); }, - handleHdMove: function(e, t) { + handleHdMove: function (e, t) { var hw = 5, x = e.getPageX(), hd = e.getTarget('.x-treegrid-hd', 3, true); @@ -74,12 +74,12 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { } }, - onBeforeStart: function(e) { + onBeforeStart: function (e) { this.dragHd = this.activeHd; return !!this.dragHd; }, - onStart: function(e) { + onStart: function (e) { this.dragHeadersDisabled = this.tree.headersDisabled; this.tree.headersDisabled = true; this.proxy = this.tree.body.createChild({ cls: 'x-treegrid-resizer' }); @@ -98,14 +98,14 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { this.tree.innerBody.translatePoints(this.hdX).left; }, - onDrag: function(e) { + onDrag: function (e) { var cursorX = this.tracker.getXY()[0]; this.proxy.setWidth( (cursorX - this.hdX).constrain(this.minWidth, this.maxWidth) ); }, - onEnd: function(e) { + onEnd: function (e) { var nw = this.proxy.getWidth(), tree = this.tree, disabled = this.dragHeadersDisabled; @@ -116,7 +116,7 @@ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { tree.columns[this.hdIndex].width = nw; tree.updateColumnWidths(); - setTimeout(function() { + setTimeout(function () { tree.headersDisabled = disabled; }, 100); }, diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridColumns.js b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridColumns.js index 312bf21..0c88f17 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridColumns.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridColumns.js @@ -4,9 +4,9 @@ * licensing@sencha.com * http://www.sencha.com/license */ -(function() { +(function () { Ext.override(Ext.list.Column, { - init: function() { + init: function () { var types = Ext.data.Types, st = this.sortType; diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridLoader.js b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridLoader.js index eb5156a..db14848 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridLoader.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridLoader.js @@ -9,7 +9,7 @@ * @extends Ext.tree.TreeLoader */ Ext.ux.tree.TreeGridLoader = Ext.extend(Ext.tree.TreeLoader, { - createNode: function(attr) { + createNode: function (attr) { if (!attr.uiProvider) { attr.uiProvider = Ext.ux.tree.TreeGridNodeUI; } diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridNodeUI.js b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridNodeUI.js index e58a801..09b1718 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridNodeUI.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridNodeUI.js @@ -11,7 +11,7 @@ Ext.ux.tree.TreeGridNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { isTreeGridNodeUI: true, - renderElements: function(n, a, targetNode, bulkRender) { + renderElements: function (n, a, targetNode, bulkRender) { var t = n.getOwnerTree(), cols = t.columns, c = cols[0], @@ -111,7 +111,7 @@ Ext.ux.tree.TreeGridNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { }, // private - animExpand: function(cb) { + animExpand: function (cb) { this.ctNode.style.display = ''; Ext.ux.tree.TreeGridNodeUI.superclass.animExpand.call(this, cb); }, @@ -121,7 +121,7 @@ Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { isTreeGridNodeUI: true, // private - render: function() { + render: function () { if (!this.rendered) { this.wrap = this.ctNode = this.node.ownerTree.innerCt.dom; this.node.expanded = true; @@ -131,13 +131,13 @@ Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { // weird table-layout: fixed issue in webkit var ct = this.ctNode; ct.style.tableLayout = null; - (function() { + (function () { ct.style.tableLayout = 'fixed'; }.defer(1)); } }, - destroy: function() { + destroy: function () { if (this.elNode) { Ext.dd.Registry.unregister(this.elNode.id); } diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridNodeUIFix.js b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridNodeUIFix.js index 4c21bc3..7708bd7 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridNodeUIFix.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridNodeUIFix.js @@ -9,7 +9,7 @@ */ Ext.override(Ext.ux.tree.TreeGridNodeUI, { - updateColumns: function() { + updateColumns: function () { if (!this.rendered) return; var a = this.node.attributes, diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridRenderColumn.js b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridRenderColumn.js index 20bde8a..ed95d95 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridRenderColumn.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridRenderColumn.js @@ -1,5 +1,5 @@ Ext.tree.RenderColumn = Ext.extend(Ext.tree.Column, { - constructor: function(c) { + constructor: function (c) { c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':this.format}'); c.tpl.format = c.renderer; c.tpl.col = this; diff --git a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridSorter.js b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridSorter.js index 376f414..fdf1f38 100644 --- a/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridSorter.js +++ b/deluge/ui/web/js/extjs/ext-extensions/tree/TreeGridSorter.js @@ -40,7 +40,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { */ sortDescText: 'Sort Descending', - constructor: function(tree, config) { + constructor: function (tree, config) { if (!Ext.isObject(config)) { config = { property: tree.columns[0].dataIndex || 'text', @@ -58,7 +58,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { tree.ddAppendOnly = true; var me = this; - this.defaultSortFn = function(n1, n2) { + this.defaultSortFn = function (n1, n2) { var desc = me.dir && me.dir.toLowerCase() == 'desc', prop = me.property || 'text', sortType = me.sortType, @@ -101,7 +101,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { tree.on('headermenuclick', this.onHeaderMenuClick, this); }, - onAfterTreeRender: function() { + onAfterTreeRender: function () { if (this.tree.hmenu) { this.tree.hmenu.insert( 0, @@ -120,14 +120,14 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { this.updateSortIcon(0, 'asc'); }, - onHeaderMenuClick: function(c, id, index) { + onHeaderMenuClick: function (c, id, index) { if (id === 'asc' || id === 'desc') { this.onHeaderClick(c, null, index); return false; } }, - onHeaderClick: function(c, el, i) { + onHeaderClick: function (c, el, i) { if (c && !this.tree.headersDisabled) { var me = this; @@ -139,7 +139,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { : this.caseSensitive; me.sortFn = c.sortFn || this.defaultSortFn; - this.tree.root.cascade(function(n) { + this.tree.root.cascade(function (n) { if (!n.isLeaf()) { me.updateSort(me.tree, n); } @@ -150,7 +150,7 @@ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { }, // private - updateSortIcon: function(col, dir) { + updateSortIcon: function (col, dir) { var sc = this.sortClasses, hds = this.tree.innerHd.select('td').removeClass(sc); hds.item(col).addClass(sc[dir == 'desc' ? 1 : 0]); diff --git a/deluge/ui/web/js/gettext.js b/deluge/ui/web/js/gettext.js index 9cc1c4f..559577a 100644 --- a/deluge/ui/web/js/gettext.js +++ b/deluge/ui/web/js/gettext.js @@ -55,6 +55,8 @@ GetText.add('Connection Manager','${escape(_("Connection Manager"))}') GetText.add('Connection restored','${escape(_("Connection restored"))}') GetText.add('Connections','${escape(_("Connections"))}') GetText.add('Cookies','${escape(_("Cookies"))}') +GetText.add('Copy','${escape(_("Copy"))}') +GetText.add('Copy Magnet URI','${escape(_("Copy Magnet URI"))}') GetText.add('Copy of .torrent files to:','${escape(_("Copy of .torrent files to:"))}') GetText.add('Copyright 2007-2018 Deluge Team','${escape(_("Copyright 2007-2018 Deluge Team"))}') GetText.add('Create','${escape(_("Create"))}') @@ -92,6 +94,8 @@ GetText.add('Encryption','${escape(_("Encryption"))}') GetText.add('Error','${escape(_("Error"))}') GetText.add('Expand All','${escape(_("Expand All"))}') GetText.add('External IP Address','${escape(_("External IP Address"))}') +GetText.add('Failed to download torrent','${escape(_("Failed to download torrent"))}') +GetText.add('Failed to upload torrent','${escape(_("Failed to upload torrent"))}') GetText.add('File','${escape(_("File"))}') GetText.add('File Browser','${escape(_("File Browser"))}') GetText.add('Filename','${escape(_("Filename"))}') diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py index bfacb58..3f25614 100644 --- a/deluge/ui/web/json_api.py +++ b/deluge/ui/web/json_api.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009-2010 Damien Churchill <damoxc@gmail.com> # @@ -7,8 +6,7 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - +import cgi import json import logging import os @@ -16,7 +14,6 @@ import shutil import tempfile from base64 import b64encode from types import FunctionType -from xml.sax.saxutils import escape as xml_escape from twisted.internet import defer, reactor from twisted.internet.defer import Deferred, DeferredList @@ -39,7 +36,7 @@ log = logging.getLogger(__name__) class JSONComponent(component.Component): def __init__(self, name, interval=1, depend=None): - super(JSONComponent, self).__init__(name, interval, depend) + super().__init__(name, interval, depend) self._json = component.get('JSON') self._json.register_object(self, name) @@ -146,7 +143,7 @@ class JSON(resource.Resource, component.Component): params = request_data['params'] request_id = request_data['id'] except KeyError as ex: - message = 'Invalid JSON request, missing param %s in %s' % ( + message = 'Invalid JSON request, missing param {} in {}'.format( ex, request_data, ) @@ -167,7 +164,7 @@ class JSON(resource.Resource, component.Component): except Exception as ex: log.error('Error calling method `%s`: %s', method, ex) log.exception(ex) - error = {'message': '%s: %s' % (ex.__class__.__name__, str(ex)), 'code': 3} + error = {'message': f'{ex.__class__.__name__}: {str(ex)}', 'code': 3} return request_id, result, error @@ -184,7 +181,7 @@ class JSON(resource.Resource, component.Component): """ log.error(reason) response['error'] = { - 'message': '%s: %s' % (reason.__class__.__name__, str(reason)), + 'message': f'{reason.__class__.__name__}: {str(reason)}', 'code': 4, } return self._send_response(request, response) @@ -194,7 +191,7 @@ class JSON(resource.Resource, component.Component): Handler to take the json data as a string and pass it on to the _handle_request method for further processing. """ - content_type = request.getHeader(b'content-type').decode() + content_type, _ = cgi.parse_header(request.getHeader(b'content-type').decode()) if content_type != 'application/json': message = 'Invalid JSON request content-type: %s' % content_type raise JSONException(message) @@ -221,7 +218,7 @@ class JSON(resource.Resource, component.Component): 'id': None, 'error': { 'code': 5, - 'message': '%s: %s' % (reason.__class__.__name__, str(reason)), + 'message': f'{reason.__class__.__name__}: {str(reason)}', }, } return self._send_response(request, response) @@ -288,7 +285,7 @@ class JSON(resource.Resource, component.Component): FILES_KEYS = ['files', 'file_progress', 'file_priorities'] -class EventQueue(object): +class EventQueue: """ This class subscribes to events from the core and stores them until all the subscribed listeners have received the events. @@ -378,10 +375,8 @@ class WebApi(JSONComponent): methods available from the core RPC. """ - XSS_VULN_KEYS = ['name', 'message', 'comment', 'tracker_status', 'peers'] - def __init__(self): - super(WebApi, self).__init__('Web', depend=['SessionProxy']) + super().__init__('Web', depend=['SessionProxy']) self.hostlist = HostList() self.core_config = CoreConfig() self.event_queue = EventQueue() @@ -472,7 +467,7 @@ class WebApi(JSONComponent): The current connection state. :returns: True if the client is connected - :rtype: booleon + :rtype: boolean """ return client.connected() @@ -498,7 +493,7 @@ class WebApi(JSONComponent): :type keys: list :param filter_dict: the filters to apply when selecting torrents. :type filter_dict: dictionary - :returns: The torrent and ui information. + :returns: The torrent and UI information. :rtype: dictionary """ d = Deferred() @@ -518,7 +513,7 @@ class WebApi(JSONComponent): return d def got_stats(stats): - ui_info['stats']['num_connections'] = stats['num_peers'] + ui_info['stats']['num_connections'] = stats['peer.num_peers_connected'] ui_info['stats']['upload_rate'] = stats['payload_upload_rate'] ui_info['stats']['download_rate'] = stats['payload_download_rate'] ui_info['stats']['download_protocol_rate'] = ( @@ -527,9 +522,9 @@ class WebApi(JSONComponent): ui_info['stats']['upload_protocol_rate'] = ( stats['upload_rate'] - stats['payload_upload_rate'] ) - ui_info['stats']['dht_nodes'] = stats['dht_nodes'] + ui_info['stats']['dht_nodes'] = stats['dht.dht_nodes'] ui_info['stats']['has_incoming_connections'] = stats[ - 'has_incoming_connections' + 'net.has_incoming_connections' ] def got_filters(filters): @@ -555,13 +550,13 @@ class WebApi(JSONComponent): d3 = client.core.get_session_status( [ - 'num_peers', + 'peer.num_peers_connected', 'payload_download_rate', 'payload_upload_rate', 'download_rate', 'upload_rate', - 'dht_nodes', - 'has_incoming_connections', + 'dht.dht_nodes', + 'net.has_incoming_connections', ] ) d3.addCallback(got_stats) @@ -584,7 +579,7 @@ class WebApi(JSONComponent): paths = [] info = {} for index, torrent_file in enumerate(files): - path = xml_escape(torrent_file['path']) + path = torrent_file['path'] paths.append(path) torrent_file['progress'] = file_progress[index] torrent_file['priority'] = file_priorities[index] @@ -621,25 +616,10 @@ class WebApi(JSONComponent): file_tree.walk(walk) d.callback(file_tree.get_tree()) - def _on_torrent_status(self, torrent, d): - for key in self.XSS_VULN_KEYS: - try: - if key == 'peers': - for peer in torrent[key]: - peer['client'] = xml_escape(peer['client']) - else: - torrent[key] = xml_escape(torrent[key]) - except KeyError: - pass - d.callback(torrent) - @export def get_torrent_status(self, torrent_id, keys): """Get the status for a torrent, filtered by status keys.""" - main_deferred = Deferred() - d = component.get('SessionProxy').get_torrent_status(torrent_id, keys) - d.addCallback(self._on_torrent_status, main_deferred) - return main_deferred + return component.get('SessionProxy').get_torrent_status(torrent_id, keys) @export def get_torrent_files(self, torrent_id): @@ -659,9 +639,9 @@ class WebApi(JSONComponent): @export def download_torrent_from_url(self, url, cookie=None): """ - Download a torrent file from a url to a temporary directory. + Download a torrent file from a URL to a temporary directory. - :param url: the url of the torrent + :param url: the URL of the torrent :type url: string :returns: the temporary file name of the torrent file :rtype: string @@ -829,7 +809,7 @@ class WebApi(JSONComponent): password (str): The password to login to the daemon with. Returns: - bool: True if succesful, False otherwise. + bool: True if successful, False otherwise. """ return self.hostlist.update_host(host_id, host, port, username, password) @@ -842,7 +822,7 @@ class WebApi(JSONComponent): host_id (str): The host identifying hash. Returns: - bool: True if succesful, False otherwise. + bool: True if successful, False otherwise. """ return self.hostlist.remove_host(host_id) @@ -1002,11 +982,11 @@ class WebApi(JSONComponent): class WebUtils(JSONComponent): """ - Utility functions for the webui that do not fit in the WebApi. + Utility functions for the Web UI that do not fit in the WebApi. """ def __init__(self): - super(WebUtils, self).__init__('WebUtils') + super().__init__('WebUtils') @export def get_languages(self): @@ -1014,6 +994,6 @@ class WebUtils(JSONComponent): Get the available translated languages Returns: - list: of tuples [(lang-id, language-name), ...] + list: of tuples ``[(lang-id, language-name), ...]`` """ return get_languages() diff --git a/deluge/ui/web/pluginmanager.py b/deluge/ui/web/pluginmanager.py index 24f20ce..2da5b61 100644 --- a/deluge/ui/web/pluginmanager.py +++ b/deluge/ui/web/pluginmanager.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Damien Churchill <damoxc@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os @@ -74,22 +71,20 @@ class PluginManager(PluginManagerBase, component.Component): scripts = component.get('Scripts') for script in info['scripts']: - scripts.remove_script( - '%s/%s' % (name.lower(), os.path.basename(script).lower()) - ) + scripts.remove_script(f'{name.lower()}/{os.path.basename(script).lower()}') for script in info['debug_scripts']: scripts.remove_script( - '%s/%s' % (name.lower(), os.path.basename(script).lower()), 'debug' + f'{name.lower()}/{os.path.basename(script).lower()}', 'debug' ) scripts.remove_script( - '%s/%s' % (name.lower(), os.path.basename(script).lower()), 'dev' + f'{name.lower()}/{os.path.basename(script).lower()}', 'dev' ) - super(PluginManager, self).disable_plugin(name) + super().disable_plugin(name) def enable_plugin(self, name): - super(PluginManager, self).enable_plugin(name) + super().enable_plugin(name) # Get the plugin instance try: @@ -105,17 +100,15 @@ class PluginManager(PluginManagerBase, component.Component): scripts = component.get('Scripts') for script in info['scripts']: log.debug('adding script %s for %s', name, os.path.basename(script)) - scripts.add_script( - '%s/%s' % (name.lower(), os.path.basename(script)), script - ) + scripts.add_script(f'{name.lower()}/{os.path.basename(script)}', script) for script in info['debug_scripts']: log.debug('adding debug script %s for %s', name, os.path.basename(script)) scripts.add_script( - '%s/%s' % (name.lower(), os.path.basename(script)), script, 'debug' + f'{name.lower()}/{os.path.basename(script)}', script, 'debug' ) scripts.add_script( - '%s/%s' % (name.lower(), os.path.basename(script)), script, 'dev' + f'{name.lower()}/{os.path.basename(script)}', script, 'dev' ) def start(self): @@ -151,11 +144,10 @@ class PluginManager(PluginManagerBase, component.Component): info = gather_info(plugin) info['name'] = name info['scripts'] = [ - 'js/%s/%s' % (name.lower(), os.path.basename(s)) for s in info['scripts'] + f'js/{name.lower()}/{os.path.basename(s)}' for s in info['scripts'] ] info['debug_scripts'] = [ - 'js/%s/%s' % (name.lower(), os.path.basename(s)) - for s in info['debug_scripts'] + f'js/{name.lower()}/{os.path.basename(s)}' for s in info['debug_scripts'] ] del info['script_directories'] return info diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py index 192c2b7..f391a78 100644 --- a/deluge/ui/web/server.py +++ b/deluge/ui/web/server.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009-2010 Damien Churchill <damoxc@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import fnmatch import json import logging @@ -23,8 +20,7 @@ from twisted.web.resource import EncodingResourceWrapper from deluge import common, component, configmanager from deluge.common import is_ipv6 -from deluge.core.rpcserver import check_ssl_keys -from deluge.crypto_utils import get_context_factory +from deluge.crypto_utils import check_ssl_keys, get_context_factory from deluge.i18n import set_language, setup_translation from deluge.ui.tracker_icons import TrackerIcons from deluge.ui.web.auth import Auth @@ -191,7 +187,7 @@ class Tracker(resource.Resource): request.finish() def render(self, request): - d = self.tracker_icons.fetch(request.tracker_name) + d = self.tracker_icons.fetch(request.tracker_name.decode()) d.addCallback(self.on_got_icon, request) return server.NOT_DONE_YET @@ -376,7 +372,7 @@ class ScriptResource(resource.Resource, component.Component): order_file = os.path.join(root, '.order') if os.path.isfile(order_file): - with open(order_file, 'r') as _file: + with open(order_file) as _file: for line in _file: if line.startswith('+ '): order_filename = line.split()[1] @@ -592,13 +588,13 @@ class TopLevel(resource.Resource): uri_false = ('false', 'no', 'off', '0') debug_arg = None - req_dbg_arg = request.args.get('debug', [b''])[-1].decode().lower() + req_dbg_arg = request.args.get(b'debug', [b''])[-1].decode().lower() if req_dbg_arg in uri_true: debug_arg = True elif req_dbg_arg in uri_false: debug_arg = False - dev_arg = request.args.get('dev', [b''])[-1].decode().lower() in uri_true + dev_arg = request.args.get(b'dev', [b''])[-1].decode().lower() in uri_true dev_ver = 'dev' in common.get_version() script_type = 'normal' @@ -653,7 +649,7 @@ class DelugeWeb(component.Component): Args: options (argparse.Namespace): The web server options. - daemon (bool): If True run web server as a seperate daemon process (starts a twisted + daemon (bool): If True run web server as a separate daemon process (starts a twisted reactor). If False shares the process and twisted reactor from WebUI plugin or tests. """ @@ -698,7 +694,7 @@ class DelugeWeb(component.Component): self.auth = Auth(self.config) self.daemon = daemon - # Initalize the plugins + # Initialize the plugins self.plugins = PluginManager() def _on_language_changed(self, key, value): diff --git a/deluge/ui/web/web.py b/deluge/ui/web/web.py index 4d06247..f855bd0 100644 --- a/deluge/ui/web/web.py +++ b/deluge/ui/web/web.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Damien Churchill <damoxc@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import print_function, unicode_literals - import logging from twisted.internet.error import CannotListenError @@ -24,7 +21,7 @@ class Web(UI): cmd_description = """Web-based user interface (http://localhost:8112)""" def __init__(self, *args, **kwargs): - super(Web, self).__init__( + super().__init__( 'web', *args, description='Starts the Deluge Web interface', **kwargs ) self.__server = None @@ -67,7 +64,7 @@ class Web(UI): return self.__server def start(self): - super(Web, self).start() + super().start() from deluge.ui.web import server |