      1 /*
      2 Script: deluge-torrents.js
      3     Contains all objects and functions related to the torrent grid.
      4
      5 Copyright:
      6 	(C) Damien Churchill 2009 <damoxc@gmail.com>
      7 	This program is free software; you can redistribute it and/or modify
      8 	it under the terms of the GNU General Public License as published by
      9 	the Free Software Foundation; either version 3, or (at your option)
     10 	any later version.
     11
     12 	This program is distributed in the hope that it will be useful,
     13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 	GNU General Public License for more details.
     16
     17 	You should have received a copy of the GNU General Public License
     18 	along with this program.  If not, write to:
     19 		The Free Software Foundation, Inc.,
     20 		51 Franklin Street, Fifth Floor
     21 		Boston, MA  02110-1301, USA.
     22 #
     23 #    In addition, as a special exception, the copyright holders give
     24 #    permission to link the code of portions of this program with the OpenSSL
     25 #    library.
     26 #    You must obey the GNU General Public License in all respects for all of
     27 #    the code used other than OpenSSL. If you modify file(s) with this
     28 #    exception, you may extend this exception to your version of the file(s),
     29 #    but you are not obligated to do so. If you do not wish to do so, delete
     30 #    this exception statement from your version. If you delete this exception
     31 #    statement from all source files in the program, then also delete it here.
     32 #
     33 */
     34
     35 (function() {
     36 	/* Renderers for the Torrent Grid */
     37 	function queueRenderer(value) {
     38 		return (value == -1) ? '' : value + 1;
     39 	}
     40 	function torrentNameRenderer(value, p, r) {
     41 		return String.format('<div class="torrent-name x-deluge-{0}">{1}</div>', r.data['state'].toLowerCase(), value);
     42 	}
     43 	function torrentSpeedRenderer(value) {
     44 		if (!value) return;
     45 		return fspeed(value);
     46 	}
     47 	function torrentProgressRenderer(value, p, r) {
     48 		value = new Number(value);
     49 		var progress = value;
     50 		var text = r.data['state'] + ' ' + value.toFixed(2) + '%'
     51 		var width = new Number(this.style.match(/\w+:\s*(\d+)\w+/)[1]) - 8;
     52 		return Deluge.progressBar(value, width, text);
     53 	}
     54 	function seedsRenderer(value, p, r) {
     55 		if (r.data['total_seeds'] > -1) {
     56 			return String.format('{0} ({1})', value, r.data['total_seeds']);
     57 		} else {
     58 			return value;
     59 		}
     60 	}
     61 	function peersRenderer(value, p, r) {
     62 		if (r.data['total_peers'] > -1) {
     63 			return String.format('{0} ({1})', value, r.data['total_peers']);
     64 		} else {
     65 			return value;
     66 		}
     67 	}
     68 	function availRenderer(value, p, r)	{
     69 		return new Number(value).toFixed(3);
     70 	}
     71 	function trackerRenderer(value, p, r) {
     72 		return String.format('<div style="background: url(/tracker/{0}) no-repeat; padding-left: 20px;">{0}</div>', value);
     73 	}
     74
     75 	/**
     76 	* Ext.deluge.TorrentGrid Class
     77 	*
     78 	* @author Damien Churchill <damoxc@gmail.com>
     79 	* @version 1.2
     80 	*
     81 	* @class Ext.deluge.TorrentGrid
     82 	* @extends Ext.grid.GridPanel
     83 	* @constructor
     84 	* @param {Object} config Configuration options
     85 	*/
     86 	Ext.deluge.TorrentGrid = Ext.extend(Ext.grid.GridPanel, {
     87 		constructor: function(config) {
     88 			config = Ext.apply({
     89 				id: 'torrentGrid',
     90 				store: new Ext.data.SimpleStore({
     91 					fields: [
     92 						{name: 'queue'},
     93 						{name: 'name'},
     94 						{name: 'size', type: 'int'},
     95 						{name: 'state'},
     96 						{name: 'progress', type: 'float'},
     97 						{name: 'seeds', type: 'int'},
     98 						{name: 'total_seeds', type: 'int'},
     99 						{name: 'peers', type: 'int'},
    100 						{name: 'total_peers', type: 'int'},
    101 						{name: 'downspeed', type: 'int'},
    102 						{name: 'upspeed', type: 'int'},
    103 						{name: 'eta', type: 'int'},
    104 						{name: 'ratio', type: 'float'},
    105 						{name: 'avail', type: 'float'},
    106 						{name: 'added', type: 'int'},
    107 						{name: 'tracker'}
    108 					],
    109 					id: 16
    110 				}),
    111 				columns: [{
    112 					id:'queue',
    113 					header: _('#'),
    114 					width: 30,
    115 					sortable: true,
    116 					renderer: queueRenderer,
    117 					dataIndex: 'queue'
    118 				}, {
    119 					id:'name',
    120 					header: _('Name'),
    121 					width: 150,
    122 					sortable: true,
    123 					renderer: torrentNameRenderer,
    124 					dataIndex: 'name'
    125 				}, {
    126 					header: _('Size'),
    127 					width: 75,
    128 					sortable: true,
    129 					renderer: fsize,
    130 					dataIndex: 'size'
    131 				}, {
    132 					header: _('Progress'),
    133 					width: 150,
    134 					sortable: true,
    135 					renderer: torrentProgressRenderer,
    136 					dataIndex: 'progress'
    137 				}, {
    138 					header: _('Seeders'),
    139 					width: 60,
    140 					sortable: true,
    141 					renderer: seedsRenderer,
    142 					dataIndex: 'seeds'
    143 				}, {
    144 					header: _('Peers'),
    145 					width: 60,
    146 					sortable: true,
    147 					renderer: peersRenderer,
    148 					dataIndex: 'peers'
    149 				}, {
    150 					header: _('Down Speed'),
    151 					width: 80,
    152 					sortable: true,
    153 					renderer: torrentSpeedRenderer,
    154 					dataIndex: 'downspeed'
    155 				}, {
    156 					header: _('Up Speed'),
    157 					width: 80,
    158 					sortable: true,
    159 					renderer: torrentSpeedRenderer,
    160 					dataIndex: 'upspeed'
    161 				}, {
    162 					header: _('ETA'),
    163 					width: 60,
    164 					sortable: true,
    165 					renderer: ftime,
    166 					dataIndex: 'eta'
    167 				}, {
    168 					header: _('Ratio'),
    169 					width: 60,
    170 					sortable: true,
    171 					renderer: availRenderer,
    172 					dataIndex: 'ratio'
    173 				}, {
    174 					header: _('Avail'),
    175 					width: 60,
    176 					sortable: true,
    177 					renderer: availRenderer,
    178 					dataIndex: 'avail'
    179 				}, {
    180 					header: _('Added'),
    181 					width: 80,
    182 					sortable: true,
    183 					renderer: fdate,
    184 					dataIndex: 'added'
    185 				}, {
    186 					header: _('Tracker'),
    187 					width: 120,
    188 					sortable: true,
    189 					renderer: trackerRenderer,
    190 					dataIndex: 'tracker'
    191 				}],
    192 				region: 'center',
    193 				cls: 'deluge-torrents',
    194 				stripeRows: true,
    195 				autoExpandColumn: 'name',
    196 				deferredRender:false,
    197 				autoScroll:true,
    198 				margins: '5 5 0 0'
    199 			}, config);
    200 			Ext.deluge.TorrentGrid.superclass.constructor.call(this, config);
    201 		},
    202
    203 		initComponent: function() {
    204 			Ext.deluge.TorrentGrid.superclass.initComponent.call(this);
    205 			Deluge.Events.on('torrentRemoved', this.onTorrentRemoved, this);
    206 			this.on('rowcontextmenu', function(grid, rowIndex, e) {
    207 				e.stopEvent();
    208 				var selection = grid.getSelectionModel();
    209 				if (!selection.hasSelection()) {
    210 					selection.selectRow(rowIndex);
    211 				}
    212 				Deluge.Menus.Torrent.showAt(e.getPoint());
    213 			});
    214 		},
    215
    216 		/**
    217 		 * Returns the record representing the torrent at the specified index.
    218 		 *
    219 		 * @param {int} The row index of the torrent you wish to retrieve.
    220 		 * @return {Ext.data.Record} The record representing the torrent.
    221 		 */
    222 		getTorrent: function(rowIndex) {
    223 			return this.getStore().getAt(rowIndex);
    224 		},
    225
    226 		getSelected: function() {
    227 		return this.getSelectionModel().getSelected();
    228 		},
    229
    230 		getSelections: function() {
    231 			return this.getSelectionModel().getSelections();
    232 		},
    233
    234 		update: function(torrents) {
    235 			//var torrents = [];
    236 			var store = this.getStore();
    237 			for (var torrentId in torrents) {
    238 				var record = store.getById(torrentId);
    239 				var torrent = torrents[torrentId];
    240 				if (!record) {
    241 					// We need to create a new record
    242 					var data = [
    243 						torrent.queue,
    244 						torrent.name,
    245 						torrent.total_size,
    246 						torrent.state,
    247 						torrent.progress,
    248 						torrent.num_seeds,
    249 						torrent.total_seeds,
    250 						torrent.num_peers,
    251 						torrent.total_peers,
    252 						torrent.download_payload_rate,
    253 						torrent.upload_payload_rate,
    254 						torrent.eta,
    255 						torrent.ratio,
    256 						torrent.distributed_copies,
    257 						torrent.time_added,
    258 						torrent.tracker_host,
    259 						torrentId
    260 					];
    261 					store.loadData([data], true);
    262 				} else {
    263 					// We just need to do an update
    264 					record.set('queue', torrent.queue);
    265 					record.set('name', torrent.name);
    266 					record.set('size', torrent.total_size);
    267 					record.set('state', torrent.state);
    268 					record.set('progress', torrent.progress);
    269 					record.set('seeds', torrent.num_seeds);
    270 					record.set('total_seeds', torrent.total_seeds);
    271 					record.set('peers', torrent.num_peers);
    272 					record.set('total_peers', torrent.total_peers);
    273 					record.set('downspeed', torrent.download_payload_rate);
    274 					record.set('upspeed', torrent.upload_payload_rate);
    275 					record.set('eta', torrent.eta);
    276 					record.set('ratio', torrent.ratio);
    277 					record.set('avail', torrent.distributed_copies);
    278 					record.set('added', torrent.time_added);
    279 					record.set('tracker', torrent.tracker_host);
    280 					record.commit();
    281 				}
    282 			}
    283
    284 			var torrentIds = Ext.keys(torrents);
    285 			store.each(function(record) {
    286 				if (torrentIds.indexOf(record.id) == -1) {
    287 					// Torrent is no longer in the grid so we must remove it.
    288 					store.remove(record);
    289 				}
    290 			}, this);
    291 		},
    292
    293 		// private
    294 		onTorrentRemoved: function(torrentIds) {
    295 			var selModel = this.getSelectionModel();
    296 			Ext.each(torrentIds, function(torrentId) {
    297 				var record = this.getStore().getById(torrentId);
    298 				if (selModel.isSelected(record)) {
    299 					selModel.deselectRow(this.getStore().indexOf(record));
    300 				}
    301 				this.getStore().remove(record);
    302 			}, this);
    303 		}
    304 	});
    305 	Deluge.Torrents = new Ext.deluge.TorrentGrid();
    306 })();