github.com/jonathaningram/gophish@v0.3.1-0.20170829042651-ac3fe6aeae6c/static/js/src/app/campaign_results.js (about)

     1  var map = null
     2  var doPoll = true;
     3  
     4  // statuses is a helper map to point result statuses to ui classes
     5  var statuses = {
     6      "Email Sent": {
     7          color: "#1abc9c",
     8          label: "label-success",
     9          icon: "fa-envelope",
    10          point: "ct-point-sent"
    11      },
    12      "Emails Sent": {
    13          color: "#1abc9c",
    14          label: "label-success",
    15          icon: "fa-envelope",
    16          point: "ct-point-sent"
    17      },
    18      "In progress": {
    19          label: "label-primary"
    20      },
    21      "Queued": {
    22          label: "label-info"
    23      },
    24      "Completed": {
    25          label: "label-success"
    26      },
    27      "Email Opened": {
    28          color: "#f9bf3b",
    29          label: "label-warning",
    30          icon: "fa-envelope",
    31          point: "ct-point-opened"
    32      },
    33      "Clicked Link": {
    34          color: "#F39C12",
    35          label: "label-clicked",
    36          icon: "fa-mouse-pointer",
    37          point: "ct-point-clicked"
    38      },
    39      "Success": {
    40          color: "#f05b4f",
    41          label: "label-danger",
    42          icon: "fa-exclamation",
    43          point: "ct-point-clicked"
    44      },
    45      "Error": {
    46          color: "#6c7a89",
    47          label: "label-default",
    48          icon: "fa-times",
    49          point: "ct-point-error"
    50      },
    51      "Error Sending Email": {
    52          color: "#6c7a89",
    53          label: "label-default",
    54          icon: "fa-times",
    55          point: "ct-point-error"
    56      },
    57      "Submitted Data": {
    58          color: "#f05b4f",
    59          label: "label-danger",
    60          icon: "fa-exclamation",
    61          point: "ct-point-clicked"
    62      },
    63      "Unknown": {
    64          color: "#6c7a89",
    65          label: "label-default",
    66          icon: "fa-question",
    67          point: "ct-point-error"
    68      },
    69      "Sending": {
    70          color: "#428bca",
    71          label: "label-primary",
    72          icon: "fa-spinner",
    73          point: "ct-point-sending"
    74      },
    75      "Campaign Created": {
    76          label: "label-success",
    77          icon: "fa-rocket"
    78      }
    79  }
    80  
    81  var statusMapping = {
    82      "Email Sent": "sent",
    83      "Email Opened": "opened",
    84      "Clicked Link": "clicked",
    85      "Submitted Data": "submitted_data",
    86  }
    87  
    88  // This is an underwhelming attempt at an enum
    89  // until I have time to refactor this appropriately.
    90  var progressListing = [
    91      "Email Sent",
    92      "Email Opened",
    93      "Clicked Link",
    94      "Submitted Data"
    95  ]
    96  
    97  var campaign = {}
    98  var bubbles = []
    99  
   100  function dismiss() {
   101      $("#modal\\.flashes").empty()
   102      $("#modal").modal('hide')
   103      $("#resultsTable").dataTable().DataTable().clear().draw()
   104  }
   105  
   106  // Deletes a campaign after prompting the user
   107  function deleteCampaign() {
   108      swal({
   109          title: "Are you sure?",
   110          text: "This will delete the campaign. This can't be undone!",
   111          type: "warning",
   112          animation: false,
   113          showCancelButton: true,
   114          confirmButtonText: "Delete Campaign",
   115          confirmButtonColor: "#428bca",
   116          reverseButtons: true,
   117          allowOutsideClick: false,
   118          showLoaderOnConfirm: true,
   119          preConfirm: function () {
   120              return new Promise(function (resolve, reject) {
   121                  api.campaignId.delete(campaign.id)
   122                      .success(function (msg) {
   123                          resolve()
   124                      })
   125                      .error(function (data) {
   126                          reject(data.responseJSON.message)
   127                      })
   128              })
   129          }
   130      }).then(function () {
   131          swal(
   132              'Campaign Deleted!',
   133              'This campaign has been deleted!',
   134              'success'
   135          );
   136          $('button:contains("OK")').on('click', function () {
   137              location.href = '/campaigns'
   138          })
   139      })
   140  }
   141  
   142  // Completes a campaign after prompting the user
   143  function completeCampaign() {
   144      swal({
   145          title: "Are you sure?",
   146          text: "Gophish will stop processing events for this campaign",
   147          type: "warning",
   148          animation: false,
   149          showCancelButton: true,
   150          confirmButtonText: "Complete Campaign",
   151          confirmButtonColor: "#428bca",
   152          reverseButtons: true,
   153          allowOutsideClick: false,
   154          showLoaderOnConfirm: true,
   155          preConfirm: function () {
   156              return new Promise(function (resolve, reject) {
   157                  api.campaignId.complete(campaign.id)
   158                      .success(function (msg) {
   159                          resolve()
   160                      })
   161                      .error(function (data) {
   162                          reject(data.responseJSON.message)
   163                      })
   164              })
   165          }
   166      }).then(function () {
   167          swal(
   168              'Campaign Completed!',
   169              'This campaign has been completed!',
   170              'success'
   171          );
   172          $('#complete_button')[0].disabled = true;
   173          $('#complete_button').text('Completed!')
   174          doPoll = false;
   175      })
   176  }
   177  
   178  // Exports campaign results as a CSV file
   179  function exportAsCSV(scope) {
   180      exportHTML = $("#exportButton").html()
   181      var csvScope = null
   182      switch (scope) {
   183          case "results":
   184              csvScope = campaign.results
   185              break;
   186          case "events":
   187              csvScope = campaign.timeline
   188              break;
   189      }
   190      if (!csvScope) {
   191          return
   192      }
   193      $("#exportButton").html('<i class="fa fa-spinner fa-spin"></i>')
   194      var csvString = Papa.unparse(csvScope, {})
   195      var csvData = new Blob([csvString], {
   196          type: 'text/csv;charset=utf-8;'
   197      });
   198      if (navigator.msSaveBlob) {
   199          navigator.msSaveBlob(csvData, scope + '.csv');
   200      } else {
   201          var csvURL = window.URL.createObjectURL(csvData);
   202          var dlLink = document.createElement('a');
   203          dlLink.href = csvURL;
   204          dlLink.setAttribute('download', scope + '.csv');
   205          document.body.appendChild(dlLink)
   206          dlLink.click();
   207          document.body.removeChild(dlLink)
   208      }
   209      $("#exportButton").html(exportHTML)
   210  }
   211  
   212  function replay(event_idx) {
   213      request = campaign.timeline[event_idx]
   214      details = JSON.parse(request.details)
   215      url = null
   216      form = $('<form>').attr({
   217          method: 'POST',
   218          target: '_blank',
   219      })
   220      /* Create a form object and submit it */
   221      $.each(Object.keys(details.payload), function (i, param) {
   222          if (param == "rid") {
   223              return true;
   224          }
   225          if (param == "__original_url") {
   226              url = details.payload[param];
   227              return true;
   228          }
   229          $('<input>').attr({
   230              name: param,
   231          }).val(details.payload[param]).appendTo(form);
   232      })
   233      /* Ensure we know where to send the user */
   234      // Prompt for the URL
   235      swal({
   236          title: 'Where do you want the credentials submitted to?',
   237          input: 'text',
   238          showCancelButton: true,
   239          inputPlaceholder: "http://example.com/login",
   240          inputValue: url || "",
   241          inputValidator: function (value) {
   242              return new Promise(function (resolve, reject) {
   243                  if (value) {
   244                      resolve();
   245                  } else {
   246                      reject('Invalid URL.');
   247                  }
   248              });
   249          }
   250      }).then(function (result) {
   251          url = result
   252          submitForm()
   253      })
   254      return
   255      submitForm()
   256  
   257      function submitForm() {
   258          form.attr({
   259              action: url
   260          })
   261          form.appendTo('body').submit().remove()
   262      }
   263  }
   264  
   265  function renderTimeline(data) {
   266      record = {
   267          "first_name": data[2],
   268          "last_name": data[3],
   269          "email": data[4],
   270          "position": data[5]
   271      }
   272      results = '<div class="timeline col-sm-12 well well-lg">' +
   273          '<h6>Timeline for ' + escapeHtml(record.first_name) + ' ' + escapeHtml(record.last_name) +
   274          '</h6><span class="subtitle">Email: ' + escapeHtml(record.email) + '</span>' +
   275          '<div class="timeline-graph col-sm-6">'
   276      $.each(campaign.timeline, function (i, event) {
   277          if (!event.email || event.email == record.email) {
   278              // Add the event
   279              results += '<div class="timeline-entry">' +
   280                  '    <div class="timeline-bar"></div>'
   281              results +=
   282                  '    <div class="timeline-icon ' + statuses[event.message].label + '">' +
   283                  '    <i class="fa ' + statuses[event.message].icon + '"></i></div>' +
   284                  '    <div class="timeline-message">' + escapeHtml(event.message) +
   285                  '    <span class="timeline-date">' + moment.utc(event.time).local().format('MMMM Do YYYY h:mm a') + '</span>'
   286              if (event.details) {
   287                  if (event.message == "Submitted Data") {
   288                      results += '<div class="timeline-replay-button"><button onclick="replay(' + i + ')" class="btn btn-success">'
   289                      results += '<i class="fa fa-refresh"></i> Replay Credentials</button></div>'
   290                      results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>'
   291                  }
   292                  details = JSON.parse(event.details)
   293                  if (details.payload) {
   294                      results += '<div class="timeline-event-results">'
   295                      results += '    <table class="table table-condensed table-bordered table-striped">'
   296                      results += '        <thead><tr><th>Parameter</th><th>Value(s)</tr></thead><tbody>'
   297                      $.each(Object.keys(details.payload), function (i, param) {
   298                          if (param == "rid") {
   299                              return true;
   300                          }
   301                          results += '    <tr>'
   302                          results += '        <td>' + escapeHtml(param) + '</td>'
   303                          results += '        <td>' + escapeHtml(details.payload[param]) + '</td>'
   304                          results += '    </tr>'
   305                      })
   306                      results += '       </tbody></table>'
   307                      results += '</div>'
   308                  }
   309                  if (details.error) {
   310                      results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>'
   311                      results += '<div class="timeline-event-results">'
   312                      results += '<span class="label label-default">Error</span> ' + details.error
   313                      results += '</div>'
   314                  }
   315              }
   316              results += '</div></div>'
   317          }
   318      })
   319      results += '</div></div>'
   320      return results
   321  }
   322  
   323  var renderTimelineChart = function (chartopts) {
   324      return Highcharts.chart('timeline_chart', {
   325          chart: {
   326              zoomType: 'x',
   327              type: 'line',
   328              height: "200px"
   329          },
   330          title: {
   331              text: 'Campaign Timeline'
   332          },
   333          xAxis: {
   334              type: 'datetime',
   335              dateTimeLabelFormats: {
   336                  second: '%l:%M:%S',
   337                  minute: '%l:%M',
   338                  hour: '%l:%M',
   339                  day: '%b %d, %Y',
   340                  week: '%b %d, %Y',
   341                  month: '%b %Y'
   342              }
   343          },
   344          yAxis: {
   345              min: 0,
   346              max: 2,
   347              visible: false,
   348              tickInterval: 1,
   349              labels: {
   350                  enabled: false
   351              },
   352              title: {
   353                  text: ""
   354              }
   355          },
   356          tooltip: {
   357              formatter: function () {
   358                  return Highcharts.dateFormat('%A, %b %d %l:%M:%S %P', new Date(this.x)) +
   359                      '<br>Event: ' + this.point.message + '<br>Email: <b>' + this.point.email + '</b>'
   360              }
   361          },
   362          legend: {
   363              enabled: false
   364          },
   365          plotOptions: {
   366              series: {
   367                  marker: {
   368                      enabled: true,
   369                      symbol: 'circle',
   370                      radius: 3
   371                  },
   372                  cursor: 'pointer',
   373              },
   374              line: {
   375                  states: {
   376                      hover: {
   377                          lineWidth: 1
   378                      }
   379                  }
   380              }
   381          },
   382          credits: {
   383              enabled: false
   384          },
   385          series: [{
   386              data: chartopts['data'],
   387              dashStyle: "shortdash",
   388              color: "#cccccc",
   389              lineWidth: 1
   390          }]
   391      })
   392  }
   393  
   394  /* Renders a pie chart using the provided chartops */
   395  var renderPieChart = function (chartopts) {
   396      return Highcharts.chart(chartopts['elemId'], {
   397          chart: {
   398              type: 'pie',
   399              events: {
   400                  load: function () {
   401                      var chart = this,
   402                          rend = chart.renderer,
   403                          pie = chart.series[0],
   404                          left = chart.plotLeft + pie.center[0],
   405                          top = chart.plotTop + pie.center[1];
   406                      this.innerText = rend.text(chartopts['data'][0].y, left, top).
   407                          attr({
   408                              'text-anchor': 'middle',
   409                              'font-size': '24px',
   410                              'font-weight': 'bold',
   411                              'fill': chartopts['colors'][0],
   412                              'font-family': 'Helvetica,Arial,sans-serif'
   413                          }).add();
   414                  },
   415                  render: function () {
   416                      this.innerText.attr({ text: chartopts['data'][0].y })
   417                  }
   418              }
   419          },
   420          title: {
   421              text: chartopts['title']
   422          },
   423          plotOptions: {
   424              pie: {
   425                  innerSize: '80%',
   426                  dataLabels: {
   427                      enabled: false
   428                  }
   429              }
   430          },
   431          credits: {
   432              enabled: false
   433          },
   434          tooltip: {
   435              formatter: function () {
   436                  if (this.key == undefined) {
   437                      return false
   438                  }
   439                  return '<span style="color:' + this.color + '">\u25CF</span>' + this.point.name + ': <b>' + this.y + '</b><br/>'
   440              }
   441          },
   442          series: [{
   443              data: chartopts['data'],
   444              colors: chartopts['colors'],
   445          }]
   446      })
   447  }
   448  
   449  /* poll - Queries the API and updates the UI with the results
   450   *
   451   * Updates:
   452   * * Timeline Chart
   453   * * Email (Donut) Chart
   454   * * Map Bubbles
   455   * * Datatables
   456   */
   457  function poll() {
   458      api.campaignId.results(campaign.id)
   459          .success(function (c) {
   460              campaign = c
   461              /* Update the timeline */
   462              var timeline_series_data = []
   463              $.each(campaign.timeline, function (i, event) {
   464                  var event_date = moment.utc(event.time).local()
   465                  timeline_series_data.push({
   466                      email: event.email,
   467                      x: event_date.valueOf(),
   468                      y: 1
   469                  })
   470              })
   471              var timeline_series_data = []
   472              $.each(campaign.timeline, function (i, event) {
   473                  var event_date = moment.utc(event.time).local()
   474                  timeline_series_data.push({
   475                      email: event.email,
   476                      message: event.message,
   477                      x: event_date.valueOf(),
   478                      y: 1,
   479                      marker: {
   480                          fillColor: statuses[event.message].color
   481                      }
   482                  })
   483              })
   484              var timeline_chart = $("#timeline_chart").highcharts()
   485              timeline_chart.series[0].update({
   486                  data: timeline_series_data
   487              })
   488              /* Update the results donut chart */
   489              var email_series_data = {}
   490              // Load the initial data
   491              Object.keys(statusMapping).forEach(function (k) {
   492                  email_series_data[k] = 0
   493              });
   494              $.each(campaign.results, function (i, result) {
   495                  email_series_data[result.status]++;
   496                  // Backfill status values
   497                  var step = progressListing.indexOf(result.status)
   498                  for (var i = 0; i < step; i++) {
   499                      email_series_data[progressListing[i]]++
   500                  }
   501              })
   502              $.each(email_series_data, function (status, count) {
   503                  var email_data = []
   504                  if (!(status in statusMapping)) {
   505                      return true
   506                  }
   507                  email_data.push({
   508                      name: status,
   509                      y: count
   510                  })
   511                  email_data.push({
   512                      name: '',
   513                      y: campaign.results.length - count
   514                  })
   515                  var chart = $("#" + statusMapping[status] + "_chart").highcharts()
   516                  chart.series[0].update({
   517                      data: email_data
   518                  })
   519              })
   520              /* Update the datatable */
   521              resultsTable = $("#resultsTable").DataTable()
   522              resultsTable.rows().every(function (i, tableLoop, rowLoop) {
   523                  var row = this.row(i)
   524                  var rowData = row.data()
   525                  var rid = rowData[0]
   526                  $.each(campaign.results, function (j, result) {
   527                      if (result.id == rid) {
   528                          var label = statuses[result.status].label || "label-default";
   529                          rowData[6] = "<span class=\"label " + label + "\">" + result.status + "</span>"
   530                          resultsTable.row(i).data(rowData).draw(false)
   531                          if (row.child.isShown()) {
   532                              row.child(renderTimeline(row.data()))
   533                          }
   534                          return false
   535                      }
   536                  })
   537              })
   538              /* Update the map information */
   539              bubbles = []
   540              $.each(campaign.results, function (i, result) {
   541                  // Check that it wasn't an internal IP
   542                  if (result.latitude == 0 && result.longitude == 0) {
   543                      return true;
   544                  }
   545                  newIP = true
   546                  $.each(bubbles, function (i, bubble) {
   547                      if (bubble.ip == result.ip) {
   548                          bubbles[i].radius += 1
   549                          newIP = false
   550                          return false
   551                      }
   552                  })
   553                  if (newIP) {
   554                      bubbles.push({
   555                          latitude: result.latitude,
   556                          longitude: result.longitude,
   557                          name: result.ip,
   558                          fillKey: "point",
   559                          radius: 2
   560                      })
   561                  }
   562              })
   563              map.bubbles(bubbles)
   564              $("#refresh_message").hide()
   565              $("#refresh_btn").show()
   566          })
   567  }
   568  
   569  function load() {
   570      campaign.id = window.location.pathname.split('/').slice(-1)[0]
   571      api.campaignId.results(campaign.id)
   572          .success(function (c) {
   573              campaign = c
   574              if (campaign) {
   575                  $("title").text(c.name + " - Gophish")
   576                  $("#loading").hide()
   577                  $("#campaignResults").show()
   578                  // Set the title
   579                  $("#page-title").text("Results for " + c.name)
   580                  if (c.status == "Completed") {
   581                      $('#complete_button')[0].disabled = true;
   582                      $('#complete_button').text('Completed!');
   583                      doPoll = false;
   584                  }
   585                  // Setup tooltips
   586                  $('[data-toggle="tooltip"]').tooltip()
   587                  // Setup viewing the details of a result
   588                  $("#resultsTable").on("click", ".timeline-event-details", function () {
   589                      // Show the parameters
   590                      payloadResults = $(this).parent().find(".timeline-event-results")
   591                      if (payloadResults.is(":visible")) {
   592                          $(this).find("i").removeClass("fa-caret-down")
   593                          $(this).find("i").addClass("fa-caret-right")
   594                          payloadResults.hide()
   595                      } else {
   596                          $(this).find("i").removeClass("fa-caret-right")
   597                          $(this).find("i").addClass("fa-caret-down")
   598                          payloadResults.show()
   599                      }
   600                  })
   601                  // Setup the results table
   602                  resultsTable = $("#resultsTable").DataTable({
   603                      destroy: true,
   604                      "order": [
   605                          [2, "asc"]
   606                      ],
   607                      columnDefs: [{
   608                          orderable: false,
   609                          targets: "no-sort"
   610                      }, {
   611                          className: "details-control",
   612                          "targets": [1]
   613                      }, {
   614                          "visible": false,
   615                          "targets": [0]
   616                      }]
   617                  });
   618                  resultsTable.clear();
   619                  var email_series_data = {}
   620                  var timeline_series_data = []
   621                  Object.keys(statusMapping).forEach(function (k) {
   622                      email_series_data[k] = 0
   623                  });
   624                  $.each(campaign.results, function (i, result) {
   625                      label = statuses[result.status].label || "label-default";
   626                      resultsTable.row.add([
   627                          result.id,
   628                          "<i class=\"fa fa-caret-right\"></i>",
   629                          escapeHtml(result.first_name) || "",
   630                          escapeHtml(result.last_name) || "",
   631                          escapeHtml(result.email) || "",
   632                          escapeHtml(result.position) || "",
   633                          "<span class=\"label " + label + "\">" + result.status + "</span>"
   634                      ]).draw()
   635                      email_series_data[result.status]++;
   636                      // Backfill status values
   637                      var step = progressListing.indexOf(result.status)
   638                      for (var i = 0; i < step; i++) {
   639                          email_series_data[progressListing[i]]++
   640                      }
   641                  })
   642                  // Setup the individual timelines
   643                  $('#resultsTable tbody').on('click', 'td.details-control', function () {
   644                      var tr = $(this).closest('tr');
   645                      var row = resultsTable.row(tr);
   646                      if (row.child.isShown()) {
   647                          // This row is already open - close it
   648                          row.child.hide();
   649                          tr.removeClass('shown');
   650                          $(this).find("i").removeClass("fa-caret-down")
   651                          $(this).find("i").addClass("fa-caret-right")
   652                          row.invalidate('dom').draw(false)
   653                      } else {
   654                          // Open this row
   655                          $(this).find("i").removeClass("fa-caret-right")
   656                          $(this).find("i").addClass("fa-caret-down")
   657                          row.child(renderTimeline(row.data())).show();
   658                          tr.addClass('shown');
   659                          row.invalidate('dom').draw(false)
   660                      }
   661                  });
   662                  // Setup the graphs
   663                  $.each(campaign.timeline, function (i, event) {
   664                      var event_date = moment.utc(event.time).local()
   665                      timeline_series_data.push({
   666                          email: event.email,
   667                          message: event.message,
   668                          x: event_date.valueOf(),
   669                          y: 1,
   670                          marker: {
   671                              fillColor: statuses[event.message].color
   672                          }
   673                      })
   674                  })
   675                  renderTimelineChart({
   676                      data: timeline_series_data
   677                  })
   678                  $.each(email_series_data, function (status, count) {
   679                      var email_data = []
   680                      if (!(status in statusMapping)) {
   681                          return true
   682                      }
   683                      email_data.push({
   684                          name: status,
   685                          y: count
   686                      })
   687                      email_data.push({
   688                          name: '',
   689                          y: campaign.results.length - count
   690                      })
   691                      var chart = renderPieChart({
   692                          elemId: statusMapping[status] + '_chart',
   693                          title: status,
   694                          name: status,
   695                          data: email_data,
   696                          colors: [statuses[status].color, '#dddddd']
   697                      })
   698                  })
   699                  if (!map) {
   700                      map = new Datamap({
   701                          element: document.getElementById("resultsMap"),
   702                          responsive: true,
   703                          fills: {
   704                              defaultFill: "#ffffff",
   705                              point: "#283F50"
   706                          },
   707                          geographyConfig: {
   708                              highlightFillColor: "#1abc9c",
   709                              borderColor: "#283F50"
   710                          },
   711                          bubblesConfig: {
   712                              borderColor: "#283F50"
   713                          }
   714                      });
   715                  }
   716                  $.each(campaign.results, function (i, result) {
   717                      // Check that it wasn't an internal IP
   718                      if (result.latitude == 0 && result.longitude == 0) {
   719                          return true;
   720                      }
   721                      newIP = true
   722                      $.each(bubbles, function (i, bubble) {
   723                          if (bubble.ip == result.ip) {
   724                              bubbles[i].radius += 1
   725                              newIP = false
   726                              return false
   727                          }
   728                      })
   729                      if (newIP) {
   730                          bubbles.push({
   731                              latitude: result.latitude,
   732                              longitude: result.longitude,
   733                              name: result.ip,
   734                              fillKey: "point",
   735                              radius: 2
   736                          })
   737                      }
   738                  })
   739                  map.bubbles(bubbles)
   740              }
   741              // Load up the map data (only once!)
   742              $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
   743                  if ($(e.target).attr('href') == "#overview") {
   744                      if (!map) {
   745                          map = new Datamap({
   746                              element: document.getElementById("resultsMap"),
   747                              responsive: true,
   748                              fills: {
   749                                  defaultFill: "#ffffff"
   750                              },
   751                              geographyConfig: {
   752                                  highlightFillColor: "#1abc9c",
   753                                  borderColor: "#283F50"
   754                              }
   755                          });
   756                      }
   757                  }
   758              })
   759          })
   760          .error(function () {
   761              $("#loading").hide()
   762              errorFlash(" Campaign not found!")
   763          })
   764  }
   765  
   766  var setRefresh
   767  function refresh() {
   768      if (!doPoll) {
   769          return;
   770      }
   771      $("#refresh_message").show()
   772      $("#refresh_btn").hide()
   773      poll()
   774      clearTimeout(setRefresh)
   775      setRefresh = setTimeout(refresh, 60000)
   776  };
   777  
   778  
   779  
   780  $(document).ready(function () {
   781      Highcharts.setOptions({
   782          global: {
   783              useUTC: false
   784          }
   785      })
   786      load();
   787  
   788      // Start the polling loop
   789      setRefresh = setTimeout(refresh, 60000)
   790  })