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 })();