1/*
2 * Copyright 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/**
18 * @author: Brett Slatkin (bslatkin@google.com)
19 */
20
21// Format a number with a minimum number of digits and a padding character.
22function leftPadNumber(number, minSize, paddingChar) {
23  var stringified = '' + number;
24  if (stringified.length < minSize) {
25    for (var i = 0; i < (minSize - stringified.length); ++i) {
26      stringified = paddingChar + stringified;
27    }
28  }
29  return stringified;
30}
31
32
33// Convert milliseconds since the epoch to an ISO8601 datestring.
34function getIso8601String(timeMs) {
35  var time = new Date();
36  time.setTime(timeMs);
37  return '' +
38      time.getUTCFullYear() + '-' +
39      leftPadNumber(time.getUTCMonth() + 1, 2, '0') + '-' +
40      leftPadNumber(time.getUTCDate(), 2, '0') + 'T' +
41      leftPadNumber(time.getUTCHours(), 2, '0') + ':' +
42      leftPadNumber(time.getUTCMinutes(), 2, '0') + ':' +
43      leftPadNumber(time.getUTCSeconds(), 2, '0') + 'Z';
44}
45
46
47// Get time string for job runtime. Specially handle number of days running as
48// a prefix and milliseconds as a suffix. If the runtime is less than one
49// minute, use the format "38.123 seconds" instead.
50function getElapsedTimeString(startTimestampMs, updatedTimestampMs) {
51  var updatedDiff = Math.max(0, updatedTimestampMs - startTimestampMs);
52  var updatedDays = Math.floor(updatedDiff / 86400000.0);
53  updatedDiff -= (updatedDays * 86400000.0);
54  var updatedHours = Math.floor(updatedDiff / 3600000.0);
55  updatedDiff -= (updatedHours * 3600000.0);
56  var updatedMinutes = Math.floor(updatedDiff / 60000.0);
57  updatedDiff -= (updatedMinutes * 60000.0);
58  var updatedSeconds = Math.floor(updatedDiff / 1000.0);
59  updatedDiff -= (updatedSeconds * 1000.0);
60  var updatedMs = Math.floor(updatedDiff / 1.0);
61
62  var updatedString = '';
63
64  if (updatedMinutes > 0) {
65    if (updatedDays == 1) {
66      updatedString = '1 day, ';
67    } else if (updatedDays > 1) {
68      updatedString = '' + updatedDays + ' days, ';
69    }
70    updatedString +=
71        leftPadNumber(updatedHours, 2, '0') + ':' +
72        leftPadNumber(updatedMinutes, 2, '0') + ':' +
73        leftPadNumber(updatedSeconds, 2, '0');
74    if (updatedMs > 0) {
75      updatedString += '.' + leftPadNumber(updatedMs, 3, '0');
76    }
77  } else {
78    updatedString += updatedSeconds;
79    updatedString += '.' + leftPadNumber(updatedMs, 3, '0');
80    updatedString += ' seconds';
81  }
82
83  return updatedString;
84}
85
86
87// Clears the status butter.
88function clearButter() {
89  $('#butter').css('display', 'none');
90}
91
92
93// Sets the status butter, optionally indicating if it's an error message.
94function setButter(message, error, traceback, asHtml) {
95  var butter = $('#butter');
96  // Prevent flicker on butter update by hiding it first.
97  butter.css('display', 'none');
98  if (error) {
99    butter.removeClass('info').addClass('error');
100  } else {
101    butter.removeClass('error').addClass('info');
102  }
103  butter.children().remove();
104  if (asHtml) {
105    butter.append($('<div>').html(message));
106  } else {
107    butter.append($('<div>').text(message));
108  }
109
110  function centerButter() {
111    butter.css('left', ($(window).width() - $(butter).outerWidth()) / 2);
112  }
113
114  if (traceback) {
115    var showDetail = $('<a href="">').text('Detail');
116    showDetail.click(function(event) {
117      $('#butter-detail').toggle();
118      centerButter();
119      event.preventDefault();
120    });
121    var butterDetail = $('<pre id="butter-detail">').text(traceback);
122    butterDetail.css('display', 'none');
123
124    butter.append(showDetail);
125    butter.append(butterDetail);
126  }
127  centerButter();
128  butter.css('display', null);
129}
130
131
132// Given an AJAX error message (which is empty or null on success) and a
133// data payload containing JSON, parses the data payload and returns the object.
134// Server-side errors and AJAX errors will be brought to the user's attention
135// if present in the response object
136function getResponseDataJson(error, data) {
137  var response = null;
138  try {
139    response = $.parseJSON(data);
140  } catch (e) {
141    error = '' + e;
142  }
143  if (response && response.error_class) {
144    error = response.error_class + ': ' + response.error_message;
145    setButter('Error -- ' + error, true, response.error_traceback);
146  } else if (!response) {
147    setButter('Error -- Could not parse response JSON data.', true);
148  } else {
149    return response;
150  }
151  return null;
152}
153