From e081ecbd12be58a9c19d6d98a8505870bed5b114 Mon Sep 17 00:00:00 2001 From: Jordan Lee Date: Fri, 26 Aug 2011 01:46:07 +0000 Subject: [PATCH] (trunk web) minor code sorting in Transmission.js: group the inspector functions together --- web/javascript/transmission.js | 1403 ++++++++++++++++---------------- 1 file changed, 705 insertions(+), 698 deletions(-) diff --git a/web/javascript/transmission.js b/web/javascript/transmission.js index eb3f99c8e..a8efeff27 100644 --- a/web/javascript/transmission.js +++ b/web/javascript/transmission.js @@ -721,21 +721,6 @@ Transmission.prototype = } }, - inspectorTabClicked: function(ev, tab) { - if (iPhone) ev.stopPropagation(); - - // select this tab and deselect the others - $(tab).addClass('selected').siblings().removeClass('selected'); - - // show this tab and hide the others - $('#'+tab.id+'_container').show().siblings('.inspector_container').hide(); - - this.hideiPhoneAddressbar(); - this.updatePeersLists(); - this.updateTrackersLists(); - this.updateFileList(); - }, - filesSelectAllClicked: function() { var t = this._file_torrent; if (t) @@ -1131,761 +1116,359 @@ Transmission.prototype = return false; // to prevent the event from bubbling up }, - /* - * Update the inspector with the latest data for the selected torrents - */ - updateInspector: function() - { - if (!this[Prefs._ShowInspector]) - return; - var torrents = this.getSelectedTorrents(); - if (!torrents.length && iPhone) { - this.setInspectorVisible(false); - return; - } + onTorrentChanged: function(ev) + { + 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(); + }, - var creator = 'N/A'; - var comment = 'N/A'; - var download_dir = 'N/A'; - var date_created = 'N/A'; - var error = 'None'; - var hash = 'N/A'; - var have_public = false; - var have_private = false; - var name; - var sizeWhenDone = 0; - var sizeDone = 0; - var total_completed = 0; - var total_download = 0; - var total_download_peers = 0; - var total_download_speed = 0; - var total_availability = 0; - var total_size = 0; - var total_state = [ ]; - var pieces = 'N/A'; - var total_upload = 0; - var total_upload_peers = 0; - var total_upload_speed = 0; - var total_verified = 0; - var na = 'N/A'; - var tab = this._inspector._info_tab; + updateFromTorrentGet: function(updates, removed_ids) + { + var new_ids = []; - $("#torrent_inspector_size, .inspector_row div").css('color', '#222'); + for (var i=0, o; o=updates[i]; ++i) { + var t; + var id = o.id; + if ((t = this._torrents[id])) + t.refresh(o); + else { + t = this._torrents[id] = new Torrent(o); + $(t).bind('dataChanged',function(ev) {tr.onTorrentChanged(ev);}); + new_ids.push(id); + } + } - if (torrents.length == 0) - { - setInnerHTML(tab.name, 'No Selection'); - setInnerHTML(tab.size, na); - setInnerHTML(tab.pieces, na); - setInnerHTML(tab.hash, na); - setInnerHTML(tab.state, na); - setInnerHTML(tab.download_speed, na); - setInnerHTML(tab.upload_speed, na); - setInnerHTML(tab.uploaded, na); - setInnerHTML(tab.downloaded, na); - setInnerHTML(tab.availability, na); - setInnerHTML(tab.ratio, na); - setInnerHTML(tab.have, na); - setInnerHTML(tab.upload_to, na); - setInnerHTML(tab.download_from, na); - setInnerHTML(tab.secure, na); - setInnerHTML(tab.creator_date, na); - setInnerHTML(tab.progress, na); - setInnerHTML(tab.comment, na); - setInnerHTML(tab.creator, na); - setInnerHTML(tab.download_dir, na); - setInnerHTML(tab.error, na); - this.updateFileList(); - this.updatePeersLists(); - this.updateTrackersLists(); - $("#torrent_inspector_size, .inspector_row > div:contains('N/A')").css('color', '#666'); - return; + if (new_ids.length) { + var tr = this; + this.remote.getTorrentInitial(new_ids, function(a,b){tr.updateFromTorrentGet(a,b);}); + this.refilterSoon(); } - name = torrents.length == 1 - ? torrents[0].getName() - : torrents.length+' Transfers Selected'; + if (removed_ids) { + this.deleteTorrents(removed_ids); + this.refilterSoon(); + } + }, - if (torrents.length == 1) - { - var text; - var t = torrents[0]; - var err = t.getErrorMessage(); - if (err) - error = err; - if ((text = t.getComment())) - comment = text; - if ((text = t.getCreator())) - creator = text; - if ((text = t.getDownloadDir())) - download_dir = text; + 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.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);}); + }, - hash = t.getHashString(); - pieces = [ t.getPieceCount(), 'pieces @', Transmission.fmt.mem(t.getPieceSize()) ].join(' '); - date_created = Transmission.fmt.timestamp(t.getDateCreated()); - } + onRowClicked: function(ev, row) + { + // Prevents click carrying to parent element + // which deselects all on click + ev.stopPropagation(); + // but still hide the context menu if it is showing + $('#jqContextMenu').hide(); - for (var i=0, t; t=torrents[i]; ++i) { - var l = t.getLeftUntilDone(); - var d = t.getSizeWhenDone(); - sizeWhenDone += d; - sizeDone += d - l; - total_completed += t.getHave(); - total_verified += t.getHaveValid(); - total_size += t.getTotalSize(); - total_upload += t.getUploadedEver(); - total_download += t.getDownloadedEver(); - total_upload_speed += t.getUploadSpeed(); - total_download_speed += t.getDownloadSpeed(); - total_upload_peers += t.getPeersGettingFromUs(); - total_download_peers += t.getPeersSendingToUs(); - total_availability += sizeWhenDone - l + t.getDesiredAvailable(); + // 'Apple' button emulation on PC : + // Need settable meta-key and ctrl-key variables for mac emulation + var meta_key = ev.metaKey; + if (ev.ctrlKey && navigator.appVersion.toLowerCase().indexOf("mac") == -1) + meta_key = true; - var s = t.getStateString(); - if (total_state.indexOf(s) == -1) - total_state.push(s); + // Shift-Click - selects a range from the last-clicked row to this one + if (iPhone) { + if (row.isSelected()) + this.setInspectorVisible(true); + this.setSelectedRow(row); - if (t.getPrivateFlag()) - have_private = true; - else - have_public = true; - } + } else if (ev.shiftKey) { + this.selectRange(row); + // Need to deselect any selected text + window.focus(); - var private_string = ''; - var fmt = Transmission.fmt; - if (have_private && have_public) private_string = 'Mixed'; - else if (have_private) private_string = 'Private Torrent'; - else if (have_public) private_string = 'Public Torrent'; + // Apple-Click, not selected + } else if (!row.isSelected() && meta_key) { + this.selectRow(row); - setInnerHTML(tab.name, name); - setInnerHTML(tab.size, torrents.length ? fmt.size(total_size) : na); - setInnerHTML(tab.pieces, pieces); - setInnerHTML(tab.hash, hash); - setInnerHTML(tab.state, total_state.join('/')); - setInnerHTML(tab.download_speed, torrents.length ? fmt.speedBps(total_download_speed) : na); - setInnerHTML(tab.upload_speed, torrents.length ? fmt.speedBps(total_upload_speed) : na); - setInnerHTML(tab.uploaded, torrents.length ? fmt.size(total_upload) : na); - setInnerHTML(tab.downloaded, torrents.length ? fmt.size(total_download) : na); - setInnerHTML(tab.availability, torrents.length ? fmt.percentString(Math.ratio(total_availability*100, sizeWhenDone)) + '%' : na); - setInnerHTML(tab.ratio, torrents.length ? fmt.ratioString(Math.ratio(total_upload, total_download)) : na); - setInnerHTML(tab.have, torrents.length ? fmt.size(total_completed) + ' (' + fmt.size(total_verified) + ' verified)' : na); - setInnerHTML(tab.upload_to, torrents.length ? total_upload_peers : na); - setInnerHTML(tab.download_from, torrents.length ? total_download_peers : na); - setInnerHTML(tab.secure, private_string); - setInnerHTML(tab.creator_date, date_created); - setInnerHTML(tab.progress, torrents.length ? fmt.percentString(Math.ratio(sizeDone*100, sizeWhenDone)) + '%' : na); - setInnerHTML(tab.comment, comment); - setInnerHTML(tab.creator, creator); - setInnerHTML(tab.download_dir, download_dir); - setInnerHTML(tab.error, error); + // Regular Click, not selected + } else if (!row.isSelected()) { + this.setSelectedRow(row); - this.updatePeersLists(); - this.updateTrackersLists(); - $(".inspector_row > div:contains('N/A')").css('color', '#666'); - this.updateFileList(); - }, + // Apple-Click, selected + } else if (row.isSelected() && meta_key) { + this.deselectRow(row); - onFileWantedToggled: function(row, want) { - var command = want ? 'files-wanted' : 'files-unwanted'; - this.changeFileCommand(command, [ row ]); - }, - onFilePriorityToggled: function(row, priority) { - var command; - switch(priority) { - case -1: command = 'priority-low'; break; - case 1: command = 'priority-high'; break; - default: command = 'priority-normal'; break; + // Regular Click, selected + } else if (row.isSelected()) { + this.setSelectedRow(row); } - this.changeFileCommand(command, [ row ]); + + this._last_torrent_clicked = row.getTorrentId(); }, - clearFileList: function() + + deleteTorrents: function(torrent_ids) { - $(this._inspector_file_list).empty(); - delete this._file_torrent; - delete this._file_rows; - }, - updateFileList: function() - { - if (!$(this._inspector_file_list).is(':visible')) - return; - - var sel = this.getSelectedTorrents(); - if (sel.length !== 1) { - this.clearFileList(); - return; + if (torrent_ids && torrent_ids.length) + { + for (var i=0, id; id=torrent_ids[i]; ++i) + delete this._torrents[id]; + this.refilter(); } + }, - var torrent = sel[0]; - if (torrent === this._files_torrent) - if(torrent.getFileCount() === (this._files ? this._files.length: 0)) - return; + updateStatusbar: function() + { + this.refreshFilterButton(); - // build the file list - this.clearFileList(); - this._file_torrent = torrent; - var n = torrent.getFileCount(); - this._file_rows = []; - var fragment = document.createDocumentFragment(); - var tr = this; - for (var i=0; i'); - if (torrents.length > 1) { - html.push('
', torrent.getName(), '
'); - } - if (!peers || !peers.length) { - html.push('
'); // firefox won't paint the top border if the div is empty - continue; + // Display the upload dialog + if (! confirmed) { + $('input#torrent_upload_file').attr('value', ''); + $('input#torrent_upload_url').attr('value', ''); + $('input#torrent_auto_start').attr('checked', $('#prefs_form #auto_start')[0].checked); + $('#upload_container').show(); + $('#torrent_upload_url').focus(); + if (!iPhone && Safari3) { + setTimeout("$('div#upload_container div.dialog_window').css('top', '0px');",10); } - html.push('', - '', - '', - '', - '', - '', - '', - '', - '', - ''); - for (var i=0, peer; peer=peers[i]; ++i) { - var parity = ((i+1) % 2 == 0 ? 'even' : 'odd'); - html.push('', - '', - '', - '', - '', - '', - '', - '', - ''); + + // Submit the upload form + } else { + var tr = this; + var args = { }; + var paused = !$('#torrent_auto_start').is(':checked'); + if ('' != $('#torrent_upload_url').val()) { + tr.remote.addTorrentByUrl($('#torrent_upload_url').val(), { paused: paused }); + } else { + args.url = '../upload?paused=' + paused; + args.type = 'POST'; + args.data = { 'X-Transmission-Session-Id' : tr.remote._token }; + args.dataType = 'xml'; + args.iframe = true; + args.success = function() { + tr.refreshTorrents(); + tr.togglePeriodicRefresh(true); + }; + tr.togglePeriodicRefresh(false); + $('#torrent_upload_form').ajaxSubmit(args); } - html.push('
UpDown%StatusAddressClient
', (peer.isEncrypted ? 'Encrypted' : ''), '', (peer.rateToPeer ? fmt.speedBps(peer.rateToPeer) : ''), '', (peer.rateToClient ? fmt.speedBps(peer.rateToClient) : ''), '', Math.floor(peer.progress*100), '%', '', fmt.peerStatus(peer.flagStr), '', peer.address, '', peer.clientName, '
'); } - - setInnerHTML(this._inspector_peers_list, html.join('')); }, - updateTrackersLists: function() { - if (!$(this._inspector_trackers_list).is(':visible')) - return; + removeSelectedTorrents: function() { + var torrents = this.getSelectedTorrents(); + if (torrents.length) + this.promptToRemoveTorrents(torrents); + }, - var tr = this; - var html = [ ]; - var na = 'N/A'; + removeSelectedTorrentsAndData: function() { var torrents = this.getSelectedTorrents(); + if (torrents.length) + this.promptToRemoveTorrentsAndData(torrents); + }, - // 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) + promptToRemoveTorrents:function(torrents) + { + if (torrents.length == 1) { - html.push ('
'); + var torrent = torrents[0]; + var header = 'Remove ' + torrent.getName() + '?'; + var message = 'Once removed, continuing the transfer will require the torrent file. Are you sure you want to remove it?'; + dialog.confirm(header, message, 'Remove', 'transmission.removeTorrents', torrents); + } + else + { + var header = 'Remove ' + torrents.length + ' transfers?'; + var message = 'Once removed, continuing the transfers will require the torrent files. Are you sure you want to remove them?'; + dialog.confirm(header, message, 'Remove', 'transmission.removeTorrents', torrents); + } + }, - if (torrents.length > 1) - html.push('
', torrent.getName(), '
'); + promptToRemoveTorrentsAndData:function(torrents) + { + if (torrents.length == 1) + { + var torrent = torrents[0], + header = 'Remove ' + torrent.getName() + ' and delete data?', + message = 'All data downloaded for this torrent will be deleted. Are you sure you want to remove it?'; + dialog.confirm(header, message, 'Remove', 'transmission.removeTorrentsAndData', torrents); + } + else + { + var header = 'Remove ' + torrents.length + ' transfers and delete data?', + message = 'All data downloaded for these torrents will be deleted. Are you sure you want to remove them?'; + dialog.confirm(header, message, 'Remove', 'transmission.removeTorrentsAndData', torrents); + } + }, - 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('
'); - - tier = tracker.tier; + removeTorrents: function(torrents) { + var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); + var tr = this; + this.remote.removeTorrents(torrent_ids, function() { tr.refreshTorrents();}); + }, - html.push('
', - 'Tier ', tier, '
', - ''); + reannounceSelectedTorrents: function() { + this.reannounceTorrents(this.getSelectedTorrents()); + }, - html.push(''); // inspector_group - } + startSelectedTorrents: function(force) { + this.startTorrents(this.getSelectedTorrents(), force); + }, + startAllTorrents: function() { + this.startTorrents(this.getAllTorrents(), false); + }, + startTorrent: function(torrent) { + this.startTorrents([ torrent ], false); + }, + startTorrents: function(torrents, force) { + var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); + var tr = this; + this.remote.startTorrents(torrent_ids, force, function() { tr.refreshTorrents(torrent_ids); }); + }, + verifyTorrent: function(torrent) { + this.verifyTorrents([ torrent ]); + }, + verifyTorrents: function(torrents) { + var tr = this; + var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); + this.remote.verifyTorrents(torrent_ids, function() { tr.refreshTorrents(torrent_ids); }); + }, - setInnerHTML(this._inspector_trackers_list, html.join('')); + reannounceTorrent: function(torrent) { + this.reannounceTorrents([ torrent ]); + }, + reannounceTorrents: function(torrents) { + var tr = this; + var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); + this.remote.reannounceTorrents(torrent_ids, function() { tr.refreshTorrents(torrent_ids); }); }, - lastAnnounceStatus: function(tracker) { - var lastAnnounceLabel = 'Last Announce'; - var lastAnnounce = [ 'N/A' ]; - if (tracker.hasAnnounced) { - var lastAnnounceTime = Transmission.fmt.timestamp(tracker.lastAnnounceTime); - if (tracker.lastAnnounceSucceeded) { - lastAnnounce = [ lastAnnounceTime, ' (got ', Transmission.fmt.plural(tracker.lastAnnouncePeerCount, 'peer'), ')' ]; - } else { - lastAnnounceLabel = 'Announce error'; - lastAnnounce = [ (tracker.lastAnnounceResult ? (tracker.lastAnnounceResult + ' - ') : ''), lastAnnounceTime ]; - } - } - return { 'label':lastAnnounceLabel, 'value':lastAnnounce.join('') }; - }, - - announceState: function(tracker) { - var announceState = ''; - switch (tracker.announceState) { - case Torrent._TrackerActive: - announceState = 'Announce in progress'; - break; - case Torrent._TrackerWaiting: - var timeUntilAnnounce = tracker.nextAnnounceTime - ((new Date()).getTime() / 1000); - if (timeUntilAnnounce < 0) { - timeUntilAnnounce = 0; - } - announceState = 'Next announce in ' + Transmission.fmt.timeInterval(timeUntilAnnounce); - break; - case Torrent._TrackerQueued: - announceState = 'Announce is queued'; - break; - case Torrent._TrackerInactive: - announceState = tracker.isBackup ? - 'Tracker will be used as a backup' : - 'Announce not scheduled'; - break; - default: - announceState = 'unknown announce state: ' + tracker.announceState; - } - return announceState; + stopSelectedTorrents: function() { + this.stopTorrents(this.getSelectedTorrents()); }, - - lastScrapeStatus: function(tracker) { - var lastScrapeLabel = 'Last Scrape'; - var lastScrape = 'N/A'; - if (tracker.hasScraped) { - var lastScrapeTime = Transmission.fmt.timestamp(tracker.lastScrapeTime); - if (tracker.lastScrapeSucceeded) { - lastScrape = lastScrapeTime; - } else { - lastScrapeLabel = 'Scrape error'; - lastScrape = (tracker.lastScrapeResult ? tracker.lastScrapeResult + ' - ' : '') + lastScrapeTime; - } - } - return {'label':lastScrapeLabel, 'value':lastScrape}; + stopAllTorrents: function() { + this.stopTorrents(this.getAllTorrents()); }, - - toggleInspector: function() - { - this.setInspectorVisible(!this[Prefs._ShowInspector]); + stopTorrent: function(torrent) { + this.stopTorrents([ torrent ]); }, - 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); - } - - // update the ui widgetry - $('#torrent_inspector').toggle(visible); - if (iPhone) { - $('body').toggleClass('inspector_showing',visible); - $('#inspector_close').toggle(visible); - this.hideiPhoneAddressbar(); - } else { - var w = visible ? $('#torrent_inspector').width() + 1 + 'px' : '0px'; - $('#torrent_container')[0].style.right = w; - } - - setInnerHTML($('ul li#context_toggle_inspector')[0], (visible?'Hide':'Show')+' Inspector'); - this.setPref(Prefs._ShowInspector, visible); - if (visible) - this.updateInspector(); + stopTorrents: function(torrents) { + var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); + var tr = this; + this.remote.stopTorrents(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); }, - - onTorrentChanged: function(ev) - { - 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(); + changeFileCommand: function(command, rows) { + this.remote.changeFileCommand(command, rows); }, - updateFromTorrentGet: function(updates, removed_ids) - { - var new_ids = []; - - for (var i=0, o; o=updates[i]; ++i) { - var t; - var id = o.id; - if ((t = this._torrents[id])) - t.refresh(o); - else { - t = this._torrents[id] = new Torrent(o); - $(t).bind('dataChanged',function(ev) {tr.onTorrentChanged(ev);}); - new_ids.push(id); + hideiPhoneAddressbar: function(timeInSeconds) { + if (iPhone) { + var delayLength = timeInSeconds ? timeInSeconds*1000 : 150; + // not currently supported on iPhone + if (/*document.body.scrollTop!=1 && */scroll_timeout==null) { + var tr = this; + scroll_timeout = setTimeout(function() {tr.doToolbarHide();}, delayLength); } } - - if (new_ids.length) { - var tr = this; - this.remote.getTorrentInitial(new_ids, function(a,b){tr.updateFromTorrentGet(a,b);}); - this.refilterSoon(); - } - - if (removed_ids) { - this.deleteTorrents(removed_ids); - this.refilterSoon(); - } + }, + doToolbarHide: function() { + window.scrollTo(0,1); + scroll_timeout=null; }, - refreshTorrents: function(ids) { - if (!ids) - ids = 'recently-active'; + // Queue + moveTop: function() { var tr = this; - this.remote.getTorrentStats(ids, function(a,b){tr.updateFromTorrentGet(a,b);}); + var torrent_ids = this.getSelectedTorrentIds(); + this.remote.moveTorrentsToTop(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); }, - initializeAllTorrents: function() { + moveUp: function() { var tr = this; - this.remote.getTorrentInitial(null, function(a,b){tr.updateFromTorrentGet(a,b);}); + var torrent_ids = this.getSelectedTorrentIds(); + this.remote.moveTorrentsUp(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); }, - refreshMetadata: function(ids) { + moveDown: function() { var tr = this; - this.remote.getTorrentMetadata(ids, function(a,b){tr.updateFromTorrentGet(a,b);}); + var torrent_ids = this.getSelectedTorrentIds(); + this.remote.moveTorrentsDown(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); }, - refreshInspectorTorrents: function(full) { + moveBottom: function() { var tr = this; - var ids = tr.getSelectedTorrentIds(); - if (ids.length > 0) - this.remote.getTorrentDetails(ids, full, function(a,b){tr.updateFromTorrentGet(a,b);}); + var torrent_ids = this.getSelectedTorrentIds(); + this.remote.moveTorrentsToBottom(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); }, - onRowClicked: function(ev, row) - { - // Prevents click carrying to parent element - // which deselects all on click - ev.stopPropagation(); - // but still hide the context menu if it is showing - $('#jqContextMenu').hide(); - - // 'Apple' button emulation on PC : - // Need settable meta-key and ctrl-key variables for mac emulation - var meta_key = ev.metaKey; - if (ev.ctrlKey && navigator.appVersion.toLowerCase().indexOf("mac") == -1) - meta_key = true; - - // Shift-Click - selects a range from the last-clicked row to this one - if (iPhone) { - if (row.isSelected()) - this.setInspectorVisible(true); - this.setSelectedRow(row); - - } else if (ev.shiftKey) { - this.selectRange(row); - // Need to deselect any selected text - window.focus(); - - // Apple-Click, not selected - } else if (!row.isSelected() && meta_key) { - this.selectRow(row); - - // Regular Click, not selected - } else if (!row.isSelected()) { - this.setSelectedRow(row); - // Apple-Click, selected - } else if (row.isSelected() && meta_key) { - this.deselectRow(row); + /*** + **** + ***/ - // Regular Click, selected - } else if (row.isSelected()) { - this.setSelectedRow(row); - } + onToggleRunningClicked: function(ev) + { + var torrent = ev.data.r.getTorrent(); - this._last_torrent_clicked = row.getTorrentId(); + if (torrent.isStopped()) + this.startTorrent(torrent); + else + this.stopTorrent(torrent); }, - deleteTorrents: function(torrent_ids) + setEnabled: function(key, flag) { - if (torrent_ids && torrent_ids.length) - { - for (var i=0, id; id=torrent_ids[i]; ++i) - delete this._torrents[id]; - this.refilter(); - } + $(key).toggleClass('disabled', !flag); }, - updateStatusbar: function() + updateButtonStates: function() { - this.refreshFilterButton(); + var showing_dialog = new RegExp("(prefs_showing|dialog_showing|open_showing)").test(document.body.className); + this._toolbar_buttons.toggleClass('disabled', showing_dialog); - // up/down speed - var u=0, d=0; - var torrents = this.getAllTorrents(); - for (var i=0, row; row=torrents[i]; ++i) { - u += row.getUploadSpeed(); - d += row.getDownloadSpeed(); - } - setInnerHTML($('#statusbar #speed-up-label')[0], '↑ ' + Transmission.fmt.speedBps(u)); - setInnerHTML($('#statusbar #speed-dn-label')[0], '↓ ' + Transmission.fmt.speedBps(d)); - }, - - /* - * Select a torrent file to upload - * FIXME - */ - uploadTorrentFile: function(confirmed) - { - // Display the upload dialog - if (! confirmed) { - $('input#torrent_upload_file').attr('value', ''); - $('input#torrent_upload_url').attr('value', ''); - $('input#torrent_auto_start').attr('checked', $('#prefs_form #auto_start')[0].checked); - $('#upload_container').show(); - $('#torrent_upload_url').focus(); - if (!iPhone && Safari3) { - setTimeout("$('div#upload_container div.dialog_window').css('top', '0px');",10); - } - - // Submit the upload form - } else { - var tr = this; - var args = { }; - var paused = !$('#torrent_auto_start').is(':checked'); - if ('' != $('#torrent_upload_url').val()) { - tr.remote.addTorrentByUrl($('#torrent_upload_url').val(), { paused: paused }); - } else { - args.url = '../upload?paused=' + paused; - args.type = 'POST'; - args.data = { 'X-Transmission-Session-Id' : tr.remote._token }; - args.dataType = 'xml'; - args.iframe = true; - args.success = function() { - tr.refreshTorrents(); - tr.togglePeriodicRefresh(true); - }; - tr.togglePeriodicRefresh(false); - $('#torrent_upload_form').ajaxSubmit(args); - } - } - }, - - removeSelectedTorrents: function() { - var torrents = this.getSelectedTorrents(); - if (torrents.length) - this.promptToRemoveTorrents(torrents); - }, - - removeSelectedTorrentsAndData: function() { - var torrents = this.getSelectedTorrents(); - if (torrents.length) - this.promptToRemoveTorrentsAndData(torrents); - }, - - promptToRemoveTorrents:function(torrents) - { - if (torrents.length == 1) - { - var torrent = torrents[0]; - var header = 'Remove ' + torrent.getName() + '?'; - var message = 'Once removed, continuing the transfer will require the torrent file. Are you sure you want to remove it?'; - dialog.confirm(header, message, 'Remove', 'transmission.removeTorrents', torrents); - } - else - { - var header = 'Remove ' + torrents.length + ' transfers?'; - var message = 'Once removed, continuing the transfers will require the torrent files. Are you sure you want to remove them?'; - dialog.confirm(header, message, 'Remove', 'transmission.removeTorrents', torrents); - } - }, - - promptToRemoveTorrentsAndData:function(torrents) - { - if (torrents.length == 1) - { - var torrent = torrents[0], - header = 'Remove ' + torrent.getName() + ' and delete data?', - message = 'All data downloaded for this torrent will be deleted. Are you sure you want to remove it?'; - dialog.confirm(header, message, 'Remove', 'transmission.removeTorrentsAndData', torrents); - } - else - { - var header = 'Remove ' + torrents.length + ' transfers and delete data?', - message = 'All data downloaded for these torrents will be deleted. Are you sure you want to remove them?'; - dialog.confirm(header, message, 'Remove', 'transmission.removeTorrentsAndData', torrents); - } - }, - - removeTorrents: function(torrents) { - var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); - var tr = this; - this.remote.removeTorrents(torrent_ids, function() { tr.refreshTorrents();}); - }, - - removeTorrentsAndData: function(torrents) { - this.remote.removeTorrentsAndData(torrents); - }, - - verifySelectedTorrents: function() { - this.verifyTorrents(this.getSelectedTorrents()); - }, - - reannounceSelectedTorrents: function() { - this.reannounceTorrents(this.getSelectedTorrents()); - }, - - startSelectedTorrents: function(force) { - this.startTorrents(this.getSelectedTorrents(), force); - }, - startAllTorrents: function() { - this.startTorrents(this.getAllTorrents(), false); - }, - startTorrent: function(torrent) { - this.startTorrents([ torrent ], false); - }, - startTorrents: function(torrents, force) { - var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); - var tr = this; - this.remote.startTorrents(torrent_ids, force, function() { tr.refreshTorrents(torrent_ids); }); - }, - verifyTorrent: function(torrent) { - this.verifyTorrents([ torrent ]); - }, - verifyTorrents: function(torrents) { - var tr = this; - var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); - this.remote.verifyTorrents(torrent_ids, function() { tr.refreshTorrents(torrent_ids); }); - }, - - reannounceTorrent: function(torrent) { - this.reannounceTorrents([ torrent ]); - }, - reannounceTorrents: function(torrents) { - var tr = this; - var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); - this.remote.reannounceTorrents(torrent_ids, function() { tr.refreshTorrents(torrent_ids); }); - }, - - stopSelectedTorrents: function() { - this.stopTorrents(this.getSelectedTorrents()); - }, - stopAllTorrents: function() { - this.stopTorrents(this.getAllTorrents()); - }, - stopTorrent: function(torrent) { - this.stopTorrents([ torrent ]); - }, - stopTorrents: function(torrents) { - var torrent_ids = jQuery.map(torrents, function(t) { return t.getId(); }); - var tr = this; - this.remote.stopTorrents(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); - }, - changeFileCommand: function(command, rows) { - this.remote.changeFileCommand(command, rows); - }, - - hideiPhoneAddressbar: function(timeInSeconds) { - if (iPhone) { - var delayLength = timeInSeconds ? timeInSeconds*1000 : 150; - // not currently supported on iPhone - if (/*document.body.scrollTop!=1 && */scroll_timeout==null) { - var tr = this; - scroll_timeout = setTimeout(function() {tr.doToolbarHide();}, delayLength); - } - } - }, - doToolbarHide: function() { - window.scrollTo(0,1); - scroll_timeout=null; - }, - - // Queue - moveTop: function() { - var tr = this; - var torrent_ids = this.getSelectedTorrentIds(); - this.remote.moveTorrentsToTop(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); - }, - moveUp: function() { - var tr = this; - var torrent_ids = this.getSelectedTorrentIds(); - this.remote.moveTorrentsUp(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); - }, - moveDown: function() { - var tr = this; - var torrent_ids = this.getSelectedTorrentIds(); - this.remote.moveTorrentsDown(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); - }, - moveBottom: function() { - var tr = this; - var torrent_ids = this.getSelectedTorrentIds(); - this.remote.moveTorrentsToBottom(torrent_ids, function() { tr.refreshTorrents(torrent_ids);}); - }, - - - /*** - **** - ***/ - - onToggleRunningClicked: function(ev) - { - var torrent = ev.data.r.getTorrent(); - - if (torrent.isStopped()) - this.startTorrent(torrent); - else - this.stopTorrent(torrent); - }, - - setEnabled: function(key, flag) - { - $(key).toggleClass('disabled', !flag); - }, - - updateButtonStates: function() - { - var showing_dialog = new RegExp("(prefs_showing|dialog_showing|open_showing)").test(document.body.className); - this._toolbar_buttons.toggleClass('disabled', showing_dialog); - - if (!showing_dialog) - { - var haveSelection = false; - var haveActive = false; - var haveActiveSelection = false; - var havePaused = false; - var havePausedSelection = false; + if (!showing_dialog) + { + var haveSelection = false; + var haveActive = false; + var haveActiveSelection = false; + var havePaused = false; + var havePausedSelection = false; for (var i=0, row; row=this._rows[i]; ++i) { var isStopped = row.getTorrent().isStopped(); @@ -1912,6 +1495,430 @@ Transmission.prototype = } }, + /**** + ***** + ***** INSPECTOR + ***** + ****/ + + inspectorTabClicked: function(ev, tab) + { + if (iPhone) ev.stopPropagation(); + + // select this tab and deselect the others + $(tab).addClass('selected').siblings().removeClass('selected'); + + // show this tab and hide the others + $('#'+tab.id+'_container').show().siblings('.inspector_container').hide(); + + this.hideiPhoneAddressbar(); + this.updatePeersLists(); + this.updateTrackersLists(); + this.updateFileList(); + }, + /* + * Update the inspector with the latest data for the selected torrents + */ + updateInspector: function() + { + if (!this[Prefs._ShowInspector]) + return; + + var torrents = this.getSelectedTorrents(); + if (!torrents.length && iPhone) { + this.setInspectorVisible(false); + return; + } + + var creator = 'N/A'; + var comment = 'N/A'; + var download_dir = 'N/A'; + var date_created = 'N/A'; + var error = 'None'; + var hash = 'N/A'; + var have_public = false; + var have_private = false; + var name; + var sizeWhenDone = 0; + var sizeDone = 0; + var total_completed = 0; + var total_download = 0; + var total_download_peers = 0; + var total_download_speed = 0; + var total_availability = 0; + var total_size = 0; + var total_state = [ ]; + var pieces = 'N/A'; + var total_upload = 0; + var total_upload_peers = 0; + var total_upload_speed = 0; + var total_verified = 0; + var na = 'N/A'; + var tab = this._inspector._info_tab; + + $("#torrent_inspector_size, .inspector_row div").css('color', '#222'); + + if (torrents.length == 0) + { + setInnerHTML(tab.name, 'No Selection'); + setInnerHTML(tab.size, na); + setInnerHTML(tab.pieces, na); + setInnerHTML(tab.hash, na); + setInnerHTML(tab.state, na); + setInnerHTML(tab.download_speed, na); + setInnerHTML(tab.upload_speed, na); + setInnerHTML(tab.uploaded, na); + setInnerHTML(tab.downloaded, na); + setInnerHTML(tab.availability, na); + setInnerHTML(tab.ratio, na); + setInnerHTML(tab.have, na); + setInnerHTML(tab.upload_to, na); + setInnerHTML(tab.download_from, na); + setInnerHTML(tab.secure, na); + setInnerHTML(tab.creator_date, na); + setInnerHTML(tab.progress, na); + setInnerHTML(tab.comment, na); + setInnerHTML(tab.creator, na); + setInnerHTML(tab.download_dir, na); + setInnerHTML(tab.error, na); + this.updateFileList(); + this.updatePeersLists(); + this.updateTrackersLists(); + $("#torrent_inspector_size, .inspector_row > div:contains('N/A')").css('color', '#666'); + return; + } + + name = torrents.length == 1 + ? torrents[0].getName() + : torrents.length+' Transfers Selected'; + + if (torrents.length == 1) + { + var text; + var t = torrents[0]; + var err = t.getErrorMessage(); + if (err) + error = err; + if ((text = t.getComment())) + comment = text; + if ((text = t.getCreator())) + creator = text; + if ((text = t.getDownloadDir())) + download_dir = text; + + hash = t.getHashString(); + pieces = [ t.getPieceCount(), 'pieces @', Transmission.fmt.mem(t.getPieceSize()) ].join(' '); + date_created = Transmission.fmt.timestamp(t.getDateCreated()); + } + + for (var i=0, t; t=torrents[i]; ++i) { + var l = t.getLeftUntilDone(); + var d = t.getSizeWhenDone(); + sizeWhenDone += d; + sizeDone += d - l; + total_completed += t.getHave(); + total_verified += t.getHaveValid(); + total_size += t.getTotalSize(); + total_upload += t.getUploadedEver(); + total_download += t.getDownloadedEver(); + total_upload_speed += t.getUploadSpeed(); + total_download_speed += t.getDownloadSpeed(); + total_upload_peers += t.getPeersGettingFromUs(); + total_download_peers += t.getPeersSendingToUs(); + total_availability += sizeWhenDone - l + t.getDesiredAvailable(); + + var s = t.getStateString(); + if (total_state.indexOf(s) == -1) + total_state.push(s); + + if (t.getPrivateFlag()) + have_private = true; + else + have_public = true; + } + + var private_string = ''; + var fmt = Transmission.fmt; + if (have_private && have_public) private_string = 'Mixed'; + else if (have_private) private_string = 'Private Torrent'; + else if (have_public) private_string = 'Public Torrent'; + + setInnerHTML(tab.name, name); + setInnerHTML(tab.size, torrents.length ? fmt.size(total_size) : na); + setInnerHTML(tab.pieces, pieces); + setInnerHTML(tab.hash, hash); + setInnerHTML(tab.state, total_state.join('/')); + setInnerHTML(tab.download_speed, torrents.length ? fmt.speedBps(total_download_speed) : na); + setInnerHTML(tab.upload_speed, torrents.length ? fmt.speedBps(total_upload_speed) : na); + setInnerHTML(tab.uploaded, torrents.length ? fmt.size(total_upload) : na); + setInnerHTML(tab.downloaded, torrents.length ? fmt.size(total_download) : na); + setInnerHTML(tab.availability, torrents.length ? fmt.percentString(Math.ratio(total_availability*100, sizeWhenDone)) + '%' : na); + setInnerHTML(tab.ratio, torrents.length ? fmt.ratioString(Math.ratio(total_upload, total_download)) : na); + setInnerHTML(tab.have, torrents.length ? fmt.size(total_completed) + ' (' + fmt.size(total_verified) + ' verified)' : na); + setInnerHTML(tab.upload_to, torrents.length ? total_upload_peers : na); + setInnerHTML(tab.download_from, torrents.length ? total_download_peers : na); + setInnerHTML(tab.secure, private_string); + setInnerHTML(tab.creator_date, date_created); + setInnerHTML(tab.progress, torrents.length ? fmt.percentString(Math.ratio(sizeDone*100, sizeWhenDone)) + '%' : na); + setInnerHTML(tab.comment, comment); + setInnerHTML(tab.creator, creator); + setInnerHTML(tab.download_dir, download_dir); + setInnerHTML(tab.error, error); + + this.updatePeersLists(); + this.updateTrackersLists(); + $(".inspector_row > div:contains('N/A')").css('color', '#666'); + this.updateFileList(); + }, + + onFileWantedToggled: function(row, want) { + var command = want ? 'files-wanted' : 'files-unwanted'; + this.changeFileCommand(command, [ row ]); + }, + onFilePriorityToggled: function(row, priority) { + var command; + switch(priority) { + case -1: command = 'priority-low'; break; + case 1: command = 'priority-high'; break; + default: command = 'priority-normal'; break; + } + this.changeFileCommand(command, [ row ]); + }, + clearFileList: function() + { + $(this._inspector_file_list).empty(); + delete this._file_torrent; + delete this._file_rows; + }, + updateFileList: function() + { + if (!$(this._inspector_file_list).is(':visible')) + return; + + var sel = this.getSelectedTorrents(); + if (sel.length !== 1) { + this.clearFileList(); + 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._file_torrent = torrent; + var n = torrent.getFileCount(); + this._file_rows = []; + var fragment = document.createDocumentFragment(); + var tr = this; + for (var i=0; i'); + if (torrents.length > 1) { + html.push('
', torrent.getName(), '
'); + } + if (!peers || !peers.length) { + html.push('
'); // firefox won't paint the top border if the div is empty + continue; + } + html.push('', + '', + '', + '', + '', + '', + '', + '', + '', + ''); + for (var i=0, peer; peer=peers[i]; ++i) { + var parity = ((i+1) % 2 == 0 ? 'even' : 'odd'); + html.push('', + '', + '', + '', + '', + '', + '', + '', + ''); + } + html.push('
UpDown%StatusAddressClient
', (peer.isEncrypted ? 'Encrypted' : ''), '', (peer.rateToPeer ? fmt.speedBps(peer.rateToPeer) : ''), '', (peer.rateToClient ? fmt.speedBps(peer.rateToClient) : ''), '', Math.floor(peer.progress*100), '%', '', fmt.peerStatus(peer.flagStr), '', peer.address, '', peer.clientName, '
'); + } + + setInnerHTML(this._inspector_peers_list, html.join('')); + }, + + updateTrackersLists: function() { + if (!$(this._inspector_trackers_list).is(':visible')) + return; + + var tr = this; + var html = [ ]; + var na = 'N/A'; + var torrents = this.getSelectedTorrents(); + + // 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 ('
'); + + if (torrents.length > 1) + html.push('
', torrent.getName(), '
'); + + 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('
'); + + tier = tracker.tier; + + html.push('
', + 'Tier ', tier, '
', + '
    '); + } + + 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('
  • ', + tracker.host, '
    ', + '
    ', + '
    ', lastAnnounceStatusHash['label'], ': ', lastAnnounceStatusHash['value'], '
    ', + '
    ', announceState, '
    ', + '
    ', lastScrapeStatusHash['label'], ': ', lastScrapeStatusHash['value'], '
    ', + '
    ', + '', + '', + '', + '
    Seeders:', (tracker.seederCount > -1 ? tracker.seederCount : na), '
    Leechers:', (tracker.leecherCount > -1 ? tracker.leecherCount : na), '
    Downloads:', (tracker.downloadCount > -1 ? tracker.downloadCount : na), '
  • '); + } + if (tier !== -1) // close last tier + html.push('
'); + + html.push(''); // inspector_group + } + + setInnerHTML(this._inspector_trackers_list, html.join('')); + }, + + lastAnnounceStatus: function(tracker) { + var lastAnnounceLabel = 'Last Announce'; + var lastAnnounce = [ 'N/A' ]; + if (tracker.hasAnnounced) { + var lastAnnounceTime = Transmission.fmt.timestamp(tracker.lastAnnounceTime); + if (tracker.lastAnnounceSucceeded) { + lastAnnounce = [ lastAnnounceTime, ' (got ', Transmission.fmt.plural(tracker.lastAnnouncePeerCount, 'peer'), ')' ]; + } else { + lastAnnounceLabel = 'Announce error'; + lastAnnounce = [ (tracker.lastAnnounceResult ? (tracker.lastAnnounceResult + ' - ') : ''), lastAnnounceTime ]; + } + } + return { 'label':lastAnnounceLabel, 'value':lastAnnounce.join('') }; + }, + + announceState: function(tracker) { + var announceState = ''; + switch (tracker.announceState) { + case Torrent._TrackerActive: + announceState = 'Announce in progress'; + break; + case Torrent._TrackerWaiting: + var timeUntilAnnounce = tracker.nextAnnounceTime - ((new Date()).getTime() / 1000); + if (timeUntilAnnounce < 0) { + timeUntilAnnounce = 0; + } + announceState = 'Next announce in ' + Transmission.fmt.timeInterval(timeUntilAnnounce); + break; + case Torrent._TrackerQueued: + announceState = 'Announce is queued'; + break; + case Torrent._TrackerInactive: + announceState = tracker.isBackup ? + 'Tracker will be used as a backup' : + 'Announce not scheduled'; + break; + default: + announceState = 'unknown announce state: ' + tracker.announceState; + } + return announceState; + }, + + lastScrapeStatus: function(tracker) { + var lastScrapeLabel = 'Last Scrape'; + var lastScrape = 'N/A'; + if (tracker.hasScraped) { + var lastScrapeTime = Transmission.fmt.timestamp(tracker.lastScrapeTime); + if (tracker.lastScrapeSucceeded) { + lastScrape = lastScrapeTime; + } else { + lastScrapeLabel = 'Scrape error'; + lastScrape = (tracker.lastScrapeResult ? tracker.lastScrapeResult + ' - ' : '') + lastScrapeTime; + } + } + return {'label':lastScrapeLabel, 'value':lastScrape}; + }, + + toggleInspector: function() + { + this.setInspectorVisible(!this[Prefs._ShowInspector]); + }, + 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); + } + + // update the ui widgetry + $('#torrent_inspector').toggle(visible); + if (iPhone) { + $('body').toggleClass('inspector_showing',visible); + $('#inspector_close').toggle(visible); + this.hideiPhoneAddressbar(); + } else { + var w = visible ? $('#torrent_inspector').width() + 1 + 'px' : '0px'; + $('#torrent_container')[0].style.right = w; + } + + setInnerHTML($('ul li#context_toggle_inspector')[0], (visible?'Hide':'Show')+' Inspector'); + this.setPref(Prefs._ShowInspector, visible); + if (visible) + this.updateInspector(); + }, + /**** ***** ***** FILTER -- 2.40.0