/** * Copyright © Jordan Lee, Dave Perrett, Malcolm Jarvis and Bruno Bierbaumer * * This file is licensed under the GPLv2. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ function Inspector(controller) { var data = { controller: null, elements: {}, torrents: [] }, needsExtraInfo = function (torrents) { var i, id, tor; for (i = 0; tor = torrents[i]; i++) if (!tor.hasExtraInfo()) return true; return false; }, refreshTorrents = function (callback) { var fields, ids = $.map(data.torrents.slice(0), function (t) { return t.getId(); }); if (ids && ids.length) { fields = ['id'].concat(Torrent.Fields.StatsExtra); if (needsExtraInfo(data.torrents)) { $.merge(fields, Torrent.Fields.InfoExtra); } data.controller.updateTorrents(ids, fields, callback); } }, onTabClicked = function (ev) { var tab = ev.currentTarget; if (isMobileDevice) { ev.stopPropagation(); } // select this tab and deselect the others $(tab).addClass('selected').siblings().removeClass('selected'); // show this tab and hide the others $('#' + tab.id.replace('tab', 'page')).show().siblings('.inspector-page').hide(); updateInspector(); }, updateInspector = function () { var e = data.elements, torrents = data.torrents, name; // update the name, which is shown on all the pages if (!torrents || !torrents.length) { name = 'No Selection'; } else if (torrents.length === 1) { name = torrents[0].getName(); } else { name = '' + torrents.length + ' Transfers Selected'; } setTextContent(e.name_lb, name || na); // update the visible page if ($(e.info_page).is(':visible')) { updateInfoPage(); } else if ($(e.peers_page).is(':visible')) { updatePeersPage(); } else if ($(e.trackers_page).is(':visible')) { updateTrackersPage(); } else if ($(e.files_page).is(':visible')) { updateFilesPage(); } }, /**** ***** GENERAL INFO PAGE ****/ updateInfoPage = function () { var torrents = data.torrents, e = data.elements, fmt = Transmission.fmt, none = 'None', mixed = 'Mixed', unknown = 'Unknown', isMixed, allPaused, allFinished, str, baseline, it, s, i, t, sizeWhenDone = 0, leftUntilDone = 0, available = 0, haveVerified = 0, haveUnverified = 0, verifiedPieces = 0, stateString, latest, pieces, size, pieceSize, creator, mixed_creator, date, mixed_date, v, u, f, d, pct, uri, now = Date.now(); // // state_lb // if (torrents.length < 1) { str = none; } else { isMixed = false; allPaused = true; allFinished = true; baseline = torrents[0].getStatus(); for (i = 0; t = torrents[i]; ++i) { it = t.getStatus(); if (it != baseline) { isMixed = true; } if (!t.isStopped()) { allPaused = allFinished = false; } if (!t.isFinished()) { allFinished = false; } } if (isMixed) { str = mixed; } else if (allFinished) { str = 'Finished'; } else if (allPaused) { str = 'Paused'; } else { str = torrents[0].getStateString(); } } setTextContent(e.state_lb, str); stateString = str; // // have_lb // if (torrents.length < 1) str = none; else { baseline = torrents[0].getStatus(); for (i = 0; t = torrents[i]; ++i) { if (!t.needsMetaData()) { haveUnverified += t.getHaveUnchecked(); v = t.getHaveValid(); haveVerified += v; if (t.getPieceSize()) { verifiedPieces += v / t.getPieceSize(); } sizeWhenDone += t.getSizeWhenDone(); leftUntilDone += t.getLeftUntilDone(); available += (t.getHave()) + t.getDesiredAvailable(); } } d = 100.0 * (sizeWhenDone ? (sizeWhenDone - leftUntilDone) / sizeWhenDone : 1); str = fmt.percentString(d); if (!haveUnverified && !leftUntilDone) { str = fmt.size(haveVerified) + ' (100%)'; } else if (!haveUnverified) { str = fmt.size(haveVerified) + ' of ' + fmt.size(sizeWhenDone) + ' (' + str + '%)'; } else { str = fmt.size(haveVerified) + ' of ' + fmt.size(sizeWhenDone) + ' (' + str + '%), ' + fmt.size(haveUnverified) + ' Unverified'; } } setTextContent(e.have_lb, str); // // availability_lb // if (torrents.length < 1) { str = none; } else if (sizeWhenDone == 0) { str = none; } else { str = '' + fmt.percentString((100.0 * available) / sizeWhenDone) + '%'; }; setTextContent(e.availability_lb, str); // // downloaded_lb // if (torrents.length < 1) { str = none; } else { d = f = 0; for (i = 0; t = torrents[i]; ++i) { d += t.getDownloadedEver(); f += t.getFailedEver(); }; if (f) { str = fmt.size(d) + ' (' + fmt.size(f) + ' corrupt)'; } else { str = fmt.size(d); }; }; setTextContent(e.downloaded_lb, str); // // uploaded_lb // if (torrents.length < 1) { str = none; } else { d = u = 0; if (torrents.length == 1) { d = torrents[0].getDownloadedEver(); u = torrents[0].getUploadedEver(); if (d == 0) { d = torrents[0].getHaveValid(); }; } else { for (i = 0; t = torrents[i]; ++i) { d += t.getDownloadedEver(); u += t.getUploadedEver(); }; }; str = fmt.size(u) + ' (Ratio: ' + fmt.ratioString(Math.ratio(u, d)) + ')'; }; setTextContent(e.uploaded_lb, str); // // running time // if (torrents.length < 1) { str = none; } else { allPaused = true; baseline = torrents[0].getStartDate(); for (i = 0; t = torrents[i]; ++i) { if (baseline != t.getStartDate()) { baseline = 0; } if (!t.isStopped()) { allPaused = false; } } if (allPaused) { str = stateString; // paused || finished} } else if (!baseline) { str = mixed; } else { str = fmt.timeInterval(now / 1000 - baseline); } }; setTextContent(e.running_time_lb, str); // // remaining time // str = ''; if (torrents.length < 1) { str = none; } else { baseline = torrents[0].getETA(); for (i = 0; t = torrents[i]; ++i) { if (baseline != t.getETA()) { str = mixed; break; } } } if (!str.length) { if (baseline < 0) { str = unknown; } else { str = fmt.timeInterval(baseline); } } setTextContent(e.remaining_time_lb, str); // // last activity // latest = -1; if (torrents.length < 1) { str = none; } else { baseline = torrents[0].getLastActivity(); for (i = 0; t = torrents[i]; ++i) { d = t.getLastActivity(); if (latest < d) { latest = d; }; }; d = now / 1000 - latest; // seconds since last activity if (d < 0) { str = none; } else if (d < 5) { str = 'Active now'; } else { str = fmt.timeInterval(d) + ' ago'; }; }; setTextContent(e.last_activity_lb, str); // // error // if (torrents.length < 1) { str = none; } else { str = torrents[0].getErrorString(); for (i = 0; t = torrents[i]; ++i) { if (str != t.getErrorString()) { str = mixed; break; }; }; }; setTextContent(e.error_lb, str || none); // // size // if (torrents.length < 1) { { str = none; }; } else { pieces = 0; size = 0; pieceSize = torrents[0].getPieceSize(); for (i = 0; t = torrents[i]; ++i) { pieces += t.getPieceCount(); size += t.getTotalSize(); if (pieceSize != t.getPieceSize()) { pieceSize = 0; } }; if (!size) { str = none; } else if (pieceSize > 0) { str = fmt.size(size) + ' (' + pieces.toStringWithCommas() + ' pieces @ ' + fmt.mem(pieceSize) + ')'; } else { str = fmt.size(size) + ' (' + pieces.toStringWithCommas() + ' pieces)'; }; }; setTextContent(e.size_lb, str); // // hash // if (torrents.length < 1) { str = none; } else { str = torrents[0].getHashString(); for (i = 0; t = torrents[i]; ++i) { if (str != t.getHashString()) { str = mixed; break; }; }; }; setTextContent(e.hash_lb, str); // // privacy // if (torrents.length < 1) { str = none; } else { baseline = torrents[0].getPrivateFlag(); str = baseline ? 'Private to this tracker -- DHT and PEX disabled' : 'Public torrent'; for (i = 0; t = torrents[i]; ++i) { if (baseline != t.getPrivateFlag()) { str = mixed; break; }; }; }; setTextContent(e.privacy_lb, str); // // comment // if (torrents.length < 1) { str = none; } else { str = torrents[0].getComment(); for (i = 0; t = torrents[i]; ++i) { if (str != t.getComment()) { str = mixed; break; }; }; }; if (!str) { str = none; } uri = parseUri(str); if (uri.protocol == 'http' || uri.protocol == 'https') { str = encodeURI(str); setInnerHTML(e.comment_lb, '' + str + ''); } else { setTextContent(e.comment_lb, str); }; // // origin // if (torrents.length < 1) { str = none; } else { mixed_creator = false; mixed_date = false; creator = torrents[0].getCreator(); date = torrents[0].getDateCreated(); for (i = 0; t = torrents[i]; ++i) { if (creator != t.getCreator()) { mixed_creator = true; }; if (date != t.getDateCreated()) { mixed_date = true; }; }; var empty_creator = !creator || !creator.length; var empty_date = !date; if (mixed_creator || mixed_date) { str = mixed; } else if (empty_creator && empty_date) { str = unknown; } else if (empty_date && !empty_creator) { str = 'Created by ' + creator; } else if (empty_creator && !empty_date) { str = 'Created on ' + (new Date(date * 1000)).toDateString(); } else { str = 'Created by ' + creator + ' on ' + (new Date(date * 1000)).toDateString(); }; }; setTextContent(e.origin_lb, str); // // foldername // if (torrents.length < 1) { str = none; } else { str = torrents[0].getDownloadDir(); for (i = 0; t = torrents[i]; ++i) { if (str != t.getDownloadDir()) { str = mixed; break; }; }; }; setTextContent(e.foldername_lb, str); }, /**** ***** FILES PAGE ****/ changeFileCommand = function (fileIndices, command) { var torrentId = data.file_torrent.getId(); data.controller.changeFileCommand(torrentId, fileIndices, command); }, onFileWantedToggled = function (ev, fileIndices, want) { changeFileCommand(fileIndices, want ? 'files-wanted' : 'files-unwanted'); }, onFilePriorityToggled = function (ev, fileIndices, priority) { var command; switch (priority) { case -1: command = 'priority-low'; break; case 1: command = 'priority-high'; break; default: command = 'priority-normal'; break; } changeFileCommand(fileIndices, command); }, onNameClicked = function (ev, fileRow, fileIndices) { $(fileRow.getElement()).siblings().slideToggle(); }, clearFileList = function () { $(data.elements.file_list).empty(); delete data.file_torrent; delete data.file_torrent_n; delete data.file_rows; }, createFileTreeModel = function (tor) { var i, j, n, name, tokens, walk, tree, token, sub, leaves = [], tree = { children: {}, file_indices: [] }; n = tor.getFileCount(); for (i = 0; i < n; ++i) { name = tor.getFile(i).name; tokens = name.split('/'); walk = tree; for (j = 0; j < tokens.length; ++j) { token = tokens[j]; sub = walk.children[token]; if (!sub) { walk.children[token] = sub = { name: token, parent: walk, children: {}, file_indices: [], depth: j }; } walk = sub; } walk.file_index = i; delete walk.children; leaves.push(walk); } for (i = 0; i < leaves.length; ++i) { walk = leaves[i]; j = walk.file_index; do { walk.file_indices.push(j); walk = walk.parent; } while (walk); } return tree; }, addNodeToView = function (tor, parent, sub, i) { var row; row = new FileRow(tor, sub.depth, sub.name, sub.file_indices, i % 2); data.file_rows.push(row); parent.appendChild(row.getElement()); $(row).bind('wantedToggled', onFileWantedToggled); $(row).bind('priorityToggled', onFilePriorityToggled); $(row).bind('nameClicked', onNameClicked); }, addSubtreeToView = function (tor, parent, sub, i) { var key, div; div = document.createElement('div'); if (sub.parent) { addNodeToView(tor, div, sub, i++); } if (sub.children) { for (key in sub.children) { i = addSubtreeToView(tor, div, sub.children[key]); } } parent.appendChild(div); return i; }, updateFilesPage = function () { var i, n, tor, fragment, tree, file_list = data.elements.file_list, torrents = data.torrents; // only show one torrent at a time if (torrents.length !== 1) { clearFileList(); return; } tor = torrents[0]; n = tor ? tor.getFileCount() : 0; if (tor != data.file_torrent || n != data.file_torrent_n) { // rebuild the file list... clearFileList(); data.file_torrent = tor; data.file_torrent_n = n; data.file_rows = []; fragment = document.createDocumentFragment(); tree = createFileTreeModel(tor); addSubtreeToView(tor, fragment, tree, 0); file_list.appendChild(fragment); } else { // ...refresh the already-existing file list for (i = 0, n = data.file_rows.length; i < n; ++i) data.file_rows[i].refresh(); } }, /**** ***** PEERS PAGE ****/ updatePeersPage = function () { var i, k, tor, peers, peer, parity, html = [], fmt = Transmission.fmt, peers_list = data.elements.peers_list, torrents = data.torrents; for (k = 0; tor = torrents[k]; ++k) { peers = tor.getPeers(); html.push('
', ' | Up | ', 'Down | ', '% | ', 'Status | ', 'Address | ', 'Client | ', '
---|---|---|---|---|---|---|
', (peer.isEncrypted ? ' ' : ' '), ' ', ' | ',
'', (peer.rateToPeer ? fmt.speedBps(peer.rateToPeer) : ''), ' | ', '', (peer.rateToClient ? fmt.speedBps(peer.rateToClient) : ''), ' | ', '', Math.floor(peer.progress * 100), '%', ' | ', '', fmt.peerStatus(peer.flagStr), ' | ', '', sanitizeText(peer.address), ' | ', '', sanitizeText(peer.clientName), ' | ', '
Seeders: | ', (tracker.seederCount > -1 ? tracker.seederCount : na), ' |
---|---|
Leechers: | ', (tracker.leecherCount > -1 ? tracker.leecherCount : na), ' |
Downloads: | ', (tracker.downloadCount > -1 ? tracker.downloadCount : na), ' |