Torrent._TrackerActive = 3;
-// fields whose values never change and are always known
-Torrent._StaticFields = [
- 'hashString', 'id' ];
-
-// fields whose values never change and are known upon constructon OR
-// when a magnet torrent finishes downloading its metadata
-Torrent._MetaDataFields = [
- 'addedDate', 'comment', 'creator', 'dateCreated',
- 'isPrivate', 'name', 'totalSize', 'pieceCount', 'pieceSize' ];
-
-// torrent fields whose values change all the time
-Torrent._DynamicFields = [
- 'desiredAvailable', 'downloadDir', 'downloadedEver', 'error',
- 'errorString', 'eta', 'haveUnchecked', 'haveValid', 'isFinished',
- 'leftUntilDone', 'metadataPercentComplete', 'peers', 'peersConnected',
- 'peersGettingFromUs', 'peersSendingToUs', 'queuePosition',
- 'rateDownload', 'rateUpload', 'recheckProgress', 'seedRatioLimit',
- 'seedRatioMode', 'sizeWhenDone', 'status', 'trackerStats',
- 'uploadedEver', 'uploadRatio', 'webseedsSendingToUs' ];
+Torrent.Fields = { };
+
+// commonly used fields which only need to be loaded once,
+// either on startup or when a magnet finishes downloading its metadata
+// finishes downloading its metadata
+Torrent.Fields.Metadata = [
+ 'addedDate',
+ 'name',
+ 'totalSize'
+];
+
+// commonly used fields which need to be periodically refreshed
+Torrent.Fields.Stats = [
+ 'error',
+ 'errorString',
+ 'eta',
+ 'isFinished',
+ 'isStalled',
+ 'leftUntilDone',
+ 'metadataPercentComplete',
+ 'peersConnected',
+ 'peersGettingFromUs',
+ 'peersSendingToUs',
+ 'percentDone',
+ 'queuePosition',
+ 'rateDownload',
+ 'rateUpload',
+ 'recheckProgress',
+ 'seedRatioMode',
+ 'sizeWhenDone',
+ 'status',
+ 'trackers',
+ 'uploadedEver',
+ 'uploadRatio'
+];
+
+// fields used by the inspector which only need to be loaded once
+Torrent.Fields.InfoExtra = [
+ 'comment',
+ 'creator',
+ 'dateCreated',
+ 'files',
+ 'hashString',
+ 'isPrivate',
+ 'pieceCount',
+ 'pieceSize'
+];
+
+// fields used in the inspector which need to be periodically refreshed
+Torrent.Fields.StatsExtra = [
+ 'activityDate',
+ 'desiredAvailable',
+ 'downloadDir',
+ 'downloadLimit',
+ 'downloadLimited',
+ 'downloadedEver',
+ 'fileStats',
+ 'haveUnchecked',
+ 'haveValid',
+ 'honorsSessionLimits',
+ 'manualAnnounceTime',
+ 'peer-limit',
+ 'peers',
+ 'seedIdleLimit',
+ 'seedIdleMode',
+ 'seedRatioLimit',
+ 'startDate',
+ 'torrentFile',
+ 'trackerStats',
+ 'uploadLimited',
+ 'uploadLimit',
+ 'webseedsSendingToUs'
+];
/***
****
initialize: function(data)
{
this.fields = {};
- this._files = [];
-
- // these fields are set in the ctor and never change
- for (var i=0, key; key=Torrent._StaticFields[i]; ++i) {
- if (key in data) {
- this.fields[key] = data[key];
- }
- }
-
- this.initMetaData(data);
- this._trackerStats = this.buildTrackerStats(data.trackerStats);
- this.refresh(data);
+ this.refresh (data);
},
- buildTrackerStats: function(trackerStats) {
- var announce = [];
- var result = [];
- for (var i=0, tracker; tracker=trackerStats[i]; ++i) {
- var tier = result[tracker.tier] || [];
- tier.push(tracker);
- result[tracker.tier] = tier;
- announce.push(tracker.announce);
- }
- this.fields.collatedTrackers = announce.join('\t');
- return result;
+ setField: function(o, name, value)
+ {
+ var changed = !(name in o) || (o[name] !== value);
+ if (changed)
+ o[name] = value;
+ return changed;
},
- initMetaData: function(data) {
-
- var f = this.fields;
+ // fields.files is an array of unions of RPC's "files" and "fileStats" objects.
+ updateFiles: function(files) {
var changed = false;
-
- // populate the metadata fields
- for (var i=0, key; key=Torrent._MetaDataFields[i]; ++i) {
- if (key in data) {
- if (f[key] !== data[key]) {
- f[key] = data[key];
- if (key === 'name')
- f.collatedName = data.name.toLowerCase();
- changed = true;
- }
- }
- }
-
- // populate the files array
- if (data.files) {
- for (var i=0, row; row=data.files[i]; ++i) {
- this._files[i] = {
- 'index': i,
- 'length': row.length,
- 'name': row.name
- };
- }
+ var myfiles = this.fields.files || [];
+ var keys = [ 'length', 'name', 'bytesCompleted', 'wanted', 'priority' ];
+ for (var i=0, f; f=files[i]; ++i) {
+ var myfile = myfiles[i] || {};
+ for (var j=0, key; key=keys[j]; ++j)
+ if(key in f)
+ changed |= this.setField(myfile,key,f[key]);
+ myfiles[i] = myfile;
}
-
+ this.fields.files = myfiles;
return changed;
},
- refreshMetaData: function(data)
- {
- var changed = this.initMetaData(data);
- if (changed)
- this.fireDataChanged();
- return changed;
+ collateTrackers: function(trackers) {
+ announces = [];
+ for (var i=0, t; t=trackers[i]; ++i)
+ announces.push(t.announce.toLowerCase());
+ return announces.join('\t');
},
- refresh: function(data)
+ isField: function(name) {
+ return ( name === 'id' )
+ || ( Torrent.Fields.Stats.indexOf(name) !== -1 )
+ || ( Torrent.Fields.StatsExtra.indexOf(name) !== -1 )
+ || ( Torrent.Fields.InfoExtra.indexOf(name) !== -1 )
+ || ( Torrent.Fields.Metadata.indexOf(name) !== -1 );
+ },
+
+ refreshFields: function(data)
{
var changed = false;
- // FIXME: unnecessary coupling... this should be handled by transmission.js
- if (this.needsMetaData() && (data.metadataPercentComplete >= 1))
- changed |= transmission.refreshMetaData([ this.getId() ]);
-
- var f = this.fields;
-
- // refresh the dynamic fields
- for (var i=0, key; key=Torrent._DynamicFields[i]; ++i) {
- if (key in data) {
- if (f[key] !== data[key]) {
- f[key] = data[key];
- changed = true;
- }
+ for (var key in data) {
+ if (this.isField(key)) switch (key) {
+ case 'files':
+ case 'fileStats': // merge files and fileStats together
+ changed |= this.updateFiles(data[key]);
+ break;
+ case 'trackerStats': // 'trackerStats' is a superset of 'trackers'...
+ changed |= this.setField(this.fields,'trackers',data[key]);
+ case 'trackers': // ...so only save 'trackers' if we don't have it already
+ if (!(key in this.fields))
+ changed |= this.setField(this.fields,key,data[key]);
+ break;
+ default:
+ changed |= this.setField(this.fields,key,data[key]);
}
}
- this._trackerStats = this.buildTrackerStats(data.trackerStats);
-
- if (data.fileStats)
- changed |= this.refreshFiles(data);
-
- if (changed)
- this.fireDataChanged();
- },
-
- refreshFiles: function(data) {
- var changed = false;
- for (var i=0; i<data.fileStats.length; ++i) {
- var src = data.fileStats[i];
- var tgt = this._files[i];
- if (!tgt) {
- changed = true;
- tgt = this._files[i] = { };
- }
- if (tgt.wanted !== src.wanted) {
- tgt.wanted = src.wanted;
- changed = true;
- }
- if (tgt.priority !== src.priority) {
- tgt.priority = src.priority;
- changed = true;
- }
- if (tgt.bytesCompleted !== src.bytesCompleted) {
- tgt.bytesCompleted = src.bytesCompleted;
- changed = true;
- }
- }
return changed;
},
- fireDataChanged: function()
+ refresh: function(data)
{
- $(this).trigger('dataChanged');
+ if (this.refreshFields(data))
+ $(this).trigger('dataChanged');
},
/****
getError: function() { return this.fields.error; },
getErrorString: function() { return this.fields.errorString; },
getETA: function() { return this.fields.eta; },
+ getFile: function(i) { return this.fields.files[i]; },
+ getFileCount: function() { return this.fields.files ? this.fields.files.length : 0; },
getHashString: function() { return this.fields.hashString; },
getHaveValid: function() { return this.fields.haveValid; },
getHave: function() { return this.getHaveValid() + this.fields.haveUnchecked; },
getPeersGettingFromUs: function() { return this.fields.peersGettingFromUs; },
getPeersSendingToUs: function() { return this.fields.peersSendingToUs; },
getPieceCount: function() { return this.fields.pieceCount; },
- getPieceCount: function() { return this.fields.pieceCount; },
getPieceSize: function() { return this.fields.pieceSize; },
getPrivateFlag: function() { return this.fields.isPrivate; },
getQueuePosition: function() { return this.fields.queuePosition; },
getSizeWhenDone: function() { return this.fields.sizeWhenDone; },
getStatus: function() { return this.fields.status; },
getTotalSize: function() { return this.fields.totalSize; },
+ getTrackers: function() { return this.fields.trackers; },
getUploadSpeed: function() { return this.fields.rateUpload; },
getUploadRatio: function() { return this.fields.uploadRatio; },
getUploadedEver: function() { return this.fields.uploadedEver; },
default: return 'error';
}
},
- trackerStats: function() { return this._trackerStats; },
seedRatioLimit: function(controller){
switch(this.getSeedRatioMode()) {
case Torrent._RatioUseGlobal: return controller.seedRatioLimit();
};
Torrent.compareByName = function(ta, tb)
{
- return ta.getCollatedName().compareTo(tb.getCollatedName())
- || Torrent.compareById(ta, tb);
+ var i = 0;
+ var a = ta.getCollatedName();
+ var b = tb.getCollatedName();
+ if (a && b)
+ i = a.compareTo(b);
+ if (i)
+ return i;
+ return Torrent.compareById(ta, tb);
};
Torrent.compareByQueue = function(ta, tb)
{
$('#torrent_upload_form').submit(function() { $('#upload_confirm_button').click(); return false; });
if (iPhone) {
- $('#inspector_close').bind('click', function() { tr.hideInspector(); });
+ $('#inspector_close').bind('click', function() { tr.setInspectorVisible(false); });
$('#preferences_link').bind('click', function(e) { tr.releaseClutchPreferencesButton(e); });
} else {
$(document).bind('keydown', function(e) { tr.keyDown(e); });
$(document).bind('keyup', function(e) { tr.keyUp(e); });
$('#torrent_container').click(function() { tr.deselectAll(true); });
- $('#inspector_link').click(function(e) { tr.toggleInspectorClicked(e); });
+ $('#inspector_link').click(function(e) { tr.toggleInspector(); });
this.setupSearchBox();
this.createContextMenu();
this._inspector_file_list = $('#inspector_file_list')[0];
this._inspector_peers_list = $('#inspector_peers_list')[0];
this._inspector_trackers_list = $('#inspector_trackers_list')[0];
- this._inspector_tab_files = $('#inspector_tab_files')[0];
this._toolbar_buttons = $('#toolbar ul li');
this._toolbar_pause_button = $('#toolbar #pause_selected')[0];
this._toolbar_pause_all_button = $('#toolbar #pause_all')[0];
$('#reverse_sort_order').selectMenuItem();
if (!iPhone && this[Prefs._ShowInspector])
- this.showInspector();
+ this.setInspectorVisible(true);
this.initCompactMode();
},
selectionChanged: function()
{
+ if (this[Prefs._ShowInspector])
+ this.refreshInspectorTorrents(true);
+
this.updateButtonStates();
this.updateInspector();
this.updateSelectedData();
}
},
- toggleInspectorClicked: function(ev) {
- if (this.isButtonEnabled(ev))
- this.toggleInspector();
- },
-
inspectorTabClicked: function(ev, tab) {
if (iPhone) ev.stopPropagation();
},
filesSelectAllClicked: function() {
- var t = this._files_torrent;
+ var t = this._file_torrent;
if (t)
this.toggleFilesWantedDisplay(t, true);
},
filesDeselectAllClicked: function() {
- var t = this._files_torrent;
+ var t = this._file_torrent;
if (t)
this.toggleFilesWantedDisplay(t, false);
},
toggleFilesWantedDisplay: function(torrent, wanted) {
var rows = [ ];
- for (var i=0, row; row=this._files[i]; ++i)
- if (row.isEditable() && (torrent._files[i].wanted !== wanted))
+ for (var i=0, row; row=this._file_rows[i]; ++i)
+ if (row.isEditable() && (torrent.getFile(i).wanted !== wanted))
rows.push(row);
if (rows.length > 0) {
var command = wanted ? 'files-wanted' : 'files-unwanted';
var torrents = this.getSelectedTorrents();
if (!torrents.length && iPhone) {
- this.hideInspector();
+ this.setInspectorVisible(false);
return;
}
}
this.changeFileCommand(command, [ row ]);
},
- clearFileList: function() {
+ clearFileList: function()
+ {
$(this._inspector_file_list).empty();
- delete this._files_torrent;
- delete this._files;
+ delete this._file_torrent;
+ delete this._file_rows;
},
- updateFileList: function() {
-
- // if the file list is hidden, clear the list
- if (this._inspector_tab_files.className.indexOf('selected') == -1) {
- this.clearFileList();
+ updateFileList: function()
+ {
+ if (!$(this._inspector_file_list).is(':visible'))
return;
- }
- // if not torrent is selected, clear the list
- var selected_torrents = this.getSelectedTorrents();
- if (selected_torrents.length != 1) {
+ var sel = this.getSelectedTorrents();
+ if (sel.length !== 1) {
this.clearFileList();
return;
}
- // if the active torrent hasn't changed, noop
- var torrent = selected_torrents[0];
- if (this._files_torrent === torrent)
- return;
+ var torrent = sel[0];
+ if (torrent === this._files_torrent)
+ if(torrent.getFileCount() === (this._files ? this._files.length: 0))
+ return;
// build the file list
this.clearFileList();
- this._files_torrent = torrent;
- var n = torrent._files.length;
- this._files = new Array(n);
+ this._file_torrent = torrent;
+ var n = torrent.getFileCount();
+ this._file_rows = [];
var fragment = document.createDocumentFragment();
var tr = this;
for (var i=0; i<n; ++i) {
- var row = new FileRow(torrent, i);
+ var row = this._file_rows[i] = new FileRow(torrent, i);
fragment.appendChild(row.getElement());
- this._files[i] = row;
$(row).bind('wantedToggled',function(e,row,want) {tr.onFileWantedToggled(row,want);});
$(row).bind('priorityToggled',function(e,row,priority) {tr.onFilePriorityToggled(row,priority);});
}
this._inspector_file_list.appendChild(fragment);
},
- refreshFileView: function() {
- for (var i=0, row; row=this._files[i]; ++i)
- row.refresh();
- },
+ updatePeersLists: function()
+ {
+ if (!$(this._inspector_peers_list).is(':visible'))
+ return;
- updatePeersLists: function() {
var html = [ ];
var fmt = Transmission.fmt;
var torrents = this.getSelectedTorrents();
- if ($(this._inspector_peers_list).is(':visible')) {
- for (var k=0, torrent; torrent=torrents[k]; ++k) {
- var peers = torrent.getPeers();
- html.push('<div class="inspector_group">');
- if (torrents.length > 1) {
- html.push('<div class="inspector_torrent_label">', torrent.getName(), '</div>');
- }
- if (peers.length == 0) {
- html.push('<br></div>'); // firefox won't paint the top border if the div is empty
- continue;
- }
- html.push('<table class="peer_list">',
- '<tr class="inspector_peer_entry even">',
- '<th class="encryptedCol"></th>',
- '<th class="upCol">Up</th>',
- '<th class="downCol">Down</th>',
- '<th class="percentCol">%</th>',
- '<th class="statusCol">Status</th>',
- '<th class="addressCol">Address</th>',
- '<th class="clientCol">Client</th>',
- '</tr>');
- for (var i=0, peer; peer=peers[i]; ++i) {
- var parity = ((i+1) % 2 == 0 ? 'even' : 'odd');
- html.push('<tr class="inspector_peer_entry ', parity, '">',
- '<td>', (peer.isEncrypted ? '<img src="images/graphics/lock_icon.png" alt="Encrypted"/>' : ''), '</td>',
- '<td>', (peer.rateToPeer ? fmt.speedBps(peer.rateToPeer) : ''), '</td>',
- '<td>', (peer.rateToClient ? fmt.speedBps(peer.rateToClient) : ''), '</td>',
- '<td class="percentCol">', Math.floor(peer.progress*100), '%', '</td>',
- '<td>', fmt.peerStatus(peer.flagStr), '</td>',
- '<td>', peer.address, '</td>',
- '<td class="clientCol">', peer.clientName, '</td>',
- '</tr>');
- }
- html.push('</table></div>');
+
+ for (var k=0, torrent; torrent=torrents[k]; ++k) {
+ var peers = torrent.getPeers();
+ html.push('<div class="inspector_group">');
+ if (torrents.length > 1) {
+ html.push('<div class="inspector_torrent_label">', torrent.getName(), '</div>');
+ }
+ if (!peers || !peers.length) {
+ html.push('<br></div>'); // firefox won't paint the top border if the div is empty
+ continue;
}
+ html.push('<table class="peer_list">',
+ '<tr class="inspector_peer_entry even">',
+ '<th class="encryptedCol"></th>',
+ '<th class="upCol">Up</th>',
+ '<th class="downCol">Down</th>',
+ '<th class="percentCol">%</th>',
+ '<th class="statusCol">Status</th>',
+ '<th class="addressCol">Address</th>',
+ '<th class="clientCol">Client</th>',
+ '</tr>');
+ for (var i=0, peer; peer=peers[i]; ++i) {
+ var parity = ((i+1) % 2 == 0 ? 'even' : 'odd');
+ html.push('<tr class="inspector_peer_entry ', parity, '">',
+ '<td>', (peer.isEncrypted ? '<img src="images/graphics/lock_icon.png" alt="Encrypted"/>' : ''), '</td>',
+ '<td>', (peer.rateToPeer ? fmt.speedBps(peer.rateToPeer) : ''), '</td>',
+ '<td>', (peer.rateToClient ? fmt.speedBps(peer.rateToClient) : ''), '</td>',
+ '<td class="percentCol">', Math.floor(peer.progress*100), '%', '</td>',
+ '<td>', fmt.peerStatus(peer.flagStr), '</td>',
+ '<td>', peer.address, '</td>',
+ '<td class="clientCol">', peer.clientName, '</td>',
+ '</tr>');
+ }
+ html.push('</table></div>');
}
+
setInnerHTML(this._inspector_peers_list, html.join(''));
},
updateTrackersLists: function() {
- // By building up the HTML as as string, then have the browser
- // turn this into a DOM tree, this is a fast operation.
+ if (!$(this._inspector_trackers_list).is(':visible'))
+ return;
+
var tr = this;
var html = [ ];
var na = 'N/A';
var torrents = this.getSelectedTorrents();
- if ($(this._inspector_trackers_list).is(':visible')) {
- for (var k=0, torrent; torrent = torrents[k]; ++k) {
- html.push ('<div class="inspector_group">');
- if (torrents.length > 1) {
- html.push('<div class="inspector_torrent_label">', torrent.getName(), '</div>');
- }
- for (var i=0, tier; tier=torrent._trackerStats[i]; ++i) {
+
+ // By building up the HTML as as string, then have the browser
+ // turn this into a DOM tree, this is a fast operation.
+ for (var i=0, torrent; torrent=torrents[i]; ++i)
+ {
+ html.push ('<div class="inspector_group">');
+
+ if (torrents.length > 1)
+ html.push('<div class="inspector_torrent_label">', torrent.getName(), '</div>');
+
+ var tier = -1;
+ var trackers = torrent.getTrackers();
+ for (var j=0, tracker; tracker=trackers[j]; ++j)
+ {
+ if (tier != tracker.tier)
+ {
+ if (tier !== -1) // close previous tier
+ html.push('</ul></div>');
+
+ tier = tracker.tier;
+
html.push('<div class="inspector_group_label">',
- 'Tier ', (i + 1), '</div>',
- '<ul class="tier_list">');
- for (var j=0, tracker; tracker=tier[j]; ++j) {
- var lastAnnounceStatusHash = tr.lastAnnounceStatus(tracker);
- var announceState = tr.announceState(tracker);
- var lastScrapeStatusHash = tr.lastScrapeStatus(tracker);
-
- // Display construction
- var parity = ((j+1) % 2 == 0 ? 'even' : 'odd');
- html.push('<li class="inspector_tracker_entry ', parity, '"><div class="tracker_host" title="', tracker.announce, '">',
- tracker.host, '</div>',
- '<div class="tracker_activity">',
- '<div>', lastAnnounceStatusHash['label'], ': ', lastAnnounceStatusHash['value'], '</div>',
- '<div>', announceState, '</div>',
- '<div>', lastScrapeStatusHash['label'], ': ', lastScrapeStatusHash['value'], '</div>',
- '</div><table class="tracker_stats">',
- '<tr><th>Seeders:</th><td>', (tracker.seederCount > -1 ? tracker.seederCount : na), '</td></tr>',
- '<tr><th>Leechers:</th><td>', (tracker.leecherCount > -1 ? tracker.leecherCount : na), '</td></tr>',
- '<tr><th>Downloads:</th><td>', (tracker.downloadCount > -1 ? tracker.downloadCount : na), '</td></tr>',
- '</table></li>');
- }
- html.push('</ul>');
+ 'Tier ', tier, '</div>',
+ '<ul class="tier_list">');
}
- html.push('</div>');
+
+ var lastAnnounceStatusHash = tr.lastAnnounceStatus(tracker);
+ var announceState = tr.announceState(tracker);
+ var lastScrapeStatusHash = tr.lastScrapeStatus(tracker);
+
+ // Display construction
+ var parity = ((j+1) % 2 == 0 ? 'even' : 'odd');
+ html.push('<li class="inspector_tracker_entry ', parity, '"><div class="tracker_host" title="', tracker.announce, '">',
+ tracker.host, '</div>',
+ '<div class="tracker_activity">',
+ '<div>', lastAnnounceStatusHash['label'], ': ', lastAnnounceStatusHash['value'], '</div>',
+ '<div>', announceState, '</div>',
+ '<div>', lastScrapeStatusHash['label'], ': ', lastScrapeStatusHash['value'], '</div>',
+ '</div><table class="tracker_stats">',
+ '<tr><th>Seeders:</th><td>', (tracker.seederCount > -1 ? tracker.seederCount : na), '</td></tr>',
+ '<tr><th>Leechers:</th><td>', (tracker.leecherCount > -1 ? tracker.leecherCount : na), '</td></tr>',
+ '<tr><th>Downloads:</th><td>', (tracker.downloadCount > -1 ? tracker.downloadCount : na), '</td></tr>',
+ '</table></li>');
}
+ if (tier !== -1) // close last tier
+ html.push('</ul></div>');
+
+ html.push('</div>'); // inspector_group
}
+
setInnerHTML(this._inspector_trackers_list, html.join(''));
},
return {'label':lastScrapeLabel, 'value':lastScrape};
},
- /*
- * Toggle the visibility of the inspector (used by the context menu)
- */
- toggleInspector: function() {
- if (this[Prefs._ShowInspector])
- this.hideInspector();
- else
- this.showInspector();
+ toggleInspector: function()
+ {
+ this.setInspectorVisible(!this[Prefs._ShowInspector]);
},
-
- showInspector: function() {
- $('#torrent_inspector').show();
- if (iPhone) {
- $('body').addClass('inspector_showing');
- $('#inspector_close').show();
- this.hideiPhoneAddressbar();
- } else {
- var w = $('#torrent_inspector').width() + 1 + 'px';
- $('#torrent_container')[0].style.right = w;
+ setInspectorVisible: function(visible)
+ {
+ // we collect extra stats on torrents when they're in the inspector...
+ clearInterval(this._periodic_inspector_refresh);
+ delete this._periodic_inspector_refresh;
+ if (visible) {
+ var tr = this;
+ this._periodic_inspector_refresh = setInterval(function() {tr.refreshInspectorTorrents(false);},2000);
+ this.refreshInspectorTorrents(true);
}
- setInnerHTML($('ul li#context_toggle_inspector')[0], 'Hide Inspector');
-
- this.setPref(Prefs._ShowInspector, true);
- this.updateInspector();
- },
-
- /*
- * Hide the inspector
- */
- hideInspector: function() {
-
- $('#torrent_inspector').hide();
-
+ // update the ui widgetry
+ $('#torrent_inspector').toggle(visible);
if (iPhone) {
- this.deselectAll();
- $('body.inspector_showing').removeClass('inspector_showing');
- $('#inspector_close').hide();
+ $('body').toggleClass('inspector_showing',visible);
+ $('#inspector_close').toggle(visible);
this.hideiPhoneAddressbar();
} else {
- $('#torrent_container')[0].style.right = '0px';
- setInnerHTML($('ul li#context_toggle_inspector')[0], 'Show Inspector');
+ var w = visible ? $('#torrent_inspector').width() + 1 + 'px' : '0px';
+ $('#torrent_container')[0].style.right = w;
}
- this.setPref(Prefs._ShowInspector, false);
- },
-
- refreshMetaData: function(ids) {
- var tr = this;
- this.remote.getMetaDataFor(ids, function(active) { tr.updateMetaData(active); });
+ setInnerHTML($('ul li#context_toggle_inspector')[0], (visible?'Hide':'Show')+' Inspector');
+ this.setPref(Prefs._ShowInspector, visible);
+ if (visible)
+ this.updateInspector();
},
- updateMetaData: function(torrents)
+ onTorrentChanged: function(ev)
{
- var tr = this;
- var refresh_files_for = [ ];
- var selected_torrents = this.getSelectedTorrents();
- jQuery.each(torrents, function() {
- var t = tr._torrents[ this.id ];
- if (t) {
- t.refreshMetaData(this);
- if (selected_torrents.indexOf(t) != -1)
- refresh_files_for.push(t.getId());
- }
- });
- if (refresh_files_for.length > 0)
- tr.remote.loadTorrentFiles(refresh_files_for);
+ this.refilterSoon();
+
+ // if this torrent is in the inspector, refresh the inspector
+ if (this[Prefs._ShowInspector])
+ if (this.getSelectedTorrentIds().indexOf(ev.target.getId()) !== -1)
+ this.updateInspector();
},
- refreshTorrents: function(ids) {
- var tr = this;
- if (!ids)
- ids = 'recently-active';
-
- this.remote.getUpdatedDataFor(ids, function(active, removed) { tr.updateTorrentsData(active, removed); });
- },
+ updateFromTorrentGet: function(updates, removed_ids)
+ {
+ var new_ids = [];
- updateTorrentsData: function(updated, removed_ids) {
- var tr = this;
- var new_torrent_ids = [];
- var refresh_files_for = [];
- var selected_torrents = this.getSelectedTorrents();
-
- for (var i=0, o; o=updated[i]; ++i) {
- var t = tr._torrents[o.id];
- if (t == null)
- new_torrent_ids.push(o.id);
- else {
+ for (var i=0, o; o=updates[i]; ++i) {
+ var t;
+ var id = o.id;
+ if ((t = this._torrents[id]))
t.refresh(o);
- if (selected_torrents.indexOf(t) != -1)
- refresh_files_for.push(t.getId());
+ else {
+ t = this._torrents[id] = new Torrent(o);
+ $(t).bind('dataChanged',function(ev) {tr.onTorrentChanged(ev);});
+ new_ids.push(id);
}
}
- if (refresh_files_for.length > 0)
- tr.remote.loadTorrentFiles(refresh_files_for);
-
- if (new_torrent_ids.length > 0)
- tr.remote.getInitialDataFor(new_torrent_ids, function(torrents) {tr.addTorrents(torrents);});
-
- tr.deleteTorrents(removed_ids);
-
- if (new_torrent_ids.length != 0) {
- tr.hideiPhoneAddressbar();
- tr.deselectAll(true);
+ if (new_ids.length) {
+ var tr = this;
+ this.remote.getTorrentInitial(new_ids, function(a,b){tr.updateFromTorrentGet(a,b);});
+ this.refilterSoon();
}
- },
- updateTorrentsFileData: function(torrents) {
- for (var i=0, o; o=torrents[i]; ++i) {
- var t = this._torrents[o.id];
- if (t) {
- t.refreshFiles(o);
- if (t === this._files_torrent)
- this.refreshFileView();
- }
+ if (removed_ids) {
+ this.deleteTorrents(removed_ids);
+ this.refilterSoon();
}
},
+ refreshTorrents: function(ids) {
+ if (!ids)
+ ids = 'recently-active';
+ var tr = this;
+ this.remote.getTorrentStats(ids, function(a,b){tr.updateFromTorrentGet(a,b);});
+ },
initializeAllTorrents: function() {
var tr = this;
- this.remote.getInitialDataFor(null ,function(torrents) { tr.addTorrents(torrents); });
+ this.remote.getTorrentInitial(null, function(a,b){tr.updateFromTorrentGet(a,b);});
+ },
+ refreshMetadata: function(ids) {
+ var tr = this;
+ this.remote.getTorrentMetadata(ids, function(a,b){tr.updateFromTorrentGet(a,b);});
+ },
+ refreshInspectorTorrents: function(full) {
+ var tr = this;
+ var ids = tr.getSelectedTorrentIds();
+ if (ids.length > 0)
+ this.remote.getTorrentDetails(ids, full, function(a,b){tr.updateFromTorrentGet(a,b);});
},
onRowClicked: function(ev, row)
// Shift-Click - selects a range from the last-clicked row to this one
if (iPhone) {
if (row.isSelected())
- this.showInspector();
+ this.setInspectorVisible(true);
this.setSelectedRow(row);
} else if (ev.shiftKey) {
this._last_torrent_clicked = row.getTorrent().getId();
},
- addTorrents: function(new_torrents)
- {
- var tr = this;
- var key = 'dataChanged';
-
- for (var i=0, row; row=new_torrents[i]; ++i) {
- var t = new Torrent(row);
- $(t).bind(key,function() {tr.refilterSoon();});
- this._torrents[t.getId()] = t;
- }
-
- this.refilterSoon();
- },
-
deleteTorrents: function(torrent_ids)
{
if (torrent_ids && torrent_ids.length)
getTrackers: function()
{
- var trackers = {};
+ var ret = {};
var torrents = this.getAllTorrents();
for (var i=0, torrent; torrent=torrents[i]; ++i) {
var names = [];
- for (var j=0, tier; tier=torrent._trackerStats[j]; ++j) {
- for (var k=0, tracker; tracker=tier[k]; ++k) {
- var uri = parseUri(tracker.announce);
- var domain = this.getDomainName(uri.host);
- var name = this.getReadableDomain(domain);
- if (!(name in trackers))
- trackers[name] = { 'uri': uri, 'domain': domain, 'count': 0 };
- if (names.indexOf(name) === -1)
- names.push(name);
- }
+ var trackers = torrent.getTrackers();
+ for (var j=0, tracker; tracker=trackers[j]; ++j) {
+ var uri = parseUri(tracker.announce);
+ var domain = this.getDomainName(uri.host);
+ var name = this.getReadableDomain(domain);
+ if (!(name in ret))
+ ret[name] = { 'uri': uri, 'domain': domain, 'count': 0 };
+ if (names.indexOf(name) === -1)
+ names.push(name);
}
for (var j=0, name; name=names[j]; ++j)
- trackers[name].count++;
+ ret[name].count++;
}
- return trackers;
+ return ret;
},
/***