blob: 8ac61088aef0fd130d553788f0aba23617d329d6 [file] [log] [blame]
Timo Tijhof51516cd2013-04-09 01:32:29 +02001// Client script for Zuul status page
2//
3// Copyright 2012 OpenStack Foundation
4// Copyright 2013 Timo Tijhof
5// Copyright 2013 Wikimedia Foundation
6//
7// Licensed under the Apache License, Version 2.0 (the "License"); you may
8// not use this file except in compliance with the License. You may obtain
9// a copy of the License at
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16// License for the specific language governing permissions and limitations
17// under the License.
18
19(function ($) {
20 var $container, $msg, $msgWrap, $indicator, $queueInfo, $queueEventsNum, $queueResultsNum, $pipelines,
21 prevHtml, xhr, zuul, $jq,
22 demo = location.search.match(/[?&]demo=([^?&]*)/),
23 source = demo ?
24 './status-' + (demo[1] || 'basic') + '.json-sample' :
James E. Blair7c7ed7a2013-05-15 13:13:26 -070025 'status.json';
Timo Tijhof51516cd2013-04-09 01:32:29 +020026
27 zuul = {
28 enabled: true,
29
30 schedule: function () {
31 if (!zuul.enabled) {
32 setTimeout(zuul.schedule, 5000);
33 return;
34 }
35 zuul.update().complete(function () {
36 setTimeout(zuul.schedule, 5000);
37 });
38 },
39
40 /** @return {jQuery.Promise} */
41 update: function () {
42 // Cancel the previous update if it hasn't completed yet.
43 if (xhr) {
44 xhr.abort();
45 }
46
47 zuul.emit('update-start');
48
49 xhr = $.ajax({
50 url: source,
51 dataType: 'json',
52 cache: false
53 })
54 .done(function (data) {
55 var html = '';
56 data = data || {};
57
58 if ('message' in data) {
59 $msg.html(data.message);
60 $msgWrap.removeClass('zuul-msg-wrap-off');
61 } else {
62 $msg.empty();
63 $msgWrap.addClass('zuul-msg-wrap-off');
64 }
65
66 $.each(data.pipelines, function (i, pipeline) {
67 html += zuul.format.pipeline(pipeline);
68 });
69
70 // Only re-parse the DOM if we have to
71 if (html !== prevHtml) {
72 prevHtml = html;
73 $pipelines.html(html);
74 }
75
76 $queueEventsNum.text(
77 data.trigger_event_queue ? data.trigger_event_queue.length : '0'
78 );
79 $queueResultsNum.text(
80 data.result_event_queue ? data.result_event_queue.length : '0'
81 );
82 })
83 .fail(function (err, jqXHR, errMsg) {
84 $msg.text(source + ': ' + errMsg).show();
85 $msgWrap.removeClass('zuul-msg-wrap-off');
86 })
87 .complete(function () {
88 xhr = undefined;
89 zuul.emit('update-end');
90 });
91
92 return xhr;
93 },
94
95 format: {
96 change: function (change) {
97 var html = '<div class="well well-small zuul-change"><ul class="nav nav-list">',
98 id = change.id,
99 url = change.url;
100
101 html += '<li class="nav-header">' + change.project;
102 if (id.length === 40) {
103 id = id.substr(0, 7);
104 }
105 html += ' <span class="zuul-change-id">';
106 if (url !== null) {
107 html += '<a href="' + url + '">';
108 }
109 html += id;
110 if (url !== null) {
111 html += '</a>';
112 }
113 html += '</span></li>';
114
115 $.each(change.jobs, function (i, job) {
116 var result = job.result ? job.result.toLowerCase() : null,
117 resultClass = 'zuul-result label';
118 if (result === null) {
119 result = job.url ? 'in progress' : 'queued';
120 }
121 switch (result) {
122 case 'success':
123 resultClass += ' label-success';
124 break;
125 case 'failure':
126 resultClass += ' label-important';
127 break;
128 case 'lost':
129 case 'unstable':
130 resultClass += ' label-warning';
131 break;
132 }
133 html += '<li class="zuul-change-job">';
134 html += job.url !== null ?
135 '<a href="' + job.url + '" class="zuul-change-job-link">' :
136 '<span class="zuul-change-job-link">';
137 html += job.name;
138 html += ' <span class="' + resultClass + '">' + result + '</span>';
139 if (job.voting === false) {
140 html += ' <span class="muted">(non-voting)</span>';
141 }
142 html += job.url !== null ? '</a>' : '</span>';
143 html += '</li>';
144 });
145
146 html += '</ul></div>';
147 return html;
148 },
149
150 pipeline: function (pipeline) {
151 var html = '<div class="zuul-pipeline span4"><h3>' +
152 pipeline.name + '</h3>';
153 if (typeof pipeline.description === 'string') {
154 html += '<p><small>' + pipeline.description + '</small></p>';
155 }
156
157 $.each(pipeline.change_queues, function (queueNum, changeQueue) {
158 $.each(changeQueue.heads, function (headNum, changes) {
159 if (pipeline.change_queues.length > 1 && headNum === 0) {
160 var name = changeQueue.name;
161 html += '<p>Queue: <abbr title="' + name + '">';
162 if (name.length > 32) {
163 name = name.substr(0, 32) + '...';
164 }
165 html += name + '</abbr></p>';
166 }
167 $.each(changes, function (changeNum, change) {
168 // If there are multiple changes in the same head it means they're connected
169 if (changeNum > 0) {
170 html += '<div class="zuul-change-arrow">&uarr;</div>';
171 }
172 html += zuul.format.change(change);
173 });
174 });
175 });
176
177 html += '</div>';
178 return html;
179 }
180 },
181
182 emit: function () {
183 $jq.trigger.apply($jq, arguments);
184 return this;
185 },
186 on: function () {
187 $jq.on.apply($jq, arguments);
188 return this;
189 },
190 one: function () {
191 $jq.one.apply($jq, arguments);
192 return this;
193 }
194 };
195
196 $jq = $(zuul);
197
198 $jq.on('update-start', function () {
199 $container.addClass('zuul-container-loading');
200 $indicator.addClass('zuul-spinner-on');
201 });
202
203 $jq.on('update-end', function () {
204 $container.removeClass('zuul-container-loading');
205 setTimeout(function () {
206 $indicator.removeClass('zuul-spinner-on');
207 }, 550);
208 });
209
210 $jq.one('update-end', function () {
211 // Do this asynchronous so that if the first update adds a message, it will not animate
212 // while we fade in the content. Instead it simply appears with the rest of the content.
213 setTimeout(function () {
214 $container.addClass('zuul-container-ready'); // Fades in the content
215 });
216 });
217
218 $(function ($) {
219 $msg = $('<div class="zuul-msg alert alert-error"></div>');
220 $msgWrap = $msg.wrap('<div class="zuul-msg-wrap zuul-msg-wrap-off"></div>').parent();
221 $indicator = $('<span class="btn pull-right zuul-spinner">updating <i class="icon-refresh"></i></span>');
222 $queueInfo = $('<p>Queue lengths: <span>0</span> events, <span>0</span> results.</p>');
223 $queueEventsNum = $queueInfo.find('span').eq(0);
224 $queueResultsNum = $queueEventsNum.next();
225 $pipelines = $('<div class="row"></div>');
226
227 $container = $('#zuul-container').append($msgWrap, $indicator, $queueInfo, $pipelines);
228
229 zuul.schedule();
230
231 $(document).on({
232 'show.visibility': function () {
233 zuul.enabled = true;
234 zuul.update();
235 },
236 'hide.visibility': function () {
237 zuul.enabled = false;
238 }
239 });
240 });
241}(jQuery));