diff options
Diffstat (limited to 'deluge/ui/web/js/deluge-all/details')
-rw-r--r-- | deluge/ui/web/js/deluge-all/details/DetailsPanel.js | 81 | ||||
-rw-r--r-- | deluge/ui/web/js/deluge-all/details/DetailsTab.js | 98 | ||||
-rw-r--r-- | deluge/ui/web/js/deluge-all/details/FilesTab.js | 233 | ||||
-rw-r--r-- | deluge/ui/web/js/deluge-all/details/OptionsTab.js | 417 | ||||
-rw-r--r-- | deluge/ui/web/js/deluge-all/details/PeersTab.js | 166 | ||||
-rw-r--r-- | deluge/ui/web/js/deluge-all/details/StatusTab.js | 155 |
6 files changed, 1150 insertions, 0 deletions
diff --git a/deluge/ui/web/js/deluge-all/details/DetailsPanel.js b/deluge/ui/web/js/deluge-all/details/DetailsPanel.js new file mode 100644 index 0000000..1c51de4 --- /dev/null +++ b/deluge/ui/web/js/deluge-all/details/DetailsPanel.js @@ -0,0 +1,81 @@ +/** + * Deluge.details.DetailsPanel.js + * + * Copyright (c) Damien Churchill 2009-2010 <damoxc@gmail.com> + * + * 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. + */ +Ext.namespace('Deluge.details'); + +/** + * @class Deluge.details.DetailsPanel + */ +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(panel) { + if (panel.clear) { + panel.clear.defer(100, panel); + panel.disable(); + } + }); + }, + + update: function(tab) { + var torrent = deluge.torrents.getSelected(); + if (!torrent) { + this.clear(); + return; + } + + this.items.each(function(tab) { + if (tab.disabled) tab.enable(); + }); + + tab = tab || this.getActiveTab(); + if (tab.update) tab.update(torrent.id); + }, + + /* Event Handlers */ + + // We need to add the events in onRender since Deluge.Torrents has not been created yet. + onRender: function(ct, position) { + Deluge.details.DetailsPanel.superclass.onRender.call( + this, + ct, + position + ); + 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(selModel) { + if (!selModel.hasSelection()) this.clear(); + }, + this + ); + }, + + onTabChange: function(panel, tab) { + this.update(tab); + }, + + 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 new file mode 100644 index 0000000..84929ae --- /dev/null +++ b/deluge/ui/web/js/deluge-all/details/DetailsTab.js @@ -0,0 +1,98 @@ +/** + * Deluge.Details.Details.js + * The details tab displayed in the details panel. + * + * Copyright (c) Damien Churchill 2009-2010 <damoxc@gmail.com> + * + * 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. + */ + +Deluge.details.DetailsTab = Ext.extend(Ext.Panel, { + title: _('Details'), + + fields: {}, + autoScroll: true, + 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(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); + + for (var id in this.queuedItems) { + this.doAddItem(id, this.queuedItems[id]); + } + }, + + addItem: function(id, label) { + if (!this.rendered) { + this.queuedItems[id] = label; + } else { + this.doAddItem(id, label); + } + }, + + // private + doAddItem: function(id, label) { + Ext.DomHelper.append(this.dl, { tag: 'dt', cls: id, html: label }); + this.fields[id] = Ext.DomHelper.append( + this.dl, + { tag: 'dd', cls: id, html: '' }, + true + ); + }, + + clear: function() { + if (!this.fields) return; + for (var k in this.fields) { + this.fields[k].dom.innerHTML = ''; + } + this.oldData = {}; + }, + + update: function(torrentId) { + deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Details, { + success: this.onRequestComplete, + scope: this, + torrentId: torrentId, + }); + }, + + onRequestComplete: function(torrent, request, response, options) { + var data = { + torrent_name: torrent.name, + hash: options.options.torrentId, + path: torrent.download_location, + size: fsize(torrent.total_size), + files: torrent.num_files, + status: torrent.message, + tracker: torrent.tracker_host, + comment: torrent.comment, + creator: torrent.creator, + }; + + 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.oldData = data; + }, +}); diff --git a/deluge/ui/web/js/deluge-all/details/FilesTab.js b/deluge/ui/web/js/deluge-all/details/FilesTab.js new file mode 100644 index 0000000..3a212fa --- /dev/null +++ b/deluge/ui/web/js/deluge-all/details/FilesTab.js @@ -0,0 +1,233 @@ +/** + * Deluge.details.FilesTab.js + * + * Copyright (c) Damien Churchill 2009-2010 <damoxc@gmail.com> + * + * 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. + */ + +Deluge.details.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { + title: _('Files'), + + rootVisible: false, + + columns: [ + { + header: _('Filename'), + width: 330, + dataIndex: 'filename', + }, + { + header: _('Size'), + width: 150, + dataIndex: 'size', + tpl: new Ext.XTemplate('{size:this.fsize}', { + fsize: function(v) { + return fsize(v); + }, + }), + }, + { + xtype: 'tgrendercolumn', + header: _('Progress'), + width: 150, + dataIndex: 'progress', + renderer: function(v) { + var progress = v * 100; + return Deluge.progressBar( + progress, + this.col.width, + progress.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(v) { + return FILE_PRIORITY_CSS[v]; + }, + + getName: function(v) { + return _(FILE_PRIORITY[v]); + }, + } + ), + }, + ], + + 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 root = this.getRootNode(); + if (!root.hasChildNodes()) return; + root.cascade(function(node) { + var parentNode = node.parentNode; + if (!parentNode) return; + if (!parentNode.ownerTree) return; + parentNode.removeChild(node); + }); + }, + + createFileTree: function(files) { + function walk(files, parentNode) { + for (var file in files.contents) { + var item = files.contents[file]; + if (item.type == 'dir') { + walk( + item, + parentNode.appendChild( + new Ext.tree.TreeNode({ + text: file, + filename: file, + size: item.size, + progress: item.progress, + priority: item.priority, + }) + ) + ); + } else { + parentNode.appendChild( + new Ext.tree.TreeNode({ + text: file, + filename: file, + fileIndex: item.index, + size: item.size, + progress: item.progress, + priority: item.priority, + leaf: true, + iconCls: 'x-deluge-file', + uiProvider: Ext.ux.tree.TreeGridNodeUI, + }) + ); + } + } + } + var root = this.getRootNode(); + walk(files, root); + root.firstChild.expand(); + }, + + update: function(torrentId) { + if (this.torrentId != torrentId) { + this.clear(); + this.torrentId = torrentId; + } + + deluge.client.web.get_torrent_files(torrentId, { + success: this.onRequestComplete, + scope: this, + torrentId: torrentId, + }); + }, + + updateFileTree: function(files) { + function walk(files, parentNode) { + for (var file in files.contents) { + var item = files.contents[file]; + var node = parentNode.findChild('filename', file); + node.attributes.size = item.size; + node.attributes.progress = item.progress; + node.attributes.priority = item.priority; + node.ui.updateColumns(); + if (item.type == 'dir') { + walk(item, node); + } + } + } + walk(files, this.getRootNode()); + }, + + 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); + this.sorter = new Ext.tree.TreeSorter(this, { + folderSort: true, + }); + }, + + onContextMenu: function(node, e) { + e.stopEvent(); + var selModel = this.getSelectionModel(); + if (selModel.getSelectedNodes().length < 2) { + selModel.clearSelections(); + node.select(); + } + deluge.menus.filePriorities.showAt(e.getPoint()); + }, + + onItemClick: function(baseItem, e) { + switch (baseItem.id) { + case 'expandAll': + this.expandAll(); + break; + default: + var indexes = {}; + var walk = function(node) { + if (Ext.isEmpty(node.attributes.fileIndex)) return; + indexes[node.attributes.fileIndex] = + node.attributes.priority; + }; + this.getRootNode().cascade(walk); + + var nodes = this.getSelectionModel().getSelectedNodes(); + Ext.each(nodes, function(node) { + if (!node.isLeaf()) { + var setPriorities = function(node) { + if (Ext.isEmpty(node.attributes.fileIndex)) return; + indexes[node.attributes.fileIndex] = + baseItem.filePriority; + }; + node.cascade(setPriorities); + } else if (!Ext.isEmpty(node.attributes.fileIndex)) { + indexes[node.attributes.fileIndex] = + baseItem.filePriority; + return; + } + }); + + var priorities = new Array(Ext.keys(indexes).length); + for (var index in indexes) { + priorities[index] = indexes[index]; + } + + deluge.client.core.set_torrent_options( + [this.torrentId], + { file_priorities: priorities }, + { + success: function() { + Ext.each(nodes, function(node) { + node.setColumnValue(3, baseItem.filePriority); + }); + }, + scope: this, + } + ); + break; + } + }, + + onRequestComplete: function(files, options) { + if (!this.getRootNode().hasChildNodes()) { + this.createFileTree(files); + } else { + this.updateFileTree(files); + } + }, +}); diff --git a/deluge/ui/web/js/deluge-all/details/OptionsTab.js b/deluge/ui/web/js/deluge-all/details/OptionsTab.js new file mode 100644 index 0000000..b11486b --- /dev/null +++ b/deluge/ui/web/js/deluge-all/details/OptionsTab.js @@ -0,0 +1,417 @@ +/** + * Deluge.details.OptionsTab.js + * + * Copyright (c) Damien Churchill 2009-2010 <damoxc@gmail.com> + * + * 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. + */ + +Deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply( + { + autoScroll: true, + bodyStyle: 'padding: 5px;', + border: false, + cls: 'x-deluge-options', + defaults: { + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox', + }, + deferredRender: false, + layout: 'column', + title: _('Options'), + }, + config + ); + Deluge.details.OptionsTab.superclass.constructor.call(this, config); + }, + + 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: false, + stop_at_ratio: false, + stop_ratio: 2.0, + remove_at_ratio: false, + move_completed: false, + move_completed_path: '', + private: false, + prioritize_first_last: false, + super_seeding: false, + }, + }); + + /* + * Bandwidth Options + */ + 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, + }); + + /* + * Max Download Speed + */ + 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', + }); + + /* + * Max Upload Speed + */ + 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', + }); + + /* + * Max Connections + */ + 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, + }); + + /* + * Max Upload Slots + */ + 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, + }); + + /* + * Queue Options + */ + 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: true, + width: 50, + value: 2.0, + 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: true, + 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: true, + }); + + /* + * General Options + */ + this.rightColumn = this.add({ + border: false, + autoHeight: true, + style: 'margin-left: 5px', + width: 210, + }); + + this.fieldsets.general = this.rightColumn.add({ + xtype: 'fieldset', + autoHeight: true, + defaultType: 'checkbox', + title: _('General'), + layout: 'form', + }); + + this.fields['private'] = this.fieldsets.general.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Private'), + id: 'private', + disabled: true, + }); + + 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', + }); + + // Bind the fields so the options manager can manage them. + for (var id in this.fields) { + this.optionsManager.bind(id, this.fields[id]); + } + + /* + * Buttons + */ + this.buttonPanel = this.rightColumn.add({ + layout: 'hbox', + xtype: 'panel', + border: false, + }); + + /* + * Edit Trackers button + */ + this.buttonPanel.add({ + id: 'edit_trackers', + xtype: 'button', + text: _('Edit Trackers'), + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-edit-trackers', + border: false, + width: 100, + handler: this.onEditTrackers, + scope: this, + }); + + /* + * Apply button + */ + this.buttonPanel.add({ + id: 'apply', + xtype: 'button', + text: _('Apply'), + style: 'margin-left: 10px;', + border: false, + width: 100, + handler: this.onApply, + scope: this, + }); + }, + + 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. + this.layout = new Ext.layout.ColumnLayout(); + this.layout.setContainer(this); + this.doLayout(); + }, + + clear: function() { + if (this.torrentId == null) return; + this.torrentId = null; + this.optionsManager.changeId(null); + }, + + reset: function() { + if (this.torrentId) this.optionsManager.reset(); + }, + + 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. + + if (this.torrentId != torrentId) { + this.torrentId = torrentId; + this.optionsManager.changeId(torrentId); + } + deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Options, { + success: this.onRequestComplete, + scope: this, + }); + }, + + onApply: function() { + var changed = this.optionsManager.getDirty(); + deluge.client.core.set_torrent_options([this.torrentId], changed, { + success: function() { + this.optionsManager.commit(); + }, + scope: this, + }); + }, + + onEditTrackers: function() { + deluge.editTrackers.show(); + }, + + onMoveCompletedChecked: function(checkbox, checked) { + this.fields.move_completed_path.setDisabled(!checked); + + if (!checked) return; + this.fields.move_completed_path.focus(); + }, + + onStopRatioChecked: function(checkbox, checked) { + this.fields.remove_at_ratio.setDisabled(!checked); + this.fields.stop_ratio.setDisabled(!checked); + }, + + onRequestComplete: function(torrent, options) { + this.fields['private'].setValue(torrent['private']); + this.fields['private'].setDisabled(true); + delete torrent['private']; + torrent['auto_managed'] = torrent['is_auto_managed']; + torrent['prioritize_first_last_pieces'] = + torrent['prioritize_first_last']; + this.optionsManager.setDefault(torrent); + var stop_at_ratio = this.optionsManager.get('stop_at_ratio'); + this.fields.remove_at_ratio.setDisabled(!stop_at_ratio); + this.fields.stop_ratio.setDisabled(!stop_at_ratio); + this.fields.move_completed_path.setDisabled( + !this.optionsManager.get('move_completed') + ); + }, +}); diff --git a/deluge/ui/web/js/deluge-all/details/PeersTab.js b/deluge/ui/web/js/deluge-all/details/PeersTab.js new file mode 100644 index 0000000..515e533 --- /dev/null +++ b/deluge/ui/web/js/deluge-all/details/PeersTab.js @@ -0,0 +1,166 @@ +/** + * Deluge.details.PeersTab.js + * + * Copyright (c) Damien Churchill 2009-2010 <damoxc@gmail.com> + * + * 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. + */ + +(function() { + function flagRenderer(value) { + if (!value.replace(' ', '').replace(' ', '')) { + return ''; + } + return String.format( + '<img src="{0}flag/{1}" />', + deluge.config.base, + value + ); + } + function peerAddressRenderer(value, p, record) { + var seed = + record.data['seed'] == 1024 ? 'x-deluge-seed' : 'x-deluge-peer'; + // Modify display of IPv6 to include brackets + var peer_ip = value.split(':'); + if (peer_ip.length > 2) { + var port = peer_ip.pop(); + var ip = peer_ip.join(':'); + value = '[' + ip + ']:' + port; + } + return String.format('<div class="{0}">{1}</div>', seed, value); + } + function peerProgressRenderer(value) { + var progress = (value * 100).toFixed(0); + return Deluge.progressBar(progress, this.width - 8, progress + '%'); + } + + Deluge.details.PeersTab = Ext.extend(Ext.grid.GridPanel, { + // fast way to figure out if we have a peer already. + peers: {}, + + constructor: function(config) { + config = 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: true, + renderer: flagRenderer, + dataIndex: 'country', + }, + { + header: _('Address'), + width: 125, + sortable: true, + renderer: peerAddressRenderer, + dataIndex: 'ip', + }, + { + header: _('Client'), + width: 125, + sortable: true, + renderer: fplain, + dataIndex: 'client', + }, + { + header: _('Progress'), + width: 150, + sortable: true, + renderer: peerProgressRenderer, + dataIndex: 'progress', + }, + { + header: _('Down Speed'), + width: 100, + sortable: true, + renderer: fspeed, + dataIndex: 'down_speed', + }, + { + header: _('Up Speed'), + width: 100, + sortable: true, + renderer: fspeed, + dataIndex: 'up_speed', + }, + ], + stripeRows: true, + deferredRender: false, + autoScroll: true, + }, + config + ); + Deluge.details.PeersTab.superclass.constructor.call(this, config); + }, + + clear: function() { + this.getStore().removeAll(); + this.peers = {}; + }, + + update: function(torrentId) { + deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Peers, { + success: this.onRequestComplete, + scope: this, + }); + }, + + onRequestComplete: function(torrent, options) { + if (!torrent) return; + + var store = this.getStore(); + var newPeers = []; + var addresses = {}; + + // Go through the peers updating and creating peer records + Ext.each( + torrent.peers, + function(peer) { + if (this.peers[peer.ip]) { + var record = store.getById(peer.ip); + record.beginEdit(); + for (var k in peer) { + if (record.get(k) != peer[k]) { + record.set(k, peer[k]); + } + } + record.endEdit(); + } else { + this.peers[peer.ip] = 1; + newPeers.push(new Deluge.data.Peer(peer, peer.ip)); + } + addresses[peer.ip] = 1; + }, + this + ); + store.add(newPeers); + + // Remove any peers that should not be left in the store. + store.each(function(record) { + if (!addresses[record.id]) { + store.remove(record); + delete this.peers[record.id]; + } + }, this); + store.commitChanges(); + + var sortState = store.getSortState(); + if (!sortState) return; + store.sort(sortState.field, sortState.direction); + }, + }); +})(); diff --git a/deluge/ui/web/js/deluge-all/details/StatusTab.js b/deluge/ui/web/js/deluge-all/details/StatusTab.js new file mode 100644 index 0000000..a8753bb --- /dev/null +++ b/deluge/ui/web/js/deluge-all/details/StatusTab.js @@ -0,0 +1,155 @@ +/** + * Deluge.details.StatusTab.js + * + * Copyright (c) Damien Churchill 2009-2010 <damoxc@gmail.com> + * + * 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. + */ +Ext.ns('Deluge.details'); + +/** + * @class Deluge.details.StatusTab + * @extends Ext.Panel + */ +Deluge.details.StatusTab = Ext.extend(Ext.Panel, { + title: _('Status'), + autoScroll: true, + + onRender: function(ct, position) { + Deluge.details.StatusTab.superclass.onRender.call(this, ct, position); + + this.progressBar = this.add({ + xtype: 'progress', + cls: 'x-deluge-status-progressbar', + }); + + this.status = this.add({ + cls: 'x-deluge-status', + id: 'deluge-details-status', + + border: false, + width: 1000, + listeners: { + render: { + fn: function(panel) { + panel.load({ + url: deluge.config.base + 'render/tab_status.html', + text: _('Loading') + '...', + }); + panel + .getUpdater() + .on('update', this.onPanelUpdate, this); + }, + scope: this, + }, + }, + }); + }, + + clear: function() { + this.progressBar.updateProgress(0, ' '); + for (var k in this.fields) { + this.fields[k].innerHTML = ''; + } + }, + + update: function(torrentId) { + if (!this.fields) this.getFields(); + deluge.client.web.get_torrent_status(torrentId, Deluge.Keys.Status, { + success: this.onRequestComplete, + scope: this, + }); + }, + + onPanelUpdate: function(el, response) { + this.fields = {}; + Ext.each( + Ext.query('dd', this.status.body.dom), + function(field) { + this.fields[field.className] = field; + }, + this + ); + }, + + onRequestComplete: function(status) { + seeds = + status.total_seeds > -1 + ? status.num_seeds + ' (' + status.total_seeds + ')' + : status.num_seeds; + peers = + status.total_peers > -1 + ? status.num_peers + ' (' + status.total_peers + ')' + : status.num_peers; + last_seen_complete = + status.last_seen_complete > 0.0 + ? fdate(status.last_seen_complete) + : 'Never'; + completed_time = + status.completed_time > 0.0 ? fdate(status.completed_time) : ''; + + var data = { + downloaded: fsize(status.total_done, true), + uploaded: fsize(status.total_uploaded, true), + share: status.ratio == -1 ? '∞' : status.ratio.toFixed(3), + announce: ftime(status.next_announce), + tracker_status: status.tracker_status, + downspeed: status.download_payload_rate + ? fspeed(status.download_payload_rate) + : '0.0 KiB/s', + upspeed: status.upload_payload_rate + ? fspeed(status.upload_payload_rate) + : '0.0 KiB/s', + eta: status.eta < 0 ? '∞' : ftime(status.eta), + pieces: status.num_pieces + ' (' + fsize(status.piece_length) + ')', + seeds: seeds, + peers: peers, + avail: status.distributed_copies.toFixed(3), + active_time: ftime(status.active_time), + seeding_time: ftime(status.seeding_time), + seed_rank: status.seed_rank, + time_added: fdate(status.time_added), + last_seen_complete: last_seen_complete, + completed_time: completed_time, + time_since_transfer: ftime(status.time_since_transfer), + }; + data.auto_managed = _(status.is_auto_managed ? 'True' : 'False'); + + var translate_tracker_status = { + Error: _('Error'), + Warning: _('Warning'), + 'Announce OK': _('Announce OK'), + 'Announce Sent': _('Announce Sent'), + }; + for (var key in translate_tracker_status) { + if (data.tracker_status.indexOf(key) != -1) { + data.tracker_status = data.tracker_status.replace( + key, + translate_tracker_status[key] + ); + break; + } + } + + data.downloaded += + ' (' + + (status.total_payload_download + ? fsize(status.total_payload_download) + : '0.0 KiB') + + ')'; + data.uploaded += + ' (' + + (status.total_payload_upload + ? fsize(status.total_payload_upload) + : '0.0 KiB') + + ')'; + + for (var field in this.fields) { + this.fields[field].innerHTML = data[field]; + } + var text = status.state + ' ' + status.progress.toFixed(2) + '%'; + this.progressBar.updateProgress(status.progress / 100.0, text); + }, +}); |