Make zuul javascript app more modular

Refactor the zuul status app to be more modular. This allows the
controlling page (eg index.html) to load in only the components
it requires. This will make it easier to format just one patchset
or change where the layout controls are etc.

This is to work towards overlaying zuul results in gerrit.

Change-Id: Idfba9f08c25850c340cc962fa0f5f9652306b877
This commit is contained in:
Joshua Hesketh 2014-04-30 19:23:36 +10:00
parent 9d013546a3
commit 0f5c66a2bc
2 changed files with 424 additions and 396 deletions

View File

@ -19,19 +19,6 @@
'use strict';
(function ($) {
var $container, $msg, $indicator, $queueInfo, $queueEventsNum,
$queueResultsNum, $pipelines, $jq;
var xhr, zuul,
zuul_graph_update_count = 0,
zuul_sparkline_urls = {},
current_filter = '',
demo = location.search.match(/[?&]demo=([^?&]*)/),
source_url = location.search.match(/[?&]source_url=([^?&]*)/),
source = demo ?
'./status-' + (demo[1] || 'basic') + '.json-sample' :
'status.json';
source = source_url ? source_url[1] : source;
function set_cookie(name, value) {
document.cookie = name + '=' + value + '; path=/';
}
@ -51,96 +38,50 @@
return default_value;
}
$.zuul = function(options) {
var options = $.extend({
'enabled': true,
'graphite_url': '',
'source': 'status.json',
'msg_id': '#zuul_msg',
'pipelines_id': '#zuul_pipelines',
'queue_events_num': '#zuul_queue_events_num',
'queue_results_num': '#zuul_queue_results_num',
}, options);
zuul = {
enabled: true,
graphite_url: '',
collapsed_exceptions: [],
var collapsed_exceptions = [];
var current_filter = read_cookie('zuul_filter_string', current_filter);
var $jq;
schedule: function () {
if (!zuul.enabled) {
setTimeout(zuul.schedule, 5000);
return;
}
zuul.update().complete(function () {
setTimeout(zuul.schedule, 5000);
});
var xhr,
zuul_graph_update_count = 0,
zuul_sparkline_urls = {};
/* Only update graphs every minute */
if (zuul_graph_update_count > 11) {
zuul_graph_update_count = 0;
zuul.update_sparklines();
}
},
/** @return {jQuery.Promise} */
update: function () {
// Cancel the previous update if it hasn't completed yet.
if (xhr) {
xhr.abort();
}
zuul.emit('update-start');
xhr = $.getJSON(source)
.done(function (data) {
if ('message' in data) {
$msg.removeClass('alert-danger').addClass('alert-info');
$msg.text(data.message);
$msg.show();
} else {
$msg.empty();
$msg.hide();
}
if ('zuul_version' in data) {
$('#zuul-version-span').text(data.zuul_version);
}
if ('last_reconfigured' in data) {
var last_reconfigured =
new Date(data.last_reconfigured);
$('#last-reconfigured-span').text(
last_reconfigured.toString());
}
$pipelines.html('');
$.each(data.pipelines, function (i, pipeline) {
$pipelines.append(zuul.format.pipeline(pipeline));
function get_sparkline_url(pipeline_name) {
if (options.graphite_url !== '') {
if (!(pipeline_name in zuul_sparkline_urls)) {
zuul_sparkline_urls[pipeline_name] = $.fn.graphite
.geturl({
url: options.graphite_url,
from: "-8hours",
width: 100,
height: 26,
margin: 0,
hideLegend: true,
hideAxes: true,
hideGrid: true,
target: [
"color(stats.gauges.zuul.pipeline." + pipeline_name
+ ".current_changes, '6b8182')"
]
});
}
return zuul_sparkline_urls[pipeline_name];
}
return false;
}
$queueEventsNum.text(
data.trigger_event_queue ?
data.trigger_event_queue.length : '0'
);
$queueResultsNum.text(
data.result_event_queue ?
data.result_event_queue.length : '0'
);
})
.fail(function (err, jqXHR, errMsg) {
$msg.text(source + ': ' + errMsg).show();
$msg.removeClass('zuul-msg-wrap-off');
})
.complete(function () {
xhr = undefined;
zuul.emit('update-end');
});
return xhr;
},
update_sparklines: function() {
$.each(zuul_sparkline_urls, function(name, url) {
var newimg = new Image();
var parts = url.split('#');
newimg.src = parts[0] + '#' + new Date().getTime();
$(newimg).load(function (x) {
zuul_sparkline_urls[name] = newimg.src;
});
});
},
format: {
var format = {
job: function(job) {
var $job_line = $('<span />');
@ -160,7 +101,7 @@
);
}
$job_line.append(zuul.format.job_status(job));
$job_line.append(this.job_status(job));
if (job.voting === false) {
$job_line.append(
@ -180,11 +121,11 @@
}
if (result === 'in progress') {
return zuul.format.job_progress_bar(job.elapsed_time,
return this.job_progress_bar(job.elapsed_time,
job.remaining_time);
}
else {
return zuul.format.status_label(result);
return this.status_label(result);
}
},
@ -237,7 +178,7 @@
var now = Date.now();
var delta = now - ms;
var status = 'text-success';
var text = zuul.format.time(delta, true);
var text = this.time(delta, true);
if (delta > (4 * hours)) {
status = 'text-danger';
} else if (delta > (2 * hours)) {
@ -338,7 +279,7 @@
.append($change_link);
var $change_progress_row_right = $('<div />')
.addClass('col-xs-9')
.append(zuul.format.change_total_progress_bar(change));
.append(this.change_total_progress_bar(change));
var $change_progress_row = $('<div />')
.addClass('row')
@ -353,9 +294,9 @@
.addClass('col-xs-8')
.append($project_span, $change_progress_row);
var remaining_time = zuul.format.time(
var remaining_time = this.time(
change.remaining_time, true);
var enqueue_time = zuul.format.enqueue_time(
var enqueue_time = this.enqueue_time(
change.enqueue_time);
var $remaining_time = $('<small />').addClass('time')
.attr('title', 'Remaining Time').html(remaining_time);
@ -373,6 +314,7 @@
},
change_list: function(jobs) {
var format = this;
var $list = $('<ul />')
.addClass('list-group zuul-patchset-body');
@ -380,7 +322,7 @@
var $item = $('<li />')
.addClass('list-group-item')
.addClass('zuul-change-job')
.append(zuul.format.job(job));
.append(format.job(job));
$list.append($item);
});
@ -390,7 +332,7 @@
change_panel: function (change) {
var $header = $('<div />')
.addClass('panel-heading zuul-patchset-header')
.append(zuul.format.change_header(change));
.append(this.change_header(change));
var panel_id = change.id ? change.id.replace(',', '_')
: change.project.replace('/', '_') +
@ -399,9 +341,9 @@
.attr('id', panel_id)
.addClass('panel panel-default zuul-change')
.append($header)
.append(zuul.format.change_list(change.jobs));
.append(this.change_list(change.jobs));
$header.click(zuul.toggle_patchset);
$header.click(this.toggle_patchset);
return $panel;
},
@ -458,7 +400,7 @@
if (i === change._tree_index) {
$tree_cell.append(
zuul.format.change_status_icon(change));
this.change_status_icon(change));
}
if (change._tree_branches.indexOf(i) !== -1) {
var $image = $('<img />')
@ -481,7 +423,7 @@
var $change_column = $('<td />')
.css('width', change_width + 'px')
.addClass('zuul-change-cell')
.append(zuul.format.change_panel(change));
.append(this.change_panel(change));
$change_row.append($change_column);
@ -495,10 +437,10 @@
},
pipeline_sparkline: function(pipeline_name) {
if (zuul.graphite_url !== '') {
if (options.graphite_url !== '') {
var $sparkline = $('<img />')
.addClass('pull-right')
.attr('src', zuul.get_sparkline_url(pipeline_name));
.attr('src', get_sparkline_url(pipeline_name));
return $sparkline;
}
return false;
@ -519,7 +461,7 @@
.css('margin-top', '0.5em')
.text(count)
)
.append(zuul.format.pipeline_sparkline(pipeline.name));
.append(this.pipeline_sparkline(pipeline.name));
$header_div.append($heading);
@ -533,11 +475,11 @@
return $header_div;
},
pipeline: function (pipeline) {
var count = zuul.create_tree(pipeline);
pipeline: function (pipeline, count) {
var format = this;
var $html = $('<div />')
.addClass('zuul-pipeline col-md-4')
.append(zuul.format.pipeline_header(pipeline, count));
.append(this.pipeline_header(pipeline, count));
$.each(pipeline.change_queues,
function (queue_i, change_queue) {
@ -562,16 +504,215 @@
$.each(changes, function (change_i, change) {
var $change_box =
zuul.format.change_with_status_tree(
format.change_with_status_tree(
change, change_queue);
$html.append($change_box);
zuul.display_patchset($change_box);
format.display_patchset($change_box);
});
});
});
return $html;
},
toggle_patchset: function(e) {
// Toggle showing/hiding the patchset when the header is
// clicked.
// Grab the patchset panel
var $panel = $(e.target).parents('.zuul-change');
var $body = $panel.children('.zuul-patchset-body');
$body.toggle(200);
var collapsed_index = collapsed_exceptions.indexOf(
$panel.attr('id'));
if (collapsed_index === -1 ) {
// Currently not an exception, add it to list
collapsed_exceptions.push($panel.attr('id'));
}
else {
// Currently an except, remove from exceptions
collapsed_exceptions.splice(collapsed_index, 1);
}
},
display_patchset: function($change_box, animate) {
// Determine if to show or hide the patchset and/or the results
// when loaded
// See if we should hide the body/results
var $panel = $change_box.find('.zuul-change');
var panel_change = $panel.attr('id');
var $body = $panel.children('.zuul-patchset-body');
var expand_by_default = $('#expand_by_default')
.prop('checked');
var collapsed_index = collapsed_exceptions
.indexOf(panel_change);
if (expand_by_default && collapsed_index === -1 ||
!expand_by_default && collapsed_index !== -1) {
// Expand by default, or is an exception
$body.show(animate);
}
else {
$body.hide(animate);
}
// Check if we should hide the whole panel
var panel_project = $panel.find('.change_project').text()
.toLowerCase();
var panel_pipeline = $change_box
.parents('.zuul-pipeline')
.find('.zuul-pipeline-header > h3')
.html()
.toLowerCase();
if (current_filter !== '') {
var show_panel = false;
var filter = current_filter.trim().split(/[\s,]+/);
$.each(filter, function(index, f_val) {
if (f_val !== '') {
f_val = f_val.toLowerCase();
if (panel_project.indexOf(f_val) !== -1 ||
panel_pipeline.indexOf(f_val) !== -1 ||
panel_change.indexOf(f_val) !== -1) {
show_panel = true;
}
}
});
if (show_panel === true) {
$change_box.show(animate);
}
else {
$change_box.hide(animate);
}
}
else {
$change_box.show(animate);
}
},
};
var app = {
schedule: function (app) {
var app = app || this;
if (!options.enabled) {
setTimeout(function() {app.schedule(app);}, 5000);
return;
}
app.update().complete(function () {
setTimeout(function() {app.schedule(app);}, 5000);
});
/* Only update graphs every minute */
if (zuul_graph_update_count > 11) {
zuul_graph_update_count = 0;
zuul.update_sparklines();
}
},
/** @return {jQuery.Promise} */
update: function () {
// Cancel the previous update if it hasn't completed yet.
if (xhr) {
xhr.abort();
}
this.emit('update-start');
var app = this;
var $msg = $(options.msg_id)
xhr = $.getJSON(options.source)
.done(function (data) {
if ('message' in data) {
$msg.removeClass('alert-danger')
.addClass('alert-info')
.text(data.message)
.show();
} else {
$msg.empty()
.hide();
}
if ('zuul_version' in data) {
$('#zuul-version-span').text(data.zuul_version);
}
if ('last_reconfigured' in data) {
var last_reconfigured =
new Date(data.last_reconfigured);
$('#last-reconfigured-span').text(
last_reconfigured.toString());
}
var $pipelines = $(options.pipelines_id);
$pipelines.html('');
$.each(data.pipelines, function (i, pipeline) {
var count = app.create_tree(pipeline);
$pipelines.append(
format.pipeline(pipeline, count));
});
$(options.queue_events_num).text(
data.trigger_event_queue ?
data.trigger_event_queue.length : '0'
);
$(options.queue_results_num).text(
data.result_event_queue ?
data.result_event_queue.length : '0'
);
})
.fail(function (err, jqXHR, errMsg) {
$msg.text(source + ': ' + errMsg).show();
$msg.removeClass('zuul-msg-wrap-off');
})
.complete(function () {
xhr = undefined;
app.emit('update-end');
});
return xhr;
},
update_sparklines: function() {
$.each(zuul_sparkline_urls, function(name, url) {
var newimg = new Image();
var parts = url.split('#');
newimg.src = parts[0] + '#' + new Date().getTime();
$(newimg).load(function (x) {
zuul_sparkline_urls[name] = newimg.src;
});
});
},
emit: function () {
$jq.trigger.apply($jq, arguments);
return this;
},
on: function () {
$jq.on.apply($jq, arguments);
return this;
},
one: function () {
$jq.one.apply($jq, arguments);
return this;
},
control_form: function() {
// Build the filter form filling anything from cookies
var $control_form = $('<form />')
.attr('role', 'form')
.addClass('form-inline')
.submit(this.handle_filter_change);
$control_form
.append(this.filter_form_group())
.append(this.expand_form_group());
return $control_form;
},
filter_form_group: function() {
// Update the filter form with a clear button if required
@ -590,7 +731,7 @@
'separated')
.attr('value', current_filter);
$input.change(zuul.handle_filter_change);
$input.change(this.handle_filter_change);
var $clear_icon = $('<span />')
.addClass('form-control-feedback')
@ -621,7 +762,7 @@
.attr('type', 'checkbox')
.attr('id', 'expand_by_default')
.prop('checked', expand_by_default)
.change(zuul.handle_expand_by_default);
.change(this.handle_expand_by_default);
var $label = $('<label />')
.css('padding-left', '1em')
@ -634,284 +775,103 @@
return $form_group;
},
control_form: function() {
// Build the filter form filling anything from cookies
var $control_form = $('<form />')
.attr('role', 'form')
.addClass('form-inline')
.submit(zuul.handle_filter_change);
$control_form
.append(zuul.format.filter_form_group())
.append(zuul.format.expand_form_group());
return $control_form;
},
},
emit: function () {
$jq.trigger.apply($jq, arguments);
return this;
},
on: function () {
$jq.on.apply($jq, arguments);
return this;
},
one: function () {
$jq.one.apply($jq, arguments);
return this;
},
toggle_patchset: function(e) {
// Toggle showing/hiding the patchset when the header is clicked
// Grab the patchset panel
var $panel = $(e.target).parents('.zuul-change');
var $body = $panel.children('.zuul-patchset-body');
$body.toggle(200);
var collapsed_index = zuul.collapsed_exceptions.indexOf(
$panel.attr('id'));
if (collapsed_index === -1 ) {
// Currently not an exception, add it to list
zuul.collapsed_exceptions.push($panel.attr('id'));
}
else {
// Currently an except, remove from exceptions
zuul.collapsed_exceptions.splice(collapsed_index, 1);
}
},
display_patchset: function($change_box, animate) {
// Determine if to show or hide the patchset and/or the results
// when loaded
// See if we should hide the body/results
var $panel = $change_box.find('.zuul-change');
var panel_change = $panel.attr('id');
var $body = $panel.children('.zuul-patchset-body');
var expand_by_default = $('#expand_by_default').prop('checked');
var collapsed_index = zuul.collapsed_exceptions.indexOf(panel_change);
if (expand_by_default && collapsed_index === -1 ||
!expand_by_default && collapsed_index !== -1) {
// Expand by default, or is an exception
$body.show(animate);
}
else {
$body.hide(animate);
}
// Check if we should hide the whole panel
var panel_project = $panel.find('.change_project').text()
.toLowerCase();
var panel_pipeline = $change_box
.parents('.zuul-pipeline')
.find('.zuul-pipeline-header > h3')
.html()
.toLowerCase();
if (current_filter !== '') {
var show_panel = false;
var filter = current_filter.trim().split(/[\s,]+/);
$.each(filter, function(index, f_val) {
if (f_val !== '') {
f_val = f_val.toLowerCase();
if (panel_project.indexOf(f_val) !== -1 ||
panel_pipeline.indexOf(f_val) !== -1 ||
panel_change.indexOf(f_val) !== -1) {
show_panel = true;
}
}
});
if (show_panel === true) {
$change_box.show(animate);
handle_filter_change: function() {
// Update the filter and save it to a cookie
current_filter = $('#filter_string').val();
set_cookie('zuul_filter_string', current_filter);
if (current_filter === '') {
$('#filter_form_clear_box').hide();
}
else {
$change_box.hide(animate);
$('#filter_form_clear_box').show();
}
}
else {
$change_box.show(animate);
}
},
handle_filter_change: function() {
// Update the filter and save it to a cookie
current_filter = $('#filter_string').val();
set_cookie('zuul_filter_string', current_filter);
if (current_filter === '') {
$('#filter_form_clear_box').hide();
}
else {
$('#filter_form_clear_box').show();
}
$('.zuul-change-box').each(function(index, obj) {
var $change_box = $(obj);
zuul.display_patchset($change_box, 200);
});
return false;
},
handle_expand_by_default: function(e) {
// Handle toggling expand by default
set_cookie('zuul_expand_by_default', e.target.checked);
zuul.collapsed_exceptions = [];
$('.zuul-change-box').each(function(index, obj) {
var $change_box = $(obj);
zuul.display_patchset($change_box, 200);
});
},
create_tree: function(pipeline) {
var count = 0;
var pipeline_max_tree_columns = 1;
$.each(pipeline.change_queues, function(change_queue_i,
change_queue) {
var tree = [];
var max_tree_columns = 1;
var changes = [];
var last_tree_length = 0;
$.each(change_queue.heads, function(head_i, head) {
$.each(head, function(change_i, change) {
changes[change.id] = change;
change._tree_position = change_i;
});
$('.zuul-change-box').each(function(index, obj) {
var $change_box = $(obj);
format.display_patchset($change_box, 200);
});
$.each(change_queue.heads, function(head_i, head) {
$.each(head, function(change_i, change) {
count += 1;
var idx = tree.indexOf(change.id);
if (idx > -1) {
change._tree_index = idx;
// remove...
tree[idx] = null;
while (tree[tree.length - 1] === null) {
tree.pop();
}
} else {
change._tree_index = 0;
}
change._tree_branches = [];
change._tree = [];
if (typeof(change.items_behind) === 'undefined') {
change.items_behind = [];
}
change.items_behind.sort(function(a, b) {
return (changes[b]._tree_position -
changes[a]._tree_position);
});
$.each(change.items_behind, function(i, id) {
tree.push(id);
if (tree.length>last_tree_length &&
last_tree_length > 0) {
change._tree_branches.push(
tree.length - 1);
}
});
if (tree.length > max_tree_columns) {
max_tree_columns = tree.length;
}
if (tree.length > pipeline_max_tree_columns) {
pipeline_max_tree_columns = tree.length;
}
change._tree = tree.slice(0); // make a copy
last_tree_length = tree.length;
});
});
change_queue._tree_columns = max_tree_columns;
});
pipeline._tree_columns = pipeline_max_tree_columns;
return count;
},
get_sparkline_url: function(pipeline_name) {
if (zuul.graphite_url !== '') {
if (!(pipeline_name in zuul_sparkline_urls)) {
zuul_sparkline_urls[pipeline_name] = $.fn.graphite.geturl({
url: zuul.graphite_url,
from: "-8hours",
width: 100,
height: 26,
margin: 0,
hideLegend: true,
hideAxes: true,
hideGrid: true,
target: [
"color(stats.gauges.zuul.pipeline."+pipeline_name+".current_changes, '6b8182')"
]
});
}
return zuul_sparkline_urls[pipeline_name];
}
return false;
},
};
current_filter = read_cookie('zuul_filter_string', current_filter);
$jq = $(zuul);
$jq.on('update-start', function () {
$container.addClass('zuul-container-loading');
$indicator.addClass('zuul-spinner-on');
});
$jq.on('update-end', function () {
$container.removeClass('zuul-container-loading');
setTimeout(function () {
$indicator.removeClass('zuul-spinner-on');
}, 500);
});
$jq.one('update-end', function () {
// Do this asynchronous so that if the first update adds a message, it
// will not animate while we fade in the content. Instead it simply
// appears with the rest of the content.
setTimeout(function () {
// Fade in the content
$container.addClass('zuul-container-ready');
});
});
$(function ($) {
$msg = $('<div />').addClass('alert').hide();
$indicator = $('<button class="btn pull-right zuul-spinner">' +
'updating ' +
'<span class="glyphicon glyphicon-refresh"></span>' +
'</button>');
$queueInfo = $('<p>Queue lengths: <span>0</span> events, ' +
'<span>0</span> results.</p>');
$queueEventsNum = $queueInfo.find('span').eq(0);
$queueResultsNum = $queueEventsNum.next();
var $control_form = zuul.format.control_form();
$pipelines = $('<div class="row"></div>');
var $zuulVersion = $('<p>Zuul version: <span id="zuul-version-span">' +
'</span></p>');
var $lastReconf = $('<p>Last reconfigured: ' +
'<span id="last-reconfigured-span"></span></p>');
$container = $('#zuul-container').append($msg, $indicator,
$queueInfo, $control_form,
$pipelines, $zuulVersion,
$lastReconf);
//zuul.graphite_url = 'http://graphite.openstack.org/render/'
zuul.schedule();
$(document).on({
'show.visibility': function () {
zuul.enabled = true;
zuul.update();
return false;
},
'hide.visibility': function () {
zuul.enabled = false;
}
});
});
handle_expand_by_default: function(e) {
// Handle toggling expand by default
set_cookie('zuul_expand_by_default', e.target.checked);
collapsed_exceptions = [];
$('.zuul-change-box').each(function(index, obj) {
var $change_box = $(obj);
format.display_patchset($change_box, 200);
});
},
create_tree: function(pipeline) {
var count = 0;
var pipeline_max_tree_columns = 1;
$.each(pipeline.change_queues, function(change_queue_i,
change_queue) {
var tree = [];
var max_tree_columns = 1;
var changes = [];
var last_tree_length = 0;
$.each(change_queue.heads, function(head_i, head) {
$.each(head, function(change_i, change) {
changes[change.id] = change;
change._tree_position = change_i;
});
});
$.each(change_queue.heads, function(head_i, head) {
$.each(head, function(change_i, change) {
count += 1;
var idx = tree.indexOf(change.id);
if (idx > -1) {
change._tree_index = idx;
// remove...
tree[idx] = null;
while (tree[tree.length - 1] === null) {
tree.pop();
}
} else {
change._tree_index = 0;
}
change._tree_branches = [];
change._tree = [];
if (typeof(change.items_behind) === 'undefined') {
change.items_behind = [];
}
change.items_behind.sort(function(a, b) {
return (changes[b]._tree_position -
changes[a]._tree_position);
});
$.each(change.items_behind, function(i, id) {
tree.push(id);
if (tree.length>last_tree_length &&
last_tree_length > 0) {
change._tree_branches.push(
tree.length - 1);
}
});
if (tree.length > max_tree_columns) {
max_tree_columns = tree.length;
}
if (tree.length > pipeline_max_tree_columns) {
pipeline_max_tree_columns = tree.length;
}
change._tree = tree.slice(0); // make a copy
last_tree_length = tree.length;
});
});
change_queue._tree_columns = max_tree_columns;
});
pipeline._tree_columns = pipeline_max_tree_columns;
return count;
},
};
$jq = $(app);
return {
options: options,
format: format,
app: app,
jq: $jq
};
}
}(jQuery));

View File

@ -28,11 +28,79 @@ under the License.
<h1>Zuul Status</h1>
<p>Real-time status monitor of Zuul, the pipeline manager between Gerrit and Workers.</p>
<div class="zuul-container" id="zuul-container"></div>
<div class="zuul-container" id="zuul-container">
<div style="display: none;" class="alert" id="zuul_msg"></div>
<button class="btn pull-right zuul-spinner">updating <span class="glyphicon glyphicon-refresh"></span></button>
<p>Queue lengths: <span id="zuul_queue_events_num">0</span> events, <span id="zuul_queue_results_num">0</span> results.</p>
<div id="zuul_controls"></div>
<div id="zuul_pipelines" class="row"></div>
<p>Zuul version: <span id="zuul-version-span"></span></p>
<p>Last reconfigured: <span id="last-reconfigured-span"></span></p>
</div>
</div>
<script src="jquery.min.js"></script>
<script src="jquery-visibility.min.js"></script>
<script src="jquery.graphite.js"></script>
<script src="app.js"></script>
<script>
(function ($) {
// Load immediately
var $container, $indicator;
var demo = location.search.match(/[?&]demo=([^?&]*)/),
source_url = location.search.match(/[?&]source_url=([^?&]*)/),
source = demo ? './status-' + (demo[1] || 'basic') + '.json-sample' :
'status.json';
source = source_url ? source_url[1] : source;
var zuul = $.zuul({
source: source,
//graphite_url: 'http://graphite.openstack.org/render/'
});
zuul.jq.on('update-start', function () {
$container.addClass('zuul-container-loading');
$indicator.addClass('zuul-spinner-on');
});
zuul.jq.on('update-end', function () {
$container.removeClass('zuul-container-loading');
setTimeout(function () {
$indicator.removeClass('zuul-spinner-on');
}, 500);
});
zuul.jq.one('update-end', function () {
// Do this asynchronous so that if the first update adds a
// message, it will not animate while we fade in the content.
// Instead it simply appears with the rest of the content.
setTimeout(function () {
// Fade in the content
$container.addClass('zuul-container-ready');
});
});
$(function ($) {
// DOM ready
$container = $('#zuul-container');
$indicator = $('#zuul-spinner');
$('#zuul_controls').append(zuul.app.control_form());
zuul.app.schedule();
$(document).on({
'show.visibility': function () {
zuul.options.enabled = true;
zuul.app.update();
},
'hide.visibility': function () {
zuul.options.enabled = false;
}
});
});
}(jQuery));
</script>
</body>
</html>