/* * This file is part of Linode Manager Classic. * * Linode Manager Classic is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Linode Manager Classic 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 Linode Manager Classic. If not, see . */ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParams, setupHeader, timeString, translateKernel } from "/global.js"; (function() { // Element names specific to this page elements.backups = "backups"; elements.backupsEnabled = "backups-enabled"; elements.boot = "boot"; elements.configRadioName = "config-radio"; elements.configRemovePrefix = "config-remove-"; elements.configTable = "config-table"; elements.diskIcon = "disk-icon"; elements.diskIconImg = "/img/disk.gif"; elements.diskRemovePrefix = "disk-remove-"; elements.diskTable = "disk-table"; elements.diskUsage = "disk-usage"; elements.eventRowPrefix = "event-row-"; elements.eventTable = "event-table"; elements.extraEvent = "extra-event"; elements.info = "info"; elements.jobFailed = "job-failed"; elements.jobInfo = "job-info"; elements.jobNotice = "job-notice"; elements.jobProgress = "job-progress"; elements.jobProgressRow = "job-progress-row"; elements.jobRunning = "job-running"; elements.jobSuccess = "job-success"; elements.jobType = "job-type"; elements.lastBackup = "last-backup"; elements.lastBackupTime = "last-backup-time"; elements.linodeLabel = "linode-label"; elements.linodeTag = "linode-tag"; elements.linodeTagLink = "linode-tag-link"; elements.lmcRow = "lmc-tr3"; elements.loadingConfigs = "loading-configs"; elements.loadingDisks = "loading-disks"; elements.loadingJobs = "loading-jobs"; elements.loadingVolumes = "loading-volumes"; elements.moreJobs = "moar-jobs"; elements.netUsage = "net-usage"; elements.notification = "notification"; elements.notifications = "notifications"; elements.reboot = "reboot"; elements.serverStatus = "server-status"; elements.shutDown = "shut-down"; elements.spinnerImg = "/img/spinner-trans.gif"; elements.storageFree = "storage-free"; elements.storageTotal = "storage-total"; elements.storageUsed = "storage-used"; elements.transferMonthly = "transfer-monthly"; elements.transferOverage = "transfer-overage"; elements.transferUsed = "transfer-used"; elements.upgrade = "upgrade"; elements.volumeTable = "volume-table"; // Data recieved from API calls var data = {}; data.params = {}; data.configs = []; data.disks = []; data.events = []; data.linode = {}; data.linodeTransfer = {}; data.notifications = []; data.plan = {}; data.volumes = []; // Static references to UI elements var ui = {}; ui.backups = {}; ui.boot = {}; ui.configTable = {}; ui.diskTable = {}; ui.diskUsage = {}; ui.eventTable = {}; ui.jobProgress = {}; ui.jobProgressRow = {}; ui.lastBackup = {}; ui.lastBackupTime = {}; ui.linodeLabel = {}; ui.linodeTag = {}; ui.linodeTagLink = {}; ui.loadingConfigs = {}; ui.loadingDisks = {}; ui.loadingJobs = {}; ui.loadingVolumes = {}; ui.moreJobs = {}; ui.netUsage = {}; ui.notifications = {}; ui.reboot = {}; ui.serverStatus = {}; ui.shutDown = {}; ui.storageFree = {}; ui.storageTotal = {}; ui.storageUsed = {}; ui.transferMonthly = {}; ui.transferOverage = {}; ui.transferUsed = {}; ui.upgrade = {}; ui.volumeTable = {}; // Temporary state var state = {}; state.diskRefresh = false; state.eventsComplete = 0; state.linodeRefresh = false; state.showExtraEvents = false; // Button handler for boot button var bootLinode = function(event) { if (!confirm("Boot this Linode?")) return; var config = getSelectedConfig(); var request = {}; if (config) request.config_id = config; // Reload the page on successful API return var callback = function() { location.reload(); }; apiPost("/linode/instances/" + data.params.lid + "/boot", request, callback); }; // Convert a byte count into a "friendly" byte string (KB, MB, GB, etc) var byteString = function(count) { var prefix = "KMGTPEZY"; var unit = ""; for (var i = 0; i < prefix.length; i++) { if (count >= 1024) { count /= 1024; unit = prefix.charAt(i); } else { break; } } return count.toFixed(2) + " " + unit + "B"; }; // Generate a config profile table row var createConfigRow = function(config) { var row = document.createElement("tr"); row.className = elements.lmcRow; // Radio button selector var radioCell = document.createElement("td"); var radioInput = document.createElement("input"); radioInput.name = elements.configRadioName; radioInput.className = elements.configRadioName; radioInput.id = elements.configRadioName + "-" + config.id; radioInput.type = "radio"; radioCell.appendChild(radioInput); row.appendChild(radioCell); // Config profile name and kernel var nameCell = document.createElement("td"); var nameLink = document.createElement("a"); nameLink.href = "/linodes/config?cid=" + config.id + "&lid=" + data.params.lid; if (config.label.length > 41) nameLink.innerHTML = config.label.substring(0, 41) + "..."; else nameLink.innerHTML = config.label; nameCell.appendChild(nameLink); var prefix = document.createElement("span"); prefix.className = elements.info; prefix.innerHTML = " ("; nameCell.appendChild(prefix); var kernel = document.createElement("span"); kernel.className = elements.info; translateKernel(config.kernel, kernel); nameCell.appendChild(kernel); var suffix = document.createElement("span"); suffix.className = elements.info; suffix.innerHTML = ")"; nameCell.appendChild(suffix); row.appendChild(nameCell); // Option links var options = document.createElement("td"); var editLink = document.createElement("a"); editLink.href = "/linodes/config?cid=" + config.id + "&lid=" + data.params.lid; editLink.innerHTML = "Edit"; var separator = document.createElement("span"); separator.innerHTML = " | "; var removeLink = document.createElement("a"); removeLink.href = "#"; removeLink.id = elements.configRemovePrefix + config.id; removeLink.innerHTML = "Remove"; removeLink.addEventListener("click", deleteConfig); options.appendChild(editLink); options.appendChild(separator); options.appendChild(removeLink); row.appendChild(options); return row; }; // Generate a disk table row var createDiskRow = function(disk) { var row = document.createElement("tr"); row.className = elements.lmcRow; // Disk icon var iconCell = document.createElement("td"); var diskIcon = document.createElement("img"); diskIcon.className = elements.diskIcon; diskIcon.src = elements.diskIconImg; iconCell.appendChild(diskIcon); row.appendChild(iconCell); // Disk name and size var nameCell = document.createElement("td"); var nameLink = document.createElement("a"); nameLink.href = "/linodes/disk?did=" + disk.id + "&lid=" + data.params.lid; nameLink.innerHTML = disk.label; var size = document.createElement("span"); size.className = elements.info; size.innerHTML = " (" + disk.size + " MB, " + disk.filesystem + ")"; nameCell.appendChild(nameLink); nameCell.appendChild(size); row.appendChild(nameCell); // Options links var options = document.createElement("td"); var editLink = document.createElement("a"); editLink.href = "/linodes/disk?did=" + disk.id + "&lid=" + data.params.lid; editLink.innerHTML = "Edit"; var separator = document.createElement("span"); separator.innerHTML = " | "; var removeLink = document.createElement("a"); removeLink.href = "#"; removeLink.id = elements.diskRemovePrefix + disk.id; removeLink.innerHTML = "Remove"; removeLink.addEventListener("click", deleteDisk); options.appendChild(editLink); options.appendChild(separator); options.appendChild(removeLink); row.appendChild(options); return row; }; // Generate an event table row var createEventRow = function(event, extra) { var row = document.createElement("tr"); row.id = elements.eventRowPrefix + event.id; row.className = elements.lmcRow; if (extra) row.className += " " + elements.extraEvent; // Status cell var statusCell = document.createElement("td"); if (event.status.match(/scheduled|started/)) { statusCell.className = elements.jobRunning; var spinner = document.createElement("img"); spinner.src = elements.spinnerImg; statusCell.appendChild(spinner); } else if (event.status == "failed") { var failed = document.createElement("span"); failed.className = elements.jobFailed; failed.innerHTML = "Failed"; statusCell.appendChild(failed); } else if (event.status == "notification") { var notice = document.createElement("span"); notice.className = elements.jobNotice; notice.innerHTML = "Notice"; statusCell.appendChild(notice); } else if (event.status == "finished") { var success = document.createElement("span"); success.className = elements.jobSuccess; success.innerHTML = "Success"; statusCell.appendChild(success); } row.appendChild(statusCell); // Details cell var detailsCell = document.createElement("td"); var jobType = document.createElement("span"); jobType.className = elements.jobType; if (eventTitles[event.action]) jobType.innerHTML = eventTitles[event.action]; else jobType.innerHTML = event.action; var jobDetails = document.createElement("span"); if (event.secondary_entity) jobDetails.innerHTML = " - " + event.secondary_entity.label; var br = document.createElement("br"); var jobInfo = document.createElement("span"); jobInfo.className = elements.jobInfo; var eventDate = new Date(event.created + "Z"); var curDate = new Date(); jobInfo.innerHTML = "Entered: " + timeString(curDate - eventDate, true); if (event.duration) jobInfo.innerHTML += " - Took: " + timeString(event.duration * 1000, false); else if (event.status != "notification") jobInfo.innerHTML += " - Waiting..."; detailsCell.appendChild(jobType); detailsCell.appendChild(jobDetails); detailsCell.appendChild(br); detailsCell.appendChild(jobInfo); row.appendChild(detailsCell); // Progress cell var progressCell = document.createElement("td"); if (event.percent_complete && event.percent_complete != 100) progressCell.innerHTML = event.percent_complete + "%"; if (event.time_remaining) { if (progressCell.innerHTML.length) progressCell.innerHTML += ", "; var times = event.time_remaining.split(":"); var ms = 0; ms += parseInt(times[0]) * 3600000; ms += parseInt(times[1]) * 60000; ms += parseInt(times[2]) * 1000; progressCell.innerHTML += timeString(ms, false) + " to go"; } if (event.rate) { if (progressCell.innerHTML.length) progressCell.innerHTML += ", "; progressCell.innerHTML += event.rate; } row.appendChild(progressCell); return row; }; // Generate a volume table row var createVolumeRow = function(volume) { var row = document.createElement("tr"); row.className = elements.lmcRow; // Disk icon var iconCell = document.createElement("td"); var diskIcon = document.createElement("img"); diskIcon.className = elements.diskIcon; diskIcon.src = elements.diskIconImg; iconCell.appendChild(diskIcon); row.appendChild(iconCell); // Volume name and size var nameCell = document.createElement("td"); var nameLink = document.createElement("a"); nameLink.href = "/volumes/settings?vid=" + volume.id; nameLink.innerHTML = volume.label; var size = document.createElement("span"); size.className = elements.info; size.innerHTML = " (" + volume.size + " GiB)"; nameCell.appendChild(nameLink); nameCell.appendChild(size); row.appendChild(nameCell); // Options links var options = document.createElement("td"); var editLink = document.createElement("a"); editLink.href = "/volumes/settings?vid=" + volume.id; editLink.innerHTML = "Edit"; var separator = document.createElement("span"); separator.innerHTML = " | "; var cloneLink = document.createElement("a"); cloneLink.href = "/volumes/clone?vid=" + volume.id; cloneLink.innerHTML = "Clone"; var detachLink = document.createElement("a"); detachLink.href = "/volumes/detach?vid=" + volume.id; detachLink.innerHTML = "Detach"; var removeLink = document.createElement("a"); removeLink.href = "/volumes/remove?vid=" + volume.id; removeLink.innerHTML = "Remove"; options.appendChild(editLink); options.appendChild(separator); options.appendChild(cloneLink); options.appendChild(separator.cloneNode(true)); options.appendChild(detachLink); options.appendChild(separator.cloneNode(true)); options.appendChild(removeLink); row.appendChild(options); return row; }; // Handler for config delete var deleteConfig = function(event) { if (!confirm("Delete this Configuration Profile?")) return; var cid = event.currentTarget.id.substring(elements.configRemovePrefix.length); apiDelete("/linode/instances/" + data.params.lid + "/configs/" + cid, function() { location.reload(); }); }; // Handler for disk delete var deleteDisk = function(event) { var did = parseInt(event.currentTarget.id.substring(elements.diskRemovePrefix.length)); var disk = null; for (var i = 0; i < data.disks.length; i++) { if (data.disks[i].id == did) { disk = data.disks[i]; break; } } if (!disk) return; if (!confirm("Delete " + disk.label + "?")) return; apiDelete("/linode/instances/" + data.params.lid + "/disks/" + did, function() { location.reload(); }); }; // Callback for config profile API call var displayConfigs = function(response) { // Add configs to array data.configs = data.configs.concat(response.data); // Request the next page if there are more if (response.page != response.pages) { apiGet("/linode/instances/" + data.params.lid + "/configs?page=" + (response.page + 1), displayConfigs, null); return; } // Remove loading row ui.loadingConfigs.remove(); // Insert config profile rows into table for (var i = 0; i < data.configs.length; i++) ui.configTable.appendChild(createConfigRow(data.configs[i])); }; // Callback for linode details API call var displayDetails = function(response) { data.linode = response; // Set page title and header stuff if (document.title.indexOf("//") == -1) document.title += " // " + data.linode.label; ui.linodeLabel.innerHTML = data.linode.label; if (data.linode.tags.length == 1) { ui.linodeTagLink.href = "/linodes?tag=" + data.linode.tags[0]; ui.linodeTagLink.innerHTML = "(" + data.linode.tags[0] + ")"; ui.linodeTag.style.display = "inline"; } else { ui.linodeTag.style.display = "none"; } // Set running status ui.serverStatus.innerHTML = data.linode.status.charAt(0).toUpperCase() + data.linode.status.slice(1).replace(/_/g, " "); if (data.linode.status == "running") { ui.shutDown.disabled = false; ui.shutDown.style.display = "block"; } else { ui.shutDown.style.display = "none"; } if (data.linode.status.match(/offline|provisioning|migrating|rebuilding|restoring|shutting_down|resizing/)) { ui.reboot.style.display = "none"; ui.boot.disabled = false; ui.boot.style.display = "inline"; } else if (data.linode.status != "cloning") { ui.boot.style.display = "none"; ui.reboot.disabled = false; ui.reboot.style.display = "inline"; } // Update storage info displayStorage(); // Set backup status if (data.linode.backups.enabled) { ui.backups.innerHTML = "Enabled!"; ui.backups.className += " " + elements.backupsEnabled; if (data.linode.backups.last_successful) { var backupDate = new Date(data.linode.backups.last_successful + "Z"); var now = new Date(); ui.lastBackupTime.innerHTML = timeString(now - backupDate, true); ui.lastBackup.style.display = "block"; } } else { ui.backups.innerHTML = ""; var text = document.createElement("span"); text.innerHTML = "No - "; var backupLink = document.createElement("a"); backupLink.href = "/linodes/backups_enable?lid=" + data.params.lid; backupLink.innerHTML = "Enable"; backups.appendChild(text); backups.appendChild(backupLink); } // Get plan info if (data.linode.type && !data.plan.id) apiGet("/linode/types/" + data.linode.type, displayPlan, null); state.linodeRefresh = false; }; // Callback for disks API call var displayDisks = function(response) { // Add disks to array data.disks = data.disks.concat(response.data); // Request the next page if there are more if (response.page != response.pages) { apiGet("/linode/instances/" + data.params.lid + "/disks?page=" + (response.page + 1), displayDisks, null); return; } // Remove loading row ui.loadingDisks.remove(); // Update storage info displayStorage(); // Insert disk rows into table ui.diskTable.innerHTML = ""; for (var i = 0; i < data.disks.length; i++) ui.diskTable.appendChild(createDiskRow(data.disks[i])); state.diskRefresh = false; }; // Callback for events API call var displayEvents = function(response) { // Only collect a max of 8 finished jobs for (var i = 0; i < response.data.length && state.eventsComplete < 8; i++) { if (response.data[i].status.match(/failed|finished|notification/)) state.eventsComplete++; data.events.push(response.data[i]); } // Request the next page if there are more if (state.eventsComplete < 8 && response.page != response.pages) { var filter = { "entity.type": "linode", "entity.id": parseInt(data.params.lid) }; apiGet("/account/events?page=" + (response.page + 1), displayEvents, filter); return; } // Remove loading row ui.loadingJobs.remove(); // Insert events into table and compute total progress var progress = 0; var totalProgress = 0; for (var i = 0; i < data.events.length; i++) { var extra = (data.events[i].status.match(/failed|finished|notification/) && i >= 4); ui.eventTable.appendChild(createEventRow(data.events[i], extra)); if (!data.events[i].status.match(/failed|finished|notification/)) { totalProgress += 100; progress += data.events[i].percent_complete; // Set refresh timer for event window.setTimeout(getEvent, settings.refreshRate, data.events[i].id); } } // If there are ongoing events, show the progress bar if (totalProgress) { ui.jobProgress.max = totalProgress; ui.jobProgress.value = progress; ui.jobProgress.innerHTML = (progress / totalProgress * 100).toFixed(0) + "%"; ui.jobProgress.title = ui.jobProgress.innerHTML; ui.jobProgressRow.style.display = "table-row"; } }; // Callback for notifications API call var displayNotifications = function(response) { // Add notifications to array data.notifications = data.notifications.concat(response.data); // Request the next page if there are more pages if (response.page != response.pages) { apiGet("/account/notifications?page=" + (response.page + 1), displayNotifications, null); return; } // Display notifications for (var i = 0; i < data.notifications.length; i++) { if (!data.notifications[i].entity || data.notifications[i].entity.type != "linode" || data.notifications[i].entity.id != data.params.lid) continue; var notification = document.createElement("div"); notification.className = elements.notification; var header = document.createElement("h1"); header.innerHTML = data.notifications[i].label; notification.appendChild(header); if (data.notifications[i].type == "migration_pending") { var migrateButton = document.createElement("button"); migrateButton.type = "button"; migrateButton.innerHTML = "Migrate " + data.notifications[i].entity.label + " NOW"; migrateButton.addEventListener("click", initiateMigration); notification.appendChild(migrateButton); } var body = document.createElement("p"); body.innerHTML = data.notifications[i].message; notification.appendChild(body); ui.notifications.appendChild(notification); } }; // Callback for linode plan API call var displayPlan = function(response) { data.plan = response; // Display the upgrade banner if one is available if (data.plan.successor) ui.upgrade.style.display = "block"; }; // Show storage totals var displayStorage = function() { if (!data.linode.specs) return; ui.storageTotal.innerHTML = data.linode.specs.disk + " MB"; var storageUsed = 0; for (var i = 0; i < data.disks.length; i++) storageUsed += data.disks[i].size; ui.storageUsed.innerHTML = storageUsed + " MB"; ui.storageFree.innerHTML = (data.linode.specs.disk - storageUsed) + " MB"; ui.diskUsage.value = (storageUsed / data.linode.specs.disk * 100).toFixed(0); ui.diskUsage.innerHTML = ui.diskUsage.value + "%"; ui.diskUsage.title = ui.diskUsage.innerHTML; }; // Callback for network transfer API call var displayTransfer = function(response) { data.linodeTransfer = response; // Display network transfer info ui.transferMonthly.innerHTML = data.linodeTransfer.quota + " GB"; ui.transferUsed.innerHTML = byteString(data.linodeTransfer.used); ui.transferOverage.innerHTML = data.linodeTransfer.billable + " GB"; ui.netUsage.value = ((data.linodeTransfer.used / 1024 / 1024 / 1024) / data.linodeTransfer.quota * 100).toFixed(0); ui.netUsage.innerHTML = ui.netUsage.value + "%"; ui.netUsage.title = ui.netUsage.innerHTML; }; // Callback for volumes API call var displayVolumes = function(response) { // Add volumes to array data.volumes = data.volumes.concat(response.data); // Request the next page if there are more if (response.page != response.pages) { apiGet("/linode/instances/" + data.params.lid + "/volumes?page=" + (response.page + 1), displayVolumes, null); return; } // Remove loading row ui.loadingVolumes.remove(); // Insert volume rows into table for (var i = 0; i < data.volumes.length; i++) ui.volumeTable.appendChild(createVolumeRow(data.volumes[i])); }; // Retrieve the given event from the API var getEvent = function(eventID) { apiGet("/account/events/" + eventID, updateEvent, null); }; // Get selected config profile var getSelectedConfig = function() { var configs = document.getElementsByClassName(elements.configRadioName); for (var i = 0; i < configs.length; i++) { if (configs[i].checked) return parseInt(configs[i].id.replace(elements.configRadioName + "-", "")); } return 0; }; // Button handler to initiate migration var initiateMigration = function(event) { if (event.currentTarget.disabled) return; var req = { "upgrade": false }; apiPost("/linode/instances/" + data.params.lid + "/migrate", req, function(response) { location.reload(); }); }; // Button handler for reboot button var rebootLinode = function(event) { if (!confirm("Are you sure you want to issue a Reboot?")) return; var config = getSelectedConfig(); var request = {}; if (config) request.config_id = config; // Reload the page on successful API return var callback = function() { location.reload(); }; apiPost("/linode/instances/" + data.params.lid + "/reboot", request, callback); }; // Initial setup var setup = function() { // Parse URL parameters data.params = parseParams(); // We need a Linode ID, so die if we don't have it if (!data.params.lid) { alert("No Linode ID supplied!"); return; } setupHeader(); // Update links on page to include proper Linode ID var anchors = document.getElementsByTagName("a"); for (var i = 0; i < anchors.length; i++) anchors[i].href = anchors[i].href.replace("lid=0", "lid=" + data.params.lid); // Get element references ui.backups = document.getElementById(elements.backups); ui.boot = document.getElementById(elements.boot); ui.configTable = document.getElementById(elements.configTable); ui.diskTable = document.getElementById(elements.diskTable); ui.diskUsage = document.getElementById(elements.diskUsage); ui.eventTable = document.getElementById(elements.eventTable); ui.jobProgress = document.getElementById(elements.jobProgress); ui.jobProgressRow = document.getElementById(elements.jobProgressRow); ui.lastBackup = document.getElementById(elements.lastBackup); ui.lastBackupTime = document.getElementById(elements.lastBackupTime); ui.linodeLabel = document.getElementById(elements.linodeLabel); ui.linodeTag = document.getElementById(elements.linodeTag); ui.linodeTagLink = document.getElementById(elements.linodeTagLink); ui.loadingConfigs = document.getElementById(elements.loadingConfigs); ui.loadingDisks = document.getElementById(elements.loadingDisks); ui.loadingJobs = document.getElementById(elements.loadingJobs); ui.loadingVolumes = document.getElementById(elements.loadingVolumes); ui.moreJobs = document.getElementById(elements.moreJobs); ui.netUsage = document.getElementById(elements.netUsage); ui.notifications = document.getElementById(elements.notifications); ui.reboot = document.getElementById(elements.reboot); ui.serverStatus = document.getElementById(elements.serverStatus); ui.shutDown = document.getElementById(elements.shutDown); ui.storageFree = document.getElementById(elements.storageFree); ui.storageTotal = document.getElementById(elements.storageTotal); ui.storageUsed = document.getElementById(elements.storageUsed); ui.transferMonthly = document.getElementById(elements.transferMonthly); ui.transferOverage = document.getElementById(elements.transferOverage); ui.transferUsed = document.getElementById(elements.transferUsed); ui.upgrade = document.getElementById(elements.upgrade); ui.volumeTable = document.getElementById(elements.volumeTable); // Attach button handlers ui.boot.addEventListener("click", bootLinode); ui.moreJobs.addEventListener("click", toggleEvents); ui.reboot.addEventListener("click", rebootLinode); ui.shutDown.addEventListener("click", shutDownLinode); // Get data from the API apiGet("/linode/instances/" + data.params.lid, displayDetails, null); apiGet("/linode/instances/" + data.params.lid + "/configs", displayConfigs, null); apiGet("/linode/instances/" + data.params.lid + "/disks", displayDisks, null); apiGet("/linode/instances/" + data.params.lid + "/volumes", displayVolumes, null); apiGet("/linode/instances/" + data.params.lid + "/transfer", displayTransfer, null); var filter = { "entity.type": "linode", "entity.id": parseInt(data.params.lid) }; apiGet("/account/events", displayEvents, filter); apiGet("/account/notifications", displayNotifications, null); }; // Button handler for shutdown button var shutDownLinode = function(event) { if (!confirm("Are you sure you want to issue a Shutdown?")) return; // Reload the page on successful API return var callback = function() { location.reload(); }; apiPost("/linode/instances/" + data.params.lid + "/shutdown", {}, callback); }; // Toggle visibility of "extra" events var toggleEvents = function(event) { // Flip state.showExtraEvents = !state.showExtraEvents; var display = "none"; if (state.showExtraEvents) display = "table-row"; // Get extra events and show/hide them var extras = document.getElementsByClassName(elements.extraEvent); for (var i = 0; i < extras.length; i++) extras[i].style.display = display; }; // Update an existing event var updateEvent = function(event) { // Update the local copy of the event var progress = 0; for (var i = 0; i < data.events.length; i++) { if (data.events[i].id == event.id) { progress = event.percent_complete - data.events[i].percent_complete; // Refresh details if event status changed if (event.status != data.events[i].status && !state.linodeRefresh && !state.diskRefresh) { state.linodeRefresh = true; state.diskRefresh = true; data.linode = {}; data.disks = []; apiGet("/linode/instances/" + data.params.lid, displayDetails, null); apiGet("/linode/instances/" + data.params.lid + "/disks", displayDisks, null); } data.events[i] = event; continue; } } // Generate a new event row and replace the existing one var eventRow = document.getElementById(elements.eventRowPrefix + event.id); var extra = (eventRow.className.indexOf(elements.extraEvent) != -1); eventRow.replaceWith(createEventRow(event, extra)); // Update progress bar ui.jobProgress.value += progress; ui.jobProgress.innerHTML = (ui.jobProgress.value / ui.jobProgress.max * 100).toFixed(0) + "%"; ui.jobProgress.title = ui.jobProgress.innerHTML; if (ui.jobProgress.value == ui.jobProgress.max) ui.jobProgressRow.style.display = "none"; // Refresh again if not complete if (!event.status.match(/failed|finished|notification/)) window.setTimeout(getEvent, settings.refreshRate, event.id); }; // Attach onload handler window.addEventListener("load", setup); })();