text: var map = null
  var doPoll = true;
  
  // statuses is a helper map to point result statuses to ui classes
  var statuses = {
      "Email Sent": {
          color: "#1abc9c",
          label: "label-success",
          icon: "fa-envelope",
          point: "ct-point-sent"
      },
      "Emails Sent": {
          color: "#1abc9c",
          label: "label-success",
          icon: "fa-envelope",
          point: "ct-point-sent"
      },
      "In progress": {
          label: "label-primary"
      },
      "Queued": {
          label: "label-info"
      },
      "Completed": {
          label: "label-success"
      },
      "Email Opened": {
          color: "#f9bf3b",
          label: "label-warning",
          icon: "fa-envelope",
          point: "ct-point-opened"
      },
      "Clicked Link": {
          color: "#F39C12",
          label: "label-clicked",
          icon: "fa-mouse-pointer",
          point: "ct-point-clicked"
      },
      "Success": {
          color: "#f05b4f",
          label: "label-danger",
          icon: "fa-exclamation",
          point: "ct-point-clicked"
      },
      "Error": {
          color: "#6c7a89",
          label: "label-default",
          icon: "fa-times",
          point: "ct-point-error"
      },
      "Error Sending Email": {
          color: "#6c7a89",
          label: "label-default",
          icon: "fa-times",
          point: "ct-point-error"
      },
      "Submitted Data": {
          color: "#f05b4f",
          label: "label-danger",
          icon: "fa-exclamation",
          point: "ct-point-clicked"
      },
      "Unknown": {
          color: "#6c7a89",
          label: "label-default",
          icon: "fa-question",
          point: "ct-point-error"
      },
      "Sending": {
          color: "#428bca",
          label: "label-primary",
          icon: "fa-spinner",
          point: "ct-point-sending"
      },
      "Campaign Created": {
          label: "label-success",
          icon: "fa-rocket"
      }
  }
  
  var statusMapping = {
      "Email Sent": "sent",
      "Email Opened": "opened",
      "Clicked Link": "clicked",
      "Submitted Data": "submitted_data",
  }
  
  // This is an underwhelming attempt at an enum
  // until I have time to refactor this appropriately.
  var progressListing = [
      "Email Sent",
      "Email Opened",
      "Clicked Link",
      "Submitted Data"
  ]
  
  var campaign = {}
  var bubbles = []
  
  function dismiss() {
      $("#modal\\.flashes").empty()
      $("#modal").modal('hide')
      $("#resultsTable").dataTable().DataTable().clear().draw()
  }
  
  // Deletes a campaign after prompting the user
  function deleteCampaign() {
      swal({
          title: "Are you sure?",
          text: "This will delete the campaign. This can't be undone!",
          type: "warning",
          animation: false,
          showCancelButton: true,
          confirmButtonText: "Delete Campaign",
          confirmButtonColor: "#428bca",
          reverseButtons: true,
          allowOutsideClick: false,
          showLoaderOnConfirm: true,
          preConfirm: function () {
              return new Promise(function (resolve, reject) {
                  api.campaignId.delete(campaign.id)
                      .success(function (msg) {
                          resolve()
                      })
                      .error(function (data) {
                          reject(data.responseJSON.message)
                      })
              })
          }
      }).then(function () {
          swal(
              'Campaign Deleted!',
              'This campaign has been deleted!',
              'success'
          );
          $('button:contains("OK")').on('click', function () {
              location.href = '/campaigns'
          })
      })
  }
  
  // Completes a campaign after prompting the user
  function completeCampaign() {
      swal({
          title: "Are you sure?",
          text: "Gophish will stop processing events for this campaign",
          type: "warning",
          animation: false,
          showCancelButton: true,
          confirmButtonText: "Complete Campaign",
          confirmButtonColor: "#428bca",
          reverseButtons: true,
          allowOutsideClick: false,
          showLoaderOnConfirm: true,
          preConfirm: function () {
              return new Promise(function (resolve, reject) {
                  api.campaignId.complete(campaign.id)
                      .success(function (msg) {
                          resolve()
                      })
                      .error(function (data) {
                          reject(data.responseJSON.message)
                      })
              })
          }
      }).then(function () {
          swal(
              'Campaign Completed!',
              'This campaign has been completed!',
              'success'
          );
          $('#complete_button')[0].disabled = true;
          $('#complete_button').text('Completed!')
          doPoll = false;
      })
  }
  
  // Exports campaign results as a CSV file
  function exportAsCSV(scope) {
      exportHTML = $("#exportButton").html()
      var csvScope = null
      var filename = campaign.name + ' - ' + capitalize(scope) + '.csv'
      switch (scope) {
          case "results":
              csvScope = campaign.results
              break;
          case "events":
              csvScope = campaign.timeline
              break;
      }
      if (!csvScope) {
          return
      }
      $("#exportButton").html('<i class="fa fa-spinner fa-spin"></i>')
      var csvString = Papa.unparse(csvScope, {})
      var csvData = new Blob([csvString], {
          type: 'text/csv;charset=utf-8;'
      });
      if (navigator.msSaveBlob) {
          navigator.msSaveBlob(csvData, filename);
      } else {
          var csvURL = window.URL.createObjectURL(csvData);
          var dlLink = document.createElement('a');
          dlLink.href = csvURL;
          dlLink.setAttribute('download', filename)
          document.body.appendChild(dlLink)
          dlLink.click();
          document.body.removeChild(dlLink)
      }
      $("#exportButton").html(exportHTML)
  }
  
  function replay(event_idx) {
      request = campaign.timeline[event_idx]
      details = JSON.parse(request.details)
      url = null
      form = $('<form>').attr({
          method: 'POST',
          target: '_blank',
      })
      /* Create a form object and submit it */
      $.each(Object.keys(details.payload), function (i, param) {
          if (param == "rid") {
              return true;
          }
          if (param == "__original_url") {
              url = details.payload[param];
              return true;
          }
          $('<input>').attr({
              name: param,
          }).val(details.payload[param]).appendTo(form);
      })
      /* Ensure we know where to send the user */
      // Prompt for the URL
      swal({
          title: 'Where do you want the credentials submitted to?',
          input: 'text',
          showCancelButton: true,
          inputPlaceholder: "http://example.com/login",
          inputValue: url || "",
          inputValidator: function (value) {
              return new Promise(function (resolve, reject) {
                  if (value) {
                      resolve();
                  } else {
                      reject('Invalid URL.');
                  }
              });
          }
      }).then(function (result) {
          url = result
          submitForm()
      })
      return
      submitForm()
  
      function submitForm() {
          form.attr({
              action: url
          })
          form.appendTo('body').submit().remove()
      }
  }
  
  function renderTimeline(data) {
      record = {
          "first_name": data[2],
          "last_name": data[3],
          "email": data[4],
          "position": data[5]
      }
      results = '<div class="timeline col-sm-12 well well-lg">' +
          '<h6>Timeline for ' + escapeHtml(record.first_name) + ' ' + escapeHtml(record.last_name) +
          '</h6><span class="subtitle">Email: ' + escapeHtml(record.email) + '</span>' +
          '<div class="timeline-graph col-sm-6">'
      $.each(campaign.timeline, function (i, event) {
          if (!event.email || event.email == record.email) {
              // Add the event
              results += '<div class="timeline-entry">' +
                  '    <div class="timeline-bar"></div>'
              results +=
                  '    <div class="timeline-icon ' + statuses[event.message].label + '">' +
                  '    <i class="fa ' + statuses[event.message].icon + '"></i></div>' +
                  '    <div class="timeline-message">' + escapeHtml(event.message) +
                  '    <span class="timeline-date">' + moment.utc(event.time).local().format('MMMM Do YYYY h:mm a') + '</span>'
              if (event.details) {
                  if (event.message == "Submitted Data") {
                      results += '<div class="timeline-replay-button"><button onclick="replay(' + i + ')" class="btn btn-success">'
                      results += '<i class="fa fa-refresh"></i> Replay Credentials</button></div>'
                      results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>'
                  }
                  details = JSON.parse(event.details)
                  if (details.payload) {
                      results += '<div class="timeline-event-results">'
                      results += '    <table class="table table-condensed table-bordered table-striped">'
                      results += '        <thead><tr><th>Parameter</th><th>Value(s)</tr></thead><tbody>'
                      $.each(Object.keys(details.payload), function (i, param) {
                          if (param == "rid") {
                              return true;
                          }
                          results += '    <tr>'
                          results += '        <td>' + escapeHtml(param) + '</td>'
                          results += '        <td>' + escapeHtml(details.payload[param]) + '</td>'
                          results += '    </tr>'
                      })
                      results += '       </tbody></table>'
                      results += '</div>'
                  }
                  if (details.error) {
                      results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>'
                      results += '<div class="timeline-event-results">'
                      results += '<span class="label label-default">Error</span> ' + details.error
                      results += '</div>'
                  }
              }
              results += '</div></div>'
          }
      })
      results += '</div></div>'
      return results
  }
  
  var renderTimelineChart = function (chartopts) {
      return Highcharts.chart('timeline_chart', {
          chart: {
              zoomType: 'x',
              type: 'line',
              height: "200px"
          },
          title: {
              text: 'Campaign Timeline'
          },
          xAxis: {
              type: 'datetime',
              dateTimeLabelFormats: {
                  second: '%l:%M:%S',
                  minute: '%l:%M',
                  hour: '%l:%M',
                  day: '%b %d, %Y',
                  week: '%b %d, %Y',
                  month: '%b %Y'
              }
          },
          yAxis: {
              min: 0,
              max: 2,
              visible: false,
              tickInterval: 1,
              labels: {
                  enabled: false
              },
              title: {
                  text: ""
              }
          },
          tooltip: {
              formatter: function () {
                  return Highcharts.dateFormat('%A, %b %d %l:%M:%S %P', new Date(this.x)) +
                      '<br>Event: ' + this.point.message + '<br>Email: <b>' + this.point.email + '</b>'
              }
          },
          legend: {
              enabled: false
          },
          plotOptions: {
              series: {
                  marker: {
                      enabled: true,
                      symbol: 'circle',
                      radius: 3
                  },
                  cursor: 'pointer',
              },
              line: {
                  states: {
                      hover: {
                          lineWidth: 1
                      }
                  }
              }
          },
          credits: {
              enabled: false
          },
          series: [{
              data: chartopts['data'],
              dashStyle: "shortdash",
              color: "#cccccc",
              lineWidth: 1,
              turboThreshold: 0
          }]
      })
  }
  
  /* Renders a pie chart using the provided chartops */
  var renderPieChart = function (chartopts) {
      return Highcharts.chart(chartopts['elemId'], {
          chart: {
              type: 'pie',
              events: {
                  load: function () {
                      var chart = this,
                          rend = chart.renderer,
                          pie = chart.series[0],
                          left = chart.plotLeft + pie.center[0],
                          top = chart.plotTop + pie.center[1];
                      this.innerText = rend.text(chartopts['data'][0].y, left, top).
                      attr({
                          'text-anchor': 'middle',
                          'font-size': '24px',
                          'font-weight': 'bold',
                          'fill': chartopts['colors'][0],
                          'font-family': 'Helvetica,Arial,sans-serif'
                      }).add();
                  },
                  render: function () {
                      this.innerText.attr({
                          text: chartopts['data'][0].y
                      })
                  }
              }
          },
          title: {
              text: chartopts['title']
          },
          plotOptions: {
              pie: {
                  innerSize: '80%',
                  dataLabels: {
                      enabled: false
                  }
              }
          },
          credits: {
              enabled: false
          },
          tooltip: {
              formatter: function () {
                  if (this.key == undefined) {
                      return false
                  }
                  return '<span style="color:' + this.color + '">\u25CF</span>' + this.point.name + ': <b>' + this.y + '</b><br/>'
              }
          },
          series: [{
              data: chartopts['data'],
              colors: chartopts['colors'],
          }]
      })
  }
  
  /* Updates the bubbles on the map
  
  @param {campaign.result[]} results - The campaign results to process
  */
  var updateMap = function (results) {
      if (!map) {
          return
      }
      bubbles = []
      $.each(campaign.results, function (i, result) {
          // Check that it wasn't an internal IP
          if (result.latitude == 0 && result.longitude == 0) {
              return true;
          }
          newIP = true
          $.each(bubbles, function (i, bubble) {
              if (bubble.ip == result.ip) {
                  bubbles[i].radius += 1
                  newIP = false
                  return false
              }
          })
          if (newIP) {
              bubbles.push({
                  latitude: result.latitude,
                  longitude: result.longitude,
                  name: result.ip,
                  fillKey: "point",
                  radius: 2
              })
          }
      })
      map.bubbles(bubbles)
  }
  
  /* poll - Queries the API and updates the UI with the results
   *
   * Updates:
   * * Timeline Chart
   * * Email (Donut) Chart
   * * Map Bubbles
   * * Datatables
   */
  function poll() {
      api.campaignId.results(campaign.id)
          .success(function (c) {
              campaign = c
              /* Update the timeline */
              var timeline_series_data = []
              $.each(campaign.timeline, function (i, event) {
                  var event_date = moment.utc(event.time).local()
                  timeline_series_data.push({
                      email: event.email,
                      x: event_date.valueOf(),
                      y: 1
                  })
              })
              var timeline_series_data = []
              $.each(campaign.timeline, function (i, event) {
                  var event_date = moment.utc(event.time).local()
                  timeline_series_data.push({
                      email: event.email,
                      message: event.message,
                      x: event_date.valueOf(),
                      y: 1,
                      marker: {
                          fillColor: statuses[event.message].color
                      }
                  })
              })
              var timeline_chart = $("#timeline_chart").highcharts()
              timeline_chart.series[0].update({
                  data: timeline_series_data
              })
              /* Update the results donut chart */
              var email_series_data = {}
              // Load the initial data
              Object.keys(statusMapping).forEach(function (k) {
                  email_series_data[k] = 0
              });
              $.each(campaign.results, function (i, result) {
                  email_series_data[result.status]++;
                  // Backfill status values
                  var step = progressListing.indexOf(result.status)
                  for (var i = 0; i < step; i++) {
                      email_series_data[progressListing[i]]++
                  }
              })
              $.each(email_series_data, function (status, count) {
                  var email_data = []
                  if (!(status in statusMapping)) {
                      return true
                  }
                  email_data.push({
                      name: status,
                      y: count
                  })
                  email_data.push({
                      name: '',
                      y: campaign.results.length - count
                  })
                  var chart = $("#" + statusMapping[status] + "_chart").highcharts()
                  chart.series[0].update({
                      data: email_data
                  })
              })
              /* Update the datatable */
              resultsTable = $("#resultsTable").DataTable()
              resultsTable.rows().every(function (i, tableLoop, rowLoop) {
                  var row = this.row(i)
                  var rowData = row.data()
                  var rid = rowData[0]
                  $.each(campaign.results, function (j, result) {
                      if (result.id == rid) {
                          var label = statuses[result.status].label || "label-default";
                          rowData[6] = "<span class=\"label " + label + "\">" + result.status + "</span>"
                          resultsTable.row(i).data(rowData)
                          if (row.child.isShown()) {
                              row.child(renderTimeline(row.data()))
                          }
                          return false
                      }
                  })
              })
              resultsTable.draw(false)
              /* Update the map information */
              updateMap(campaign.results)
              $("#refresh_message").hide()
              $("#refresh_btn").show()
          })
  }
  
  function load() {
      campaign.id = window.location.pathname.split('/').slice(-1)[0]
      var use_map = JSON.parse(localStorage.getItem('gophish.use_map'))
      api.campaignId.results(campaign.id)
          .success(function (c) {
              campaign = c
              if (campaign) {
                  $("title").text(c.name + " - Gophish")
                  $("#loading").hide()
                  $("#campaignResults").show()
                  // Set the title
                  $("#page-title").text("Results for " + c.name)
                  if (c.status == "Completed") {
                      $('#complete_button')[0].disabled = true;
                      $('#complete_button').text('Completed!');
                      doPoll = false;
                  }
                  // Setup tooltips
                  $('[data-toggle="tooltip"]').tooltip()
                  // Setup viewing the details of a result
                  $("#resultsTable").on("click", ".timeline-event-details", function () {
                      // Show the parameters
                      payloadResults = $(this).parent().find(".timeline-event-results")
                      if (payloadResults.is(":visible")) {
                          $(this).find("i").removeClass("fa-caret-down")
                          $(this).find("i").addClass("fa-caret-right")
                          payloadResults.hide()
                      } else {
                          $(this).find("i").removeClass("fa-caret-right")
                          $(this).find("i").addClass("fa-caret-down")
                          payloadResults.show()
                      }
                  })
                  // Setup the results table
                  resultsTable = $("#resultsTable").DataTable({
                      destroy: true,
                      "order": [
                          [2, "asc"]
                      ],
                      columnDefs: [{
                          orderable: false,
                          targets: "no-sort"
                      }, {
                          className: "details-control",
                          "targets": [1]
                      }, {
                          "visible": false,
                          "targets": [0]
                      }]
                  });
                  resultsTable.clear();
                  var email_series_data = {}
                  var timeline_series_data = []
                  Object.keys(statusMapping).forEach(function (k) {
                      email_series_data[k] = 0
                  });
                  $.each(campaign.results, function (i, result) {
                      label = statuses[result.status].label || "label-default";
                      resultsTable.row.add([
                          result.id,
                          "<i class=\"fa fa-caret-right\"></i>",
                          escapeHtml(result.first_name) || "",
                          escapeHtml(result.last_name) || "",
                          escapeHtml(result.email) || "",
                          escapeHtml(result.position) || "",
                          "<span class=\"label " + label + "\">" + result.status + "</span>"
                      ])
                      email_series_data[result.status]++;
                      // Backfill status values
                      var step = progressListing.indexOf(result.status)
                      for (var i = 0; i < step; i++) {
                          email_series_data[progressListing[i]]++
                      }
                  })
                  resultsTable.draw();
                  // Setup the individual timelines
                  $('#resultsTable tbody').on('click', 'td.details-control', function () {
                      var tr = $(this).closest('tr');
                      var row = resultsTable.row(tr);
                      if (row.child.isShown()) {
                          // This row is already open - close it
                          row.child.hide();
                          tr.removeClass('shown');
                          $(this).find("i").removeClass("fa-caret-down")
                          $(this).find("i").addClass("fa-caret-right")
                          row.invalidate('dom').draw(false)
                      } else {
                          // Open this row
                          $(this).find("i").removeClass("fa-caret-right")
                          $(this).find("i").addClass("fa-caret-down")
                          row.child(renderTimeline(row.data())).show();
                          tr.addClass('shown');
                          row.invalidate('dom').draw(false)
                      }
                  });
                  // Setup the graphs
                  $.each(campaign.timeline, function (i, event) {
                      var event_date = moment.utc(event.time).local()
                      timeline_series_data.push({
                          email: event.email,
                          message: event.message,
                          x: event_date.valueOf(),
                          y: 1,
                          marker: {
                              fillColor: statuses[event.message].color
                          }
                      })
                  })
                  renderTimelineChart({
                      data: timeline_series_data
                  })
                  $.each(email_series_data, function (status, count) {
                      var email_data = []
                      if (!(status in statusMapping)) {
                          return true
                      }
                      email_data.push({
                          name: status,
                          y: count
                      })
                      email_data.push({
                          name: '',
                          y: campaign.results.length - count
                      })
                      var chart = renderPieChart({
                          elemId: statusMapping[status] + '_chart',
                          title: status,
                          name: status,
                          data: email_data,
                          colors: [statuses[status].color, '#dddddd']
                      })
                  })
                  if (use_map) {
                      $("#resultsMapContainer").show()
                      map = new Datamap({
                          element: document.getElementById("resultsMap"),
                          responsive: true,
                          fills: {
                              defaultFill: "#ffffff",
                              point: "#283F50"
                          },
                          geographyConfig: {
                              highlightFillColor: "#1abc9c",
                              borderColor: "#283F50"
                          },
                          bubblesConfig: {
                              borderColor: "#283F50"
                          }
                      });
                  }
                  updateMap(campaign.results)
              }
          })
          .error(function () {
              $("#loading").hide()
              errorFlash(" Campaign not found!")
          })
  }
  
  var setRefresh
  
  function refresh() {
      if (!doPoll) {
          return;
      }
      $("#refresh_message").show()
      $("#refresh_btn").hide()
      poll()
      clearTimeout(setRefresh)
      setRefresh = setTimeout(refresh, 60000)
  };
  
  
  
  $(document).ready(function () {
      Highcharts.setOptions({
          global: {
              useUTC: false
          }
      })
      load();
  
      // Start the polling loop
      setRefresh = setTimeout(refresh, 60000)
  })