/* * overdrive.js * Copyright (C) 2016 L. Bradley LaBoon * * This file is part of Overdrive. * * Overdrive is free software: you can redistribute it and/or modify it under * the terms terms of the GNU General Public License as published by the Free * Software Foundation, either verion 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ /* Global Namespace */ var _overdrive = {}; /* Settings */ _overdrive.settings = {}; _overdrive.settings.binaryKB = true; _overdrive.settings.columns = [ "id", "name", "totalSize", "status", "percentDone", "rateDownload", "rateUpload", "eta", "uploadRatio" ]; _overdrive.settings.rpcURL = "/transmission/rpc"; _overdrive.settings.refreshRate = 1000; // This will be initialized after the first request to the server _overdrive.settings.sessionID = ""; /* Data Objects */ _overdrive.data = {}; _overdrive.data.columnNames = {}; _overdrive.data.selected = []; _overdrive.data.torrents = {}; _overdrive.data.torrentsDetail = {}; _overdrive.data.torrentStat = {}; _overdrive.data.session = {}; /* Torrent Status (values given by Transmission RPC) */ _overdrive.data.torrentStat.stopped = 0; _overdrive.data.torrentStat.checkWait = 1; _overdrive.data.torrentStat.check = 2; _overdrive.data.torrentStat.downloadWait = 3; _overdrive.data.torrentStat.download = 4; _overdrive.data.torrentStat.seedWait = 5; _overdrive.data.torrentStat.seed = 6; _overdrive.data.torrentStatStrings = [ "Paused", "Waiting (C)", "Checking", "Waiting (D)", "Downloading", "Waiting (S)", "Seeding" ]; /* Torrent Column Names */ _overdrive.data.columnNames.activityDate = "Activity Date"; _overdrive.data.columnNames.addedDate = "Added Date"; _overdrive.data.columnNames.bandwidthPriority = "Priority"; _overdrive.data.columnNames.comment = "Comment"; _overdrive.data.columnNames.corruptEver = "Corrupt"; _overdrive.data.columnNames.creator = "Creator"; _overdrive.data.columnNames.dateCreated = "Date Created"; _overdrive.data.columnNames.desiredAvailable = "Desired Available"; _overdrive.data.columnNames.doneDate = "Finished Date"; _overdrive.data.columnNames.downloadDir = "Directory"; _overdrive.data.columnNames.downloadedEver = "Downloaded Ever"; _overdrive.data.columnNames.downloadLimit = "Down Limit"; _overdrive.data.columnNames.downloadLimited = "Down Limited"; _overdrive.data.columnNames.error = "Error"; _overdrive.data.columnNames.errorString = "Error String"; _overdrive.data.columnNames.eta = "ETA"; _overdrive.data.columnNames.etaIdle = "ETA Idle"; _overdrive.data.columnNames.hashString = "Hash"; _overdrive.data.columnNames.haveUnchecked = "Unchecked"; _overdrive.data.columnNames.haveValid = "Downloaded"; _overdrive.data.columnNames.honorsSessionLimits = "Honors Session Limits"; _overdrive.data.columnNames.id = "#"; _overdrive.data.columnNames.isFinished = "Finished"; _overdrive.data.columnNames.isPrivate = "Private"; _overdrive.data.columnNames.isStalled = "Stalled"; _overdrive.data.columnNames.leftUntilDone = "Remaining"; _overdrive.data.columnNames.magnetLink = "Magnet Link"; _overdrive.data.columnNames.manualAnnounceTime = "Time Until Manual Announce"; _overdrive.data.columnNames.maxConnectedPeers = "Max Connected Peers"; _overdrive.data.columnNames.metadataPercentComplete = "Metadata Progress"; _overdrive.data.columnNames.name = "Name"; _overdrive.data.columnNames["peer-limit"] = "Peer Limit"; _overdrive.data.columnNames.peersConnected = "Peers"; _overdrive.data.columnNames.peersGettingFromUs = "Peers Taking"; _overdrive.data.columnNames.peersSendingToUs = "Peers Giving"; _overdrive.data.columnNames.percentDone = "Progress"; _overdrive.data.columnNames.pieceCount = "Pieces"; _overdrive.data.columnNames.pieceSize = "Piece Size"; _overdrive.data.columnNames.queuePosition = "Position"; _overdrive.data.columnNames.rateDownload = "Down Speed"; _overdrive.data.columnNames.rateUpload = "Up Speed"; _overdrive.data.columnNames.recheckProgress = "Recheck Progress"; _overdrive.data.columnNames.secondsDownloading = "Time Downloading"; _overdrive.data.columnNames.secondsSeeding = "Time Seeding"; _overdrive.data.columnNames.seedIdleLimit = "Seed Idle Limit"; _overdrive.data.columnNames.seedIdleMode = "Seed Idle Mode"; _overdrive.data.columnNames.seedRatioLimit = "Seed Ratio Limit"; _overdrive.data.columnNames.seedRatioMode = "Seed Ratio Mode"; _overdrive.data.columnNames.sizeWhenDone = "Wanted Size"; _overdrive.data.columnNames.startDate = "Start Date"; _overdrive.data.columnNames.status = "Status"; _overdrive.data.columnNames.totalSize = "Size"; _overdrive.data.columnNames.torrentFile = "Torrent File"; _overdrive.data.columnNames.uploadedEver = "Uploaded"; _overdrive.data.columnNames.uploadLimit = "Up Limit"; _overdrive.data.columnNames.uploadRatio = "Ratio"; _overdrive.data.columnNames.webseedsSendingToUs = "Web Seeds"; // List of columns that contain string data _overdrive.data.stringCols = [ "comment", "creator", "downloadDir", "errorString", "hashString", "name", "torrentFile" ]; /* Last sent/received tags */ _overdrive.data.lastSent = 0; _overdrive.data.lastReceived = -1; _overdrive.data.lastSentSession = 0; _overdrive.data.lastReceivedSession = -1; _overdrive.data.lastSentDetail = 0; _overdrive.data.lastReceivedDetail = -1; /* UI Object */ _overdrive.ui = {}; /* Button variables */ _overdrive.ui.pauseEnabled = false; _overdrive.ui.pauseImg = "img/pause.png"; _overdrive.ui.pauseImgDisabled = "img/pause-disabled.png"; _overdrive.ui.resumeEnabled = false; _overdrive.ui.resumeImg = "img/resume.png"; _overdrive.ui.resumeImgDisabled = "img/resume-disabled.png"; _overdrive.ui.deleteEnabled = false; _overdrive.ui.deleteImg = "img/delete.png"; _overdrive.ui.deleteImgDisabled = "img/delete-disabled.png"; _overdrive.ui.altImg = "img/alt.png"; _overdrive.ui.altImgDisabled = "img/alt-disabled.png"; _overdrive.ui.displayState = [ _overdrive.data.torrentStat.stopped, _overdrive.data.torrentStat.checkWait, _overdrive.data.torrentStat.check, _overdrive.data.torrentStat.downloadWait, _overdrive.data.torrentStat.download, _overdrive.data.torrentStat.seedWait, _overdrive.data.torrentStat.seed ]; _overdrive.ui.sortCol = "name"; _overdrive.ui.sortAsc = true; _overdrive.ui.fileSortCol = "name"; _overdrive.ui.fileSortAsc = true; _overdrive.ui.peerSortCol = "address"; _overdrive.ui.peerSortAsc = true; /* UI Variables (these will be initialized later) */ _overdrive.ui.listScrollX = 0; _overdrive.ui.listScrollY = 0; _overdrive.ui.fileListScrollX = 0; _overdrive.ui.fileListScrollY = 0; _overdrive.ui.peerListScrollX = 0; _overdrive.ui.peerListScrollY = 0; _overdrive.ui.statusTablePos = 0; _overdrive.ui.sideLastX = 0; _overdrive.ui.sideLastY = 0; _overdrive.ui.sideResize = false; _overdrive.ui.vertResize = false; _overdrive.ui.windowDrag = false; _overdrive.ui.activeWindow = null; _overdrive.ui.lastClickedTorrent = -1; _overdrive.ui.lastWidth = 0; _overdrive.ui.lastHeight = 0; _overdrive.ui.detailsPadding = 0; _overdrive.ui.detailsMargin = 0; _overdrive.ui.listMargin = 0; _overdrive.ui.listBorder = 0; _overdrive.ui.canvasColor = ""; _overdrive.ui.fontColor = ""; _overdrive.ui.fontSize = ""; _overdrive.ui.fontFamily = ""; _overdrive.ui.tableCellPadding = 0; // Formats data within the torrents object _overdrive.data.formatData = function(detail) { var torrents; if (detail) torrents = _overdrive.data.torrentsDetail; else torrents = _overdrive.data.torrents; for (var i = 0; i < torrents.length; i++) { // Initial data modification if (torrents[i].status == _overdrive.data.torrentStat.seed && torrents[i].rateUpload <= 0) torrents[i].status = _overdrive.data.torrentStat.seedWait; if (_overdrive.settings.columns.indexOf("eta") != -1) { if (torrents[i].eta < 0 && torrents[i].percentDone < 1) torrents[i].eta = Number.MAX_VALUE; } // Formatting for (var prop in torrents[i]) { if (prop == "percentDone") torrents[i].percentDoneHTML = (torrents[i].percentDone * 100).toFixed(2) + "%"; else if (prop == "totalSize") torrents[i].totalSizeHTML = _overdrive.data.friendlyByteString(torrents[i].totalSize); else if (prop == "rateDownload") torrents[i].rateDownloadHTML = _overdrive.data.friendlyByteString(torrents[i].rateDownload) + "/s"; else if (prop == "rateUpload") torrents[i].rateUploadHTML = _overdrive.data.friendlyByteString(torrents[i].rateUpload) + "/s"; else if (prop == "status") torrents[i].statusHTML = _overdrive.data.torrentStatStrings[torrents[i].status]; else if (prop == "eta") torrents[i].etaHTML = _overdrive.data.friendlyTimeString(torrents[i].eta); else if (prop == "uploadRatio") torrents[i].uploadRatioHTML = torrents[i].uploadRatio.toFixed(4); else torrents[i][prop + "HTML"] = torrents[i][prop]; } } }; // Convert number of bytes to a byte string _overdrive.data.friendlyByteString = function(num) { var units = ""; var kbSize; if (_overdrive.settings.binaryKB) kbSize = 1024; else kbSize = 1000; if (num >= kbSize) { num /= kbSize; units = "K"; } if (num >= kbSize) { num /= kbSize; units = "M"; } if (num >= kbSize) { num /= kbSize; units = "G"; } if (num >= kbSize) { num /= kbSize; units = "T"; } if (num >= kbSize) { num /= kbSize; units = "P"; } if (num >= kbSize) { num /= kbSize; units = "E"; } if (num >= kbSize) { num /= kbSize; units = "Z"; } if (num >= kbSize) { num /= kbSize; units = "Y"; } if (_overdrive.settings.binaryKB && units != "") units += "i"; units += "B"; return num.toFixed(2) + " " + units; }; // Convert number of seconds to a time string _overdrive.data.friendlyTimeString = function(num) { num = num.toFixed(0); if (num < 0) { return "N/A"; } else if (num == Number.MAX_VALUE) { return "Unknown"; } else { var hours = 0, mins = 0; while (num >= 3600) { num -= 3600; hours++; } while (num >= 60) { num -= 60; mins++; } if (hours < 10) hours = "0" + hours; if (mins < 10) mins = "0" + mins; if (num < 10) num = "0" + num; return hours + ":" + mins + ":" + num; } }; // Convert a string in Base64 encoding to a bit string _overdrive.data.base64ToBits = function(str) { var base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var bitString = ""; var pos = 0; while (pos < str.length) { var chunk = str.substr(pos, 4); var count; switch (chunk.lastIndexOf('=')) { case 3: count = 16; break; case 2: count = 8; break; default: count = 24; } for (var i = 0; i < 4; i++) { if (chunk.charAt(i) != '=') { var val = base64.indexOf(chunk.charAt(i)); var div = 32; while (div >= 1) { if (val >= div) { bitString += "1"; val -= div; } else { bitString += "0"; } if (count-- == 1) break; div /= 2; } } } pos += 4; } return bitString; }; // Get detailed torrent information from server about selected torrents _overdrive.data.getDetailedInfo = function() { if (_overdrive.data.selected.length > 0) { var request = {}; request.method = "torrent-get"; request.arguments = {}; request.arguments.ids = _overdrive.data.selected; request.arguments.fields = [ "activityDate", "addedDate", "bandwidthPriority", "comment", "corruptEver", "creator", "dateCreated", "desiredAvailable", "doneDate", "downloadDir", "downloadedEver", "downloadLimit", "downloadLimited", "error", "errorString", "eta", "etaIdle", "files", "fileStats", "hashString", "haveUnchecked", "haveValid", "honorSessionLimits", "id", "isFinished", "isPrivate", "isStalled", "leftUntilDone", "magnetLink", "manualAnnounceTime", "maxConnectedPeers", "metadataPercentComplete", "name", "peer-limit", "peers", "peersConnected", "peersFrom", "peersGettingFromUs", "peersSendingToUs", "percentDone", "pieces", "pieceCount", "pieceSize", "priorities", "queuePosition", "rateDownload", "rateUpload", "recheckProgress", "secondsDownloading", "secondsSeeding", "seedIdleLimit", "seedIdleMode", "seedRatioLimit", "seedRatioMode", "sizeWhenDone", "startDate", "status", "trackers", "trackerStats", "totalSize", "torrentFile", "uploadedEver", "uploadLimit", "uploadLimited", "uploadRatio", "wanted", "webseeds", "webseedsSendingToUs" ]; request.tag = _overdrive.data.lastSentDetail++; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); _overdrive.data.getDetailedInfo(); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") { alert("getDetailedInfo() failed: " + response.result); } else { if (response.tag > _overdrive.data.lastReceivedDetail) { _overdrive.data.lastReceivedDetail = response.tag; _overdrive.data.torrentsDetail = response.arguments.torrents; _overdrive.data.formatData(true); _overdrive.ui.refreshDetails(); } } } } }; } else { _overdrive.data.torrentsDetail = []; _overdrive.ui.refreshDetails(); } }; // Get everything from server _overdrive.data.getEverything = function() { _overdrive.data.getTorrentInfo(); _overdrive.data.getSessionInfo(); _overdrive.data.getDetailedInfo(); }; // Get session information from server _overdrive.data.getSessionInfo = function() { var request = {}; request.method = "session-get"; request.tag = _overdrive.data.lastSentSession++; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); _overdrive.data.getSessionInfo(); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") { alert("getSessionInfo() failed: " + response.result); } else { if (response.tag > _overdrive.data.lastReceivedSession) { _overdrive.data.lastReceivedSession = response.tag; _overdrive.data.session = response.arguments; _overdrive.ui.refreshSettings(); } } } } }; }; // Return the torrent with the specified id _overdrive.data.getTorrentById = function(id) { for (var i = 0; i < _overdrive.data.torrents.length; i++) { if (_overdrive.data.torrents[i].id == id) return _overdrive.data.torrents[i]; } return null; }; // Get torrent information from server _overdrive.data.getTorrentInfo = function() { var request = {}; request.method = "torrent-get"; request.arguments = {}; request.arguments.fields = _overdrive.settings.columns.concat(); if (request.arguments.fields.indexOf("id") == -1) request.arguments.fields.push("id"); if (request.arguments.fields.indexOf("status") == -1) request.arguments.fields.push("status"); if (request.arguments.fields.indexOf("percentDone") == -1) request.arguments.fields.push("percentDone"); request.tag = _overdrive.data.lastSent++; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); _overdrive.data.getTorrentInfo(); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") { alert("getTorrentInfo() failed: " + response.result); } else { if (response.tag > _overdrive.data.lastReceived) { _overdrive.data.lastReceived = response.tag; _overdrive.data.validateSelection(response.arguments.torrents); _overdrive.data.torrents = response.arguments.torrents; _overdrive.data.formatData(false); _overdrive.data.sortData(); _overdrive.ui.refreshControls(); _overdrive.ui.refreshList(); } } } } }; }; // Get the index in the torrent array of the torrent with specified ID _overdrive.data.indexOfID = function(id) { for (var i = 0; i < _overdrive.data.torrents.length; i++) { if (_overdrive.data.torrents[i].id == id) return i; } return -1; }; // Sort data _overdrive.data.sortData = function() { if (_overdrive.ui.sortCol != "") { if (_overdrive.data.stringCols.indexOf(_overdrive.ui.sortCol) == -1) { if (_overdrive.ui.sortAsc) { _overdrive.data.torrents.sort(function(a, b) { return a[_overdrive.ui.sortCol] - b[_overdrive.ui.sortCol]; }); } else { _overdrive.data.torrents.sort(function(a, b) { return b[_overdrive.ui.sortCol] - a[_overdrive.ui.sortCol]; }); } } else { _overdrive.data.torrents.sort(function(a, b) { return a[_overdrive.ui.sortCol].toLowerCase().localeCompare(b[_overdrive.ui.sortCol].toLowerCase()); }); if (!_overdrive.ui.sortAsc) _overdrive.data.torrents.reverse(); } } }; // Validates the currently selected torrents against the // given incoming torrents object _overdrive.data.validateSelection = function(newTorrents) { for (var i = 0; i < _overdrive.data.selected.length; i++) { var curTorrent = _overdrive.data.getTorrentById(_overdrive.data.selected[i]); var exists = false; var display = false; for (var j = 0; j < newTorrents.length; j++) { if (curTorrent.id == newTorrents[j].id && curTorrent.name == newTorrents[j].name) { exists = true; if (_overdrive.ui.displayState.indexOf(newTorrents[j].status) != -1) display = true; break; } } if (!exists || !display) { _overdrive.data.selected.splice(i, 1); i--; } } }; // Attempt to load settings from localStorage _overdrive.settings.loadSettings = function() { if (localStorage.overdriveSideWidth) document.getElementById("overdrive-split-side").style.width = localStorage.overdriveSideWidth + "px"; if (localStorage.overdriveSplitHeight && localStorage.overdriveListHeight) { document.getElementById("overdrive-split").style.height = localStorage.overdriveSplitHeight + "px"; document.getElementById("overdrive-torrent-list").style.height = (localStorage.overdriveListHeight - _overdrive.ui.listBorder) + "px"; } if (localStorage.overdriveBinaryKB) _overdrive.settings.binaryKB = localStorage.overdriveBinaryKB; /*if (localStorage.overdriveColumns) _overdrive.settings.columns = localStorage.overdriveColumns.split(",");*/ if (localStorage.overdriveRefreshRate) _overdrive.settings.refreshRate = localStorage.overdriveRefreshRate; if (localStorage.overdriveSortCol) _overdrive.ui.sortCol = localStorage.overdriveSortCol; if (localStorage.overdriveSortAsc) _overdrive.ui.sortAsc = localStorage.overdriveSortAsc; }; // Save settings to localStorage _overdrive.settings.saveSettings = function() { localStorage.overdriveSideWidth = document.getElementById("overdrive-split-side").clientWidth; localStorage.overdriveSplitHeight = document.getElementById("overdrive-split").clientHeight; localStorage.overdriveListHeight = document.getElementById("overdrive-torrent-list").offsetHeight; localStorage.overdriveBinaryKB = _overdrive.settings.binaryKB; localStorage.overdriveColumns = _overdrive.settings.columns; localStorage.overdriveRefreshRate = _overdrive.settings.refreshRate; localStorage.overdriveSortCol = _overdrive.ui.sortCol; localStorage.overdriveSortAsc = _overdrive.ui.sortAsc; }; // Add button click handler _overdrive.ui.addClick = function() { var menu = document.getElementById("overdrive-add-window"); menu.style.display = "block"; document.getElementById("overdrive-add-url").focus(); // Make sure window is in the screen if (menu.offsetLeft + menu.offsetWidth > window.innerWidth) menu.style.left = (window.innerWidth - menu.offsetWidth) + "px"; if (menu.offsetTop + menu.offsetHeight > window.innerHeight) menu.style.top = (window.innerHeight - menu.offsetHeight) + "px"; }; // Click handler for cancel button in add menu _overdrive.ui.addCancelClick = function() { document.getElementById("overdrive-add-form").reset(); document.getElementById("overdrive-add-window").style.display = "none"; }; // Click handler for upload button in add menu _overdrive.ui.addUploadClick = function() { var addForm = document.getElementById("overdrive-add-form"); var fileSelect = document.getElementById("overdrive-add-file"); var urlBox = document.getElementById("overdrive-add-url"); var started = document.getElementById("overdrive-add-started"); if (urlBox.value != "") { // Make nyaa.eu more convenient if (urlBox.value.indexOf("nyaa.eu" != -1)) { urlBox.value = urlBox.value.replace("page=view", "page=download"); urlBox.value = urlBox.value.replace("page=torrentinfo", "page=download"); } var request = {}; request.method = "torrent-add"; request.arguments = {}; request.arguments.filename = urlBox.value; request.arguments.paused = !started.checked; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); alert("Session timed out. Please try again."); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") alert("addUploadClick() failed: " + response.result); } } }; addForm.reset(); document.getElementById("overdrive-add-window").style.display = "none"; } }; // Alt button click handler _overdrive.ui.altClick = function() { var request = {}; request.method = "session-set"; request.arguments = {}; request.arguments["alt-speed-enabled"] = !_overdrive.data.session["alt-speed-enabled"]; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); _overdrive.ui.altClick(); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") alert("altClick() failed: " + response.result); } } }; }; // Delete button click handler _overdrive.ui.deleteClick = function() { if (_overdrive.ui.deleteEnabled) { var menu = document.getElementById("overdrive-delete-window"); menu.style.display = "block"; // Make sure window is in the screen if (menu.offsetLeft + menu.offsetWidth > window.innerWidth) menu.style.left = (window.innerWidth - menu.offsetWidth) + "px"; if (menu.offsetTop + menu.offsetHeight > window.innerHeight) menu.style.top = (window.innerHeight - menu.offsetHeight) + "px"; } }; // Delete cancel click handler _overdrive.ui.deleteCancelClick = function() { document.getElementById("overdrive-delete-window").style.display = "none"; }; // Delete confirm click handler _overdrive.ui.deleteConfirmClick = function() { var request = {}; request.method = "torrent-remove"; request.arguments = {}; request.arguments.ids = _overdrive.data.selected; request.arguments["delete-local-data"] = document.getElementById("overdrive-delete-data").checked; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); _overdrive.ui.deleteConfirmClick(); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") alert("deleteConfirmClick() failed: " + response.result); } } }; document.getElementById("overdrive-delete-window").style.display = "none"; }; // File header column click handler _overdrive.ui.fileHeaderColClick = function(event) { var search = "overdrive-file-column-"; var colName = event.currentTarget.className.substr(event.currentTarget.className.indexOf(search) + search.length); if (colName == _overdrive.ui.fileSortCol) { _overdrive.ui.fileSortAsc = !_overdrive.ui.fileSortAsc; } else { _overdrive.ui.fileSortCol = colName; _overdrive.ui.fileSortAsc = true; } _overdrive.ui.refreshDetails(); }; // File list scroll handler _overdrive.ui.fileListScroll = function() { _overdrive.ui.fileListScrollY = document.getElementById("overdrive-file-list").scrollTop; _overdrive.ui.fileListScrollX = document.getElementById("overdrive-file-list").scrollLeft; document.getElementById("overdrive-file-list-header").style.top = _overdrive.ui.fileListScrollY + "px"; }; // Header column click handler _overdrive.ui.headerColClick = function(event) { var search = "overdrive-column-"; var colName = event.currentTarget.className.substr(event.currentTarget.className.indexOf(search) + search.length); if (colName == _overdrive.ui.sortCol) { _overdrive.ui.sortAsc = !_overdrive.ui.sortAsc; } else { _overdrive.ui.sortCol = colName; _overdrive.ui.sortAsc = true; } _overdrive.data.sortData(); _overdrive.ui.refreshList(); }; // Key handler _overdrive.ui.keyHandler = function(event) { // Ctrl+A - Select all torrents if (event.ctrlKey && event.which == 65) { _overdrive.data.selected = []; for (var i = 0; i < _overdrive.data.torrents.length; i++) { if (_overdrive.ui.displayState.indexOf(_overdrive.data.torrents[i].status) != -1) _overdrive.data.selected.push(_overdrive.data.torrents[i].id); } _overdrive.ui.refreshControls(); _overdrive.ui.refreshList(); _overdrive.data.getDetailedInfo(); event.preventDefault(); } }; // List click handler _overdrive.ui.listClick = function(event) { if (event.target.id == "overdrive-torrent-list") { _overdrive.data.selected = []; _overdrive.ui.refreshControls(); _overdrive.ui.refreshList(); _overdrive.data.getDetailedInfo(); } }; // List scroll event handler _overdrive.ui.listScroll = function(event) { // Keep the header row on top _overdrive.ui.listScrollY = document.getElementById("overdrive-torrent-list").scrollTop; _overdrive.ui.listScrollX = document.getElementById("overdrive-torrent-list").scrollLeft; document.getElementById("overdrive-torrent-list-header").style.top = _overdrive.ui.listScrollY + "px"; }; // Mouse move event handler _overdrive.ui.mouseMove = function(event) { if (_overdrive.ui.sideResize) { var sidePane = document.getElementById("overdrive-split-side"); sidePane.style.width = (sidePane.clientWidth + (event.clientX - _overdrive.ui.sideLastX)) + "px"; } if (_overdrive.ui.vertResize) { var splitView = document.getElementById("overdrive-split"); var torList = document.getElementById("overdrive-torrent-list"); var details = document.getElementById("overdrive-tab-contents"); splitView.style.height = (splitView.clientHeight + (event.clientY - _overdrive.ui.sideLastY)) + "px"; torList.style.height = (splitView.clientHeight - (_overdrive.ui.listMargin + _overdrive.ui.listBorder)) + "px"; var diff = window.innerHeight - document.getElementById("overdrive").clientHeight; details.style.height = ((details.clientHeight + diff) - (_overdrive.ui.detailsPadding + _overdrive.ui.detailsMargin)) + "px"; _overdrive.ui.refreshDetails(); } if (_overdrive.ui.windowDrag) { _overdrive.ui.activeWindow.style.left = (_overdrive.ui.activeWindow.offsetLeft + (event.clientX - _overdrive.ui.sideLastX)) + "px"; _overdrive.ui.activeWindow.style.top = (_overdrive.ui.activeWindow.offsetTop + (event.clientY - _overdrive.ui.sideLastY)) + "px"; } _overdrive.ui.sideLastX = event.clientX; _overdrive.ui.sideLastY = event.clientY; }; // Pause click handler _overdrive.ui.pauseClick = function() { if (_overdrive.ui.pauseEnabled) { var request = {}; request.method = "torrent-stop"; request.arguments = {}; request.arguments.ids = _overdrive.data.selected; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); _overdrive.ui.pauseClick(); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") alert("pauseClick() failed: " + response.result); } } }; } }; // Peer header column click handler _overdrive.ui.peerHeaderColClick = function(event) { var search = "overdrive-peer-column-"; var colName = event.currentTarget.className.substr(event.currentTarget.className.indexOf(search) + search.length); if (colName == _overdrive.ui.peerSortCol) { _overdrive.ui.peerSortAsc = !_overdrive.ui.peerSortAsc; } else { _overdrive.ui.peerSortCol = colName; _overdrive.ui.peerSortAsc = true; } _overdrive.ui.refreshDetails(); }; // Peer list scroll handler _overdrive.ui.peerListScroll = function() { _overdrive.ui.peerListScrollY = document.getElementById("overdrive-peer-list").scrollTop; _overdrive.ui.peerListScrollX = document.getElementById("overdrive-peer-list").scrollLeft; document.getElementById("overdrive-peer-list-header").style.top = _overdrive.ui.peerListScrollY + "px"; }; // Refresh the controls at the top _overdrive.ui.refreshControls = function() { var pauseButton = document.getElementById("overdrive-pause-button"); var resumeButton = document.getElementById("overdrive-resume-button"); var deleteButton = document.getElementById("overdrive-delete-button"); _overdrive.ui.pauseEnabled = false; _overdrive.ui.resumeEnabled = false; for (var i = 0; i < _overdrive.data.selected.length; i++) { var curTorrent = _overdrive.data.getTorrentById(_overdrive.data.selected[i]); if (curTorrent.status == _overdrive.data.torrentStat.stopped) _overdrive.ui.resumeEnabled = true; else _overdrive.ui.pauseEnabled = true; } _overdrive.ui.deleteEnabled = (_overdrive.data.selected.length > 0); if (_overdrive.ui.pauseEnabled) pauseButton.src = _overdrive.ui.pauseImg; else pauseButton.src = _overdrive.ui.pauseImgDisabled; if (_overdrive.ui.resumeEnabled) resumeButton.src = _overdrive.ui.resumeImg; else resumeButton.src = _overdrive.ui.resumeImgDisabled; if (_overdrive.ui.deleteEnabled) deleteButton.src = _overdrive.ui.deleteImg; else deleteButton.src = _overdrive.ui.deleteImgDisabled; }; // Refresh the torrent details section _overdrive.ui.refreshDetails = function() { if (_overdrive.data.selected.length == 0) _overdrive.data.torrentsDetail = []; var tab = document.getElementsByClassName("overdrive-tab-active")[0].id; tab = tab.slice(tab.indexOf('-') + 1, tab.lastIndexOf('-')); var tabContents = document.getElementById("overdrive-tab-contents"); if (tab == "status") { // Create progress bar var canvas = document.getElementById("overdrive-progress-detail"); canvas.height = canvas.clientHeight; canvas.width = canvas.clientWidth; // Calculate totals var totalValid = 0, totalUnchecked = 0, totalUp = 0, ratio = 0; var announce = 0, tracker = "", totalDownSpeed = 0, totalUpSpeed = 0; var eta = -Number.MAX_VALUE, pieces = "", connectedSeeds = 0, totalSeeds = 0; var connectedPeers = 0, totalPeers = 0, availability = 0; var activeTime = 0, seedTime = 0; for (var i = 0; i < _overdrive.data.torrentsDetail.length; i++) { totalValid += _overdrive.data.torrentsDetail[i].haveValid; totalUnchecked += _overdrive.data.torrentsDetail[i].haveUnchecked; totalUp += _overdrive.data.torrentsDetail[i].uploadedEver; totalDownSpeed += _overdrive.data.torrentsDetail[i].rateDownload; totalUpSpeed += _overdrive.data.torrentsDetail[i].rateUpload; activeTime += _overdrive.data.torrentsDetail[i].secondsDownloading; activeTime += _overdrive.data.torrentsDetail[i].secondsSeeding; seedTime += _overdrive.data.torrentsDetail[i].secondsSeeding; availability = (Number(availability) + _overdrive.data.torrentsDetail[i].percentDone).toFixed(4); for (var j = 0; j < _overdrive.data.torrentsDetail[i].peers.length; j++) { availability = (Number(availability) + _overdrive.data.torrentsDetail[i].peers[j].progress).toFixed(4); if (_overdrive.data.torrentsDetail[i].peers[j].progress == 1) connectedSeeds++; else connectedPeers++; } ratio = _overdrive.data.torrentsDetail[i].uploadRatio.toFixed(4); announce = Number.MAX_VALUE; for (var j = 0; j < _overdrive.data.torrentsDetail[i].trackerStats.length; j++) { if (_overdrive.data.torrentsDetail[i].trackerStats[j].nextAnnounceTime < announce) announce = _overdrive.data.friendlyTimeString(_overdrive.data.torrentsDetail[i].trackerStats[j].nextAnnounceTime - (new Date().getTime() / 1000)); tracker += _overdrive.data.torrentsDetail[i].trackerStats[j].host; tracker += " - "; if (_overdrive.data.torrentsDetail[i].trackerStats[j].lastAnnounceResult == "Success") tracker += ""; else tracker += ""; tracker += _overdrive.data.torrentsDetail[i].trackerStats[j].lastAnnounceResult; tracker += "
"; if (_overdrive.data.torrentsDetail[i].trackerStats[j].seederCount > 0) totalSeeds += _overdrive.data.torrentsDetail[i].trackerStats[j].seederCount; if (_overdrive.data.torrentsDetail[i].trackerStats[j].leecherCount > 0) totalPeers += _overdrive.data.torrentsDetail[i].trackerStats[j].leecherCount; } if (_overdrive.data.torrentsDetail[i].eta > eta) eta = _overdrive.data.torrentsDetail[i].eta; pieces = _overdrive.data.torrentsDetail[i].pieceCount; pieces += " ("; pieces += _overdrive.data.friendlyByteString(_overdrive.data.torrentsDetail[i].pieceSize); pieces += ")"; if (i != 0) { ratio = ""; announce = ""; tracker = ""; pieces = ""; availability = ""; ratio = (totalUp / totalValid).toFixed(4); } } // Define table information var tableInfo = [ [ [ "Downloaded:", _overdrive.data.friendlyByteString(totalValid) + " (" + _overdrive.data.friendlyByteString(totalUnchecked) + ")" ], [ "Uploaded:", _overdrive.data.friendlyByteString(totalUp) ], [ "Share Ratio:", ratio ], [ "Next Announce:", announce ], [ "Tracker Status:", tracker ] ], [ [ "Down Speed:", _overdrive.data.friendlyByteString(totalDownSpeed) + "/s" ], [ "Up Speed:", _overdrive.data.friendlyByteString(totalUpSpeed) + "/s" ], [ "ETA:", _overdrive.data.friendlyTimeString(eta) ], [ "Pieces:", pieces ] ], [ [ "Seeders:", connectedSeeds + " (" + totalSeeds + ")" ], [ "Peers:", connectedPeers + " (" + totalPeers + ")" ], [ "Availability:", availability ] ], [ [ "Active Time:", _overdrive.data.friendlyTimeString(activeTime) ], [ "Seeding Time:", _overdrive.data.friendlyTimeString(seedTime) ] ] ]; // Create table var statusTable = document.getElementById("overdrive-status-table"); statusTable.style.height = (Number(tabContents.style.height.slice(0, -2)) - canvas.height) + "px"; statusTable.innerHTML = ""; var statusTableBody = document.createElement("tbody"); var statusTableBodyRow = document.createElement("tr"); for (var i = 0; i < tableInfo.length; i++) { var statusTableBodyCol = document.createElement("td"); statusTableBodyCol.style.width = (statusTable.clientWidth / tableInfo.length) + "px"; var colTable = document.createElement("table"); var colTableBody = document.createElement("tbody"); for (var j = 0; j < tableInfo[i].length; j++) { var row = document.createElement("tr"); var rowTitle = document.createElement("td"); var rowBody = document.createElement("td"); rowTitle.innerHTML = tableInfo[i][j][0]; if (_overdrive.data.torrentsDetail.length == 0) rowBody.innerHTML = ""; else rowBody.innerHTML = tableInfo[i][j][1]; row.appendChild(rowTitle); row.appendChild(rowBody); colTableBody.appendChild(row); } colTable.appendChild(colTableBody); statusTableBodyCol.appendChild(colTable); statusTableBodyRow.appendChild(statusTableBodyCol); } statusTableBody.appendChild(statusTableBodyRow); statusTable.appendChild(statusTableBody); statusTable.scrollTop = _overdrive.ui.statusTablePos; // Draw to progress bar var ctx = canvas.getContext("2d"); ctx.clearRect(0, 0, canvas.width, canvas.height); if (_overdrive.data.torrentsDetail.length == 1) { ctx.fillStyle = _overdrive.ui.canvasColor; var bits = _overdrive.data.base64ToBits(_overdrive.data.torrentsDetail[0].pieces); var len = _overdrive.data.torrentsDetail[0].pieceCount; var i = 0, count = 0, cont = -1; while (i < len) { if (cont >= 0) { if (bits.charAt(i) == '1') count++; if (bits.charAt(i) == '0' || i == len - 1) { ctx.fillRect(cont / len * canvas.width, 0, count / len * canvas.width, canvas.height); cont = -1; } } else { if (bits.charAt(i) == '1') { cont = i; count = 1; } } i++; } var str = _overdrive.data.torrentsDetail[0].statusHTML + " " + _overdrive.data.torrentsDetail[0].percentDoneHTML; ctx.font = _overdrive.ui.fontSize + " " + _overdrive.ui.fontFamily; ctx.fillStyle = _overdrive.ui.fontColor; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(str, canvas.width / 2, canvas.height / 2); } else if (_overdrive.data.torrentsDetail.length > 1) { ctx.font = _overdrive.ui.fontSize + " " + _overdrive.ui.fontFamily; ctx.fillStyle = _overdrive.ui.fontColor; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText("Multiple selected", canvas.width / 2, canvas.height / 2); } } else if (tab == "files") { var files = []; for (var i = 0; i < _overdrive.data.torrentsDetail.length; i++) { for (var j = 0; j < _overdrive.data.torrentsDetail[i].files.length; j++) { var curFile = {}; curFile.name = _overdrive.data.torrentsDetail[i].files[j].name; var slashInd = curFile.name.lastIndexOf("/"); if (slashInd != -1) { curFile.nameHTML = "" + curFile.name.substr(0, slashInd + 1) + ""; curFile.nameHTML += curFile.name.substr(slashInd + 1); } else { curFile.nameHTML = curFile.name; } curFile.totalSize = _overdrive.data.torrentsDetail[i].files[j].length; curFile.totalSizeHTML = _overdrive.data.friendlyByteString(curFile.totalSize); curFile.percentDone = _overdrive.data.torrentsDetail[i].files[j].bytesCompleted / _overdrive.data.torrentsDetail[i].files[j].length; curFile.percentDoneHTML = (curFile.percentDone * 100).toFixed(2) + "%"; curFile.wanted = _overdrive.data.torrentsDetail[i].fileStats[j].wanted curFile.wantedHTML = ">> 0 to the end makes the result get interpreted as unsigned // This is necessary when addressSeg[0] is 128 or higher (aka the upper half of IPv4 addresses) curPeer.address += Number(addressSeg[0]) << 24 >>> 0; curPeer.address += "." + _overdrive.data.torrentsDetail[i].peers[j].port; curPeer.address = Number(curPeer.address); curPeer.addressHTML = _overdrive.data.torrentsDetail[i].peers[j].address; curPeer.addressHTML += ":" + _overdrive.data.torrentsDetail[i].peers[j].port; curPeer.clientName = _overdrive.data.torrentsDetail[i].peers[j].clientName; curPeer.clientNameHTML = curPeer.clientName; curPeer.progress = _overdrive.data.torrentsDetail[i].peers[j].progress; curPeer.progressHTML = (curPeer.progress * 100).toFixed(2) + "%"; curPeer.rateToClient = _overdrive.data.torrentsDetail[i].peers[j].rateToClient; curPeer.rateToClientHTML = _overdrive.data.friendlyByteString(curPeer.rateToClient) + "/s"; curPeer.rateToPeer = _overdrive.data.torrentsDetail[i].peers[j].rateToPeer; curPeer.rateToPeerHTML = _overdrive.data.friendlyByteString(curPeer.rateToPeer) + "/s"; peers[peers.length] = curPeer; } } var peerList = document.getElementById("overdrive-peer-list"); peerList.style.height = tabContents.style.height; var peerListHeader = document.getElementById("overdrive-peer-list-header"); peerListHeader.innerHTML = ""; var columns = ["address", "clientName", "progress", "rateToClient", "rateToPeer"]; var stringCols = ["clientName"]; var columnNames = {}; columnNames.address = "Address"; columnNames.clientName = "Client"; columnNames.progress = "Progress"; columnNames.rateToClient = "Down Speed"; columnNames.rateToPeer = "Up Speed"; // Sort peers if (stringCols.indexOf(_overdrive.ui.peerSortCol) == -1) { if (_overdrive.ui.peerSortAsc) { peers.sort(function(a, b) { return a[_overdrive.ui.peerSortCol] - b[_overdrive.ui.peerSortCol]; }); } else { peers.sort(function(a, b) { return b[_overdrive.ui.peerSortCol] - a[_overdrive.ui.peerSortCol]; }); } } else { peers.sort(function(a, b) { return a[_overdrive.ui.peerSortCol].toLowerCase().localeCompare(b[_overdrive.ui.peerSortCol].toLowerCase()); }); if (!_overdrive.ui.peerSortAsc) peers.reverse(); } for (var i = 0; i < columns.length; i++) { var column = document.createElement("div"); column.className = "overdrive-header-column overdrive-peer-column-" + columns[i]; column.onmousedown = _overdrive.ui.peerHeaderColClick; column.innerHTML = columnNames[columns[i]]; peerListHeader.appendChild(column); } var peerListBody = document.getElementById("overdrive-peer-list-body"); peerListBody.innerHTML = ""; for (var i = 0; i < peers.length; i++) { var curPeer = document.createElement("div"); curPeer.className = "overdrive-peer overdrive-table-entry"; for (var j = 0; j < columns.length; j++) { var curCol = document.createElement("div"); curCol.className = "overdrive-peer-column-" + columns[j]; curCol.innerHTML = peers[i][columns[j] + "HTML"]; curPeer.appendChild(curCol); } peerListBody.appendChild(curPeer); peerListBody.appendChild(document.createElement("br")); } for (var i = 0; i < columns.length; i++) { var maxWidth = 0; var cells = document.getElementsByClassName("overdrive-peer-column-" + columns[i]); for (var j = 0; j < cells.length; j++) maxWidth = Math.max(maxWidth, cells[j].scrollWidth - _overdrive.ui.tableCellPadding); for (var j = 0; j < cells.length; j++) cells[j].style.width = maxWidth + "px"; } peerList.scrollTop = _overdrive.ui.peerListScrollY; peerList.scrollLeft = _overdrive.ui.peerListScrollX; } }; // Refresh the torrent and state lists _overdrive.ui.refreshList = function() { var header = document.getElementById("overdrive-torrent-list-header"); header.innerHTML = ""; var list = document.getElementById("overdrive-torrent-list-body"); list.innerHTML = ""; var downloadCount = 0; var seedCount = 0; var activeCount = 0; var pauseCount = 0; // Generate list header for (var i = 0; i < _overdrive.settings.columns.length; i++) { var cell = document.createElement("div"); cell.className = "overdrive-header-column overdrive-column-" + _overdrive.settings.columns[i]; cell.innerHTML = _overdrive.data.columnNames[_overdrive.settings.columns[i]]; cell.onmousedown = _overdrive.ui.headerColClick; header.appendChild(cell); } // Generate list body for (var i = 0; i < _overdrive.data.torrents.length; i++) { if (_overdrive.ui.displayState.indexOf(_overdrive.data.torrents[i].status) != -1) { var row = document.createElement("div"); row.id = "overdrive-torrent-" + _overdrive.data.torrents[i].id; row.className = "overdrive-torrent overdrive-table-entry"; if (_overdrive.data.selected.indexOf(_overdrive.data.torrents[i].id) != -1) row.className += " overdrive-torrent-selected"; for (var j = 0; j < _overdrive.settings.columns.length; j++) { var cell = document.createElement("div"); cell.className = "overdrive-column-" + _overdrive.settings.columns[j]; cell.innerHTML = _overdrive.data.torrents[i][_overdrive.settings.columns[j] + "HTML"]; row.appendChild(cell); } row.onmousedown = _overdrive.ui.torrentClick; list.appendChild(row); list.appendChild(document.createElement("br")); } switch (_overdrive.data.torrents[i].status) { case _overdrive.data.torrentStat.stopped: pauseCount++; break; case _overdrive.data.torrentStat.downloadWait: downloadCount++; break; case _overdrive.data.torrentStat.download: activeCount++; downloadCount++; break; case _overdrive.data.torrentStat.seedWait: seedCount++; break; case _overdrive.data.torrentStat.seed: activeCount++; seedCount++; break; } } // Update state counters document.getElementById("overdrive-state-all").innerHTML = "All (" + _overdrive.data.torrents.length + ")"; document.getElementById("overdrive-state-downloading").innerHTML = "Downloading (" + downloadCount + ")"; document.getElementById("overdrive-state-seeding").innerHTML = "Seeding (" + seedCount + ")"; document.getElementById("overdrive-state-active").innerHTML = "Active (" + activeCount + ")"; document.getElementById("overdrive-state-paused").innerHTML = "Paused (" + pauseCount + ")"; // Auto-resize list columns for (var i = 0; i < _overdrive.settings.columns.length; i++) { var maxWidth = 0; var cols = document.getElementsByClassName("overdrive-column-" + _overdrive.settings.columns[i]); for (var j = 0; j < cols.length; j++) maxWidth = Math.max(maxWidth, cols[j].scrollWidth - _overdrive.ui.tableCellPadding); for (var j = 0; j < cols.length; j++) cols[j].style.width = maxWidth + "px"; } document.getElementById("overdrive-torrent-list").scrollTop = _overdrive.ui.listScrollY; document.getElementById("overdrive-torrent-list").scrollLeft = _overdrive.ui.listScrollX; }; // Refresh settings windows _overdrive.ui.refreshSettings = function() { if (_overdrive.data.session["alt-speed-enabled"]) document.getElementById("overdrive-alt-button").src = _overdrive.ui.altImg; else document.getElementById("overdrive-alt-button").src = _overdrive.ui.altImgDisabled; }; // Resize handler _overdrive.ui.resize = function() { // Redraw the details pane _overdrive.ui.refreshDetails(); // Make sure floating menus are still in the screen var menus = document.getElementsByClassName("overdrive-window"); for (var i = 0; i < menus.length; i++) { if (menus[i].offsetLeft + menus[i].offsetWidth > window.innerWidth) menus[i].style.left = (window.innerWidth - menus[i].offsetWidth) + "px"; if (menus[i].offsetTop + menus[i].offsetHeight > window.innerHeight) menus[i].style.top = (window.innerHeight - menus[i].offsetHeight) + "px"; } // Resize the details pane with the window var splitView = document.getElementById("overdrive-split"); var torList = document.getElementById("overdrive-torrent-list"); var details = document.getElementById("overdrive-tab-contents"); splitView.style.height = (splitView.clientHeight + (window.innerHeight - _overdrive.ui.lastHeight)) + "px"; torList.style.height = (splitView.clientHeight - (_overdrive.ui.listMargin + _overdrive.ui.listBorder)) + "px"; var diff = window.innerHeight - document.getElementById("overdrive").clientHeight; details.style.height = ((details.clientHeight + diff) - (_overdrive.ui.detailsPadding + _overdrive.ui.detailsMargin)) + "px"; _overdrive.ui.lastHeight = window.innerHeight; _overdrive.ui.lastWidth = window.innerWidth; }; // Resume click handler _overdrive.ui.resumeClick = function() { if (_overdrive.ui.resumeEnabled) { var request = {}; request.method = "torrent-start"; request.arguments = {}; request.arguments.ids = _overdrive.data.selected; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); _overdrive.ui.resumeClick(); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") alert("resumeClick() failed: " + response.result); } } }; } }; // State click handler _overdrive.ui.stateClick = function(event) { var state = event.currentTarget.id.substr("overdrive-state-".length); if (event.currentTarget.className.search("overdrive-state-active") != -1) return; if (state == "all") { _overdrive.ui.displayState = [ _overdrive.data.torrentStat.stopped, _overdrive.data.torrentStat.checkWait, _overdrive.data.torrentStat.check, _overdrive.data.torrentStat.downloadWait, _overdrive.data.torrentStat.download, _overdrive.data.torrentStat.seedWait, _overdrive.data.torrentStat.seed ]; } else if (state == "downloading") { _overdrive.ui.displayState = [ _overdrive.data.torrentStat.downloadWait, _overdrive.data.torrentStat.download ]; } else if (state == "seeding") { _overdrive.ui.displayState = [ _overdrive.data.torrentStat.seedWait, _overdrive.data.torrentStat.seed ]; } else if (state == "active") { _overdrive.ui.displayState = [ _overdrive.data.torrentStat.download, _overdrive.data.torrentStat.seed ]; } else if (state == "paused") _overdrive.ui.displayState = [_overdrive.data.torrentStat.stopped]; var stateLabels = document.getElementsByClassName("overdrive-state"); for (var i = 0; i < stateLabels.length; i++) { var className = "overdrive-state"; if (stateLabels[i] == event.currentTarget) className += " overdrive-state-active"; stateLabels[i].className = className; } _overdrive.data.selected = []; _overdrive.ui.refreshControls(); _overdrive.ui.refreshList(); _overdrive.data.getDetailedInfo(); }; // Status table scroll handler _overdrive.ui.statusTableScroll = function() { _overdrive.ui.statusTablePos = document.getElementById("overdrive-status-table").scrollTop; }; // Tab click handler _overdrive.ui.tabClick = function(event) { var activeTab = document.getElementsByClassName("overdrive-tab-active")[0]; if (event.currentTarget != activeTab) { activeTab.className = "overdrive-tab"; event.currentTarget.className += " overdrive-tab-active"; var tab = event.currentTarget.id; tab = tab.slice(tab.indexOf('-') + 1, tab.lastIndexOf('-')); var tabContents = document.getElementById("overdrive-tab-contents"); tabContents.innerHTML = ""; if (tab == "status") { var canvas = document.createElement("canvas"); canvas.id = "overdrive-progress-detail"; tabContents.appendChild(canvas); var statusTable = document.createElement("table"); statusTable.id = "overdrive-status-table"; statusTable.onscroll = _overdrive.ui.statusTableScroll; tabContents.appendChild(statusTable); } else if (tab == "files") { var fileList = document.createElement("div"); fileList.id = "overdrive-file-list"; fileList.className = "overdrive-table"; fileList.onscroll = _overdrive.ui.fileListScroll; var fileListHeader = document.createElement("div"); fileListHeader.id = "overdrive-file-list-header"; fileListHeader.className = "overdrive-table-header"; var loading = document.createElement("div"); loading.innerHTML = "Loading..."; fileListHeader.appendChild(loading); fileList.appendChild(fileListHeader); tabContents.appendChild(fileList); var fileListBody = document.createElement("div"); fileListBody.id = "overdrive-file-list-body"; fileListBody.className = "overdrive-table-body"; fileListBody.style.paddingTop = fileListHeader.offsetHeight + "px"; fileList.appendChild(fileListBody); } else if (tab == "peers") { var peerList = document.createElement("div"); peerList.id = "overdrive-peer-list"; peerList.className = "overdrive-table"; peerList.onscroll = _overdrive.ui.peerListScroll; var peerListHeader = document.createElement("div"); peerListHeader.id = "overdrive-peer-list-header"; peerListHeader.className = "overdrive-table-header"; var loading = document.createElement("div"); loading.innerHTML = "Loading..."; peerListHeader.appendChild(loading); peerList.appendChild(peerListHeader); tabContents.appendChild(peerList); var peerListBody = document.createElement("div"); peerListBody.id = "overdrive-peer-list-body"; peerListBody.className = "overdrive-table-body"; peerListBody.style.paddingTop = peerListHeader.offsetHeight + "px"; peerList.appendChild(peerListBody); } else { tabContents.innerHTML += tab; } _overdrive.ui.refreshDetails(); } }; // Torrent click handler (for selecting torrents) _overdrive.ui.torrentClick = function(event) { var num = Number(event.currentTarget.id.substr("overdrive-torrent-".length)); if (!event.ctrlKey) _overdrive.data.selected = []; if (event.shiftKey) { if (_overdrive.ui.lastClickedTorrent == -1) { _overdrive.data.selected.push(num); } else { var numInd = _overdrive.data.indexOfID(num); var lastInd = _overdrive.data.indexOfID(_overdrive.ui.lastClickedTorrent); var start, end; if (numInd > lastInd) { start = lastInd; end = numInd; } else { start = numInd; end = lastInd; } for (var i = start; i <= end; i++) { if (_overdrive.ui.displayState.indexOf(_overdrive.data.torrents[i].status) != -1) _overdrive.data.selected.push(_overdrive.data.torrents[i].id); } } } else { if (event.ctrlKey) { var index = _overdrive.data.selected.indexOf(num); if (index == -1) _overdrive.data.selected.push(num); else _overdrive.data.selected.splice(index, 1); } else { _overdrive.data.selected.push(num); } _overdrive.ui.lastClickedTorrent = num; } _overdrive.ui.fileListScrollX = 0; _overdrive.ui.fileListScrollY = 0; _overdrive.ui.peerListScrollX = 0; _overdrive.ui.peerListScrollY = 0; _overdrive.ui.statusTablePos = 0; _overdrive.ui.refreshList(); _overdrive.ui.refreshControls(); _overdrive.data.getDetailedInfo(); event.preventDefault(); }; // File wanted checkbox click handler _overdrive.ui.wantedBoxClick = function(event) { var boxID = event.currentTarget.id.split("-"); var torrentID = Number(boxID[boxID.length - 2]); var fileID = Number(boxID[boxID.length - 1]); var request = {}; request.method = "torrent-set"; request.arguments = {}; request.arguments.ids = [torrentID]; if (event.currentTarget.checked) request.arguments["files-wanted"] = [fileID]; else request.arguments["files-unwanted"] = [fileID]; var xmlhttp; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("POST", _overdrive.settings.rpcURL, true); xmlhttp.setRequestHeader("X-Transmission-Session-Id", _overdrive.settings.sessionID); var postData = JSON.stringify(request); xmlhttp.setRequestHeader("Content-Type", "application/json"); xmlhttp.send(postData); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 409) { _overdrive.settings.sessionID = xmlhttp.getResponseHeader("X-Transmission-Session-Id"); _overdrive.ui.wantedBoxClick(event); } else if (xmlhttp.status == 200) { var response = JSON.parse(xmlhttp.responseText); if (response.result != "success") alert("wantedBoxClick() failed: " + response.result); else _overdrive.data.getEverything(); } } }; }; // Window close button click handler _overdrive.ui.windowCloseClick = function(event) { event.currentTarget.parentNode.parentNode.style.display = "none"; }; // Titlebar mousedown handler _overdrive.ui.windowTitleMouseDown = function(event) { _overdrive.ui.activeWindow = event.currentTarget.parentNode; _overdrive.ui.windowDrag = true; event.preventDefault(); }; // Stuff to do when window is finished loading _overdrive.onLoad = function() { _overdrive.ui.lastHeight = window.innerHeight; _overdrive.ui.lastWidth = window.innerWidth; // Load some values from CSS sheet var cssSheet = null; for (var i = 0; i < document.styleSheets.length; i++) { if (document.styleSheets[i].href.indexOf("overdrive.css") != -1) { cssSheet = document.styleSheets[i]; break; } } if (cssSheet != null) { for (var i = 0; i < cssSheet.cssRules.length; i++) { if (cssSheet.cssRules[i].selectorText == "#overdrive") { _overdrive.ui.fontColor = cssSheet.cssRules[i].style.color; _overdrive.ui.fontSize = cssSheet.cssRules[i].style.fontSize; _overdrive.ui.fontFamily = cssSheet.cssRules[i].style.fontFamily; } else if (cssSheet.cssRules[i].selectorText == "#overdrive-progress-detail") { _overdrive.ui.canvasColor = cssSheet.cssRules[i].style.color; } else if (cssSheet.cssRules[i].selectorText == "#overdrive-tab-contents") { var margin = cssSheet.cssRules[i].style.marginBottom.slice(0, cssSheet.cssRules[i].style.marginBottom.indexOf("px")); _overdrive.ui.detailsMargin = Number(margin); var padding = cssSheet.cssRules[i].style.padding.slice(0, cssSheet.cssRules[i].style.padding.indexOf("px")); _overdrive.ui.detailsPadding = Number(padding) * 2; } else if (cssSheet.cssRules[i].selectorText == "#overdrive-torrent-list") { var border = cssSheet.cssRules[i].style.border.slice(0, cssSheet.cssRules[i].style.border.indexOf("px")); _overdrive.ui.listBorder = Number(border) * 2; var margin = cssSheet.cssRules[i].style.margin.slice(0, cssSheet.cssRules[i].style.margin.indexOf("px")); _overdrive.ui.listMargin = Number(margin) * 2; } else if (cssSheet.cssRules[i].selectorText == ".overdrive-table-header div") { var padding = cssSheet.cssRules[i].style.paddingLeft.slice(0, cssSheet.cssRules[i].style.paddingLeft.indexOf("px")); _overdrive.ui.tableCellPadding = Number(padding) * 2; } } } // Attempt to load stored UI settings from localStorage _overdrive.settings.loadSettings(); // Attach event handlers document.getElementById("overdrive-torrent-list").onmousedown = _overdrive.ui.listClick; document.getElementById("overdrive-torrent-list").onscroll = _overdrive.ui.listScroll; document.getElementById("overdrive-split-separator").onmousedown = function(event) { _overdrive.ui.sideResize = true; event.preventDefault(); }; document.getElementById("overdrive-vert-separator").onmousedown = function(event) { _overdrive.ui.vertResize = true; event.preventDefault(); }; document.getElementById("overdrive").onmouseup = function() { _overdrive.ui.sideResize = false; _overdrive.ui.vertResize = false; _overdrive.ui.windowDrag = false; }; document.getElementById("overdrive").onmousemove = _overdrive.ui.mouseMove; document.getElementById("overdrive").onkeydown = _overdrive.ui.keyHandler; // Attach click handlers document.getElementById("overdrive-add-button").onclick = _overdrive.ui.addClick; document.getElementById("overdrive-add-cancel").onclick = _overdrive.ui.addCancelClick; document.getElementById("overdrive-add-upload").onclick = _overdrive.ui.addUploadClick; document.getElementById("overdrive-pause-button").onclick = _overdrive.ui.pauseClick; document.getElementById("overdrive-resume-button").onclick = _overdrive.ui.resumeClick; document.getElementById("overdrive-delete-button").onclick = _overdrive.ui.deleteClick; document.getElementById("overdrive-delete-cancel").onclick = _overdrive.ui.deleteCancelClick; document.getElementById("overdrive-delete-confirm").onclick = _overdrive.ui.deleteConfirmClick; document.getElementById("overdrive-alt-button").onclick = _overdrive.ui.altClick; var closeButtons = document.getElementsByClassName("overdrive-window-close"); for (var i = 0; i < closeButtons.length; i++) closeButtons[i].onclick = _overdrive.ui.windowCloseClick; var titleBars = document.getElementsByClassName("overdrive-window-titlebar"); for (var i = 0; i < titleBars.length; i++) titleBars[i].onmousedown = _overdrive.ui.windowTitleMouseDown; var stateLabels = document.getElementsByClassName("overdrive-state"); for (var i = 0; i < stateLabels.length; i++) stateLabels[i].onmousedown = _overdrive.ui.stateClick; var tabs = document.getElementsByClassName("overdrive-tab"); for (var i = 0; i < tabs.length; i++) tabs[i].onmousedown = _overdrive.ui.tabClick; document.getElementById("overdrive-status-table").onscroll = _overdrive.ui.statusTableScroll; // UI adjustments document.getElementById("overdrive-torrent-list-body").style.paddingTop = document.getElementById("overdrive-torrent-list-header").offsetHeight + "px"; var floatMenus = document.getElementsByClassName("overdrive-window"); for (var i = 0; i < floatMenus.length; i++) { floatMenus[i].style.left = ((window.innerWidth / 2) - (floatMenus[i].offsetWidth / 2)) + "px"; floatMenus[i].style.top = ((window.innerHeight / 2) - (floatMenus[i].offsetHeight / 2)) + "px"; floatMenus[i].style.display = "none"; floatMenus[i].style.opacity = 1.0; } var details = document.getElementById("overdrive-tab-contents"); var diff = window.innerHeight - document.getElementById("overdrive").clientHeight; details.style.height = ((details.clientHeight + diff) - (_overdrive.ui.detailsPadding + _overdrive.ui.detailsMargin)) + "px"; // Start communicating with server window.setInterval(_overdrive.data.getEverything, _overdrive.settings.refreshRate); }; // Attach onLoad handler if (window.addEventListener) { window.addEventListener("load", _overdrive.onLoad, false); window.addEventListener("resize", _overdrive.ui.resize, false); window.addEventListener("unload", _overdrive.settings.saveSettings, false); } else if (window.attachEvent) { window.attachEvent("onload", _overdrive.onLoad); window.attachEvent("onresize", _overdrive.ui.resize); window.attachEvent("onunload", _overdrive.settings.saveSettings); }