Rewrite clientside-haml-js templates using underscore

Drop haml templates in favor of underscore. Haml templates are harder to maintain and require extra dependencies, we already have underscore.js included and it provides a fast templating engine. Rewrite all client side templates using it.
Performance with underscore is pretty much the same as with haml (with 10k alerts).
This commit is contained in:
Łukasz Mierzwa
2017-04-08 15:14:08 -07:00
parent 854b7acbee
commit f1e90e054d
22 changed files with 545 additions and 2722 deletions

View File

@@ -167,8 +167,6 @@ assets: loaders.css/0.1.2/loaders.min.css
assets: js-cookie/2.1.3/js.cookie.min.js
# underscore & haml, for template rendering in js
assets: underscore.js/1.8.3/underscore-min.js
assets: underscore.string/2.4.0/underscore.string.min.js
assets: clientside-haml-js/5.4/haml.js
# masonry, for grid layout
assets: masonry/4.1.1/masonry.pkgd.min.js
# copy to clipboard

View File

@@ -11,7 +11,7 @@ var Alerts = (function() {
}
Render() {
return haml.compileHaml('groups')({
return Templates.Render('alertGroup', {
group: this,
silences: silences,
static_color_label: Colors.GetStaticLabels(),

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -9,8 +9,6 @@
0.1.2-loaders.css.min.js
2.1.3-js.cookie.min.js
1.8.3-underscore-min.js
2.4.0-underscore.string.min.js
5.4-haml.js
4.1.1-masonry.pkgd.min.js
1.5.16-clipboard.min.js
3.9.1-raven.min.js

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
var Summary = (function() {
var summary;
var summary, templates;
render = function() {
var top_tags = [];
@@ -28,9 +28,7 @@ var Summary = (function() {
tags.push(tag);
});
return haml.compileHaml('breakdown-content')({
tags: tags
});
return Templates.Render('breakdownContent', {tags: tags});
}
@@ -43,7 +41,7 @@ var Summary = (function() {
placement: 'bottom',
title: 'Top labels',
content: render,
template: haml.compileHaml('breakdown')()
template: Templates.Render('breakdown', {})
});
}

View File

@@ -0,0 +1,67 @@
var Templates = (function(params) {
var templates = {},
config = {
// popover with the list of most common labels
breakdown: '#breakdown',
breakdownContent: '#breakdown-content',
// reload message if backend version bump is detected
reloadNeeded: '#reload-needed',
// errors
fatalError: '#fatal-error',
internalError: '#internal-error',
updateError: '#update-error',
// modal popup with label filters
modalTitle: '#modal-title',
modalBody: '#modal-body',
// label button
buttonLabel: '#label-button-filter',
// alert group
alertGroup: '#alert-group',
alertGroupTitle: '#alert-group-title',
alertGroupAnnotations: '#alert-group-annotations',
alertGroupLabels: '#alert-group-labels',
alertGroupElements: '#alert-group-elements',
alertGroupSilence: '#alert-group-silence',
alertGroupLabelMap: '#alert-group-label-map'
}
init = function() {
$.each(config, function(name, selector) {
try {
templates[name] = _.template($(selector).html());
} catch (err) {
console.error('Failed to parse template ' + name + ' ' + selector);
console.error(err);
}
});
}
renderTemplate = function(name, context) {
var t = templates[name];
if (t == undefined) {
console.error('Unknown template ' + name);
return '<div class="jumbotron"><h1>Internal error: unknown template ' + name + '</h1></div>';
}
try {
return t(context);
} catch (err) {
return '<div class="jumbotron">Failed to render template "' + name + '"<h1><p>' + err + '</p></h1></div>'
}
}
return {
Init: init,
Render: renderTemplate
}
})();

View File

@@ -13,15 +13,15 @@ var UI = (function(params) {
var attrs = Alerts.GetLabelAttrs(label_key, label_val);
var counter = Summary.Get(label_key, label_val);
modal.find('.modal-title').html(
haml.compileHaml('modal-title')({
attrs: attrs,
counter: counter
Templates.Render('modalTitle', {
attrs: attrs,
counter: counter
})
);
var hints = Autocomplete.GenerateHints(label_key, label_val);
modal.find('.modal-body').html(haml.compileHaml('modal-body')({
hints: hints
}));
modal.find('.modal-body').html(
Templates.Render('modalBody', {hints: hints})
);
modal.on('click', '.modal-button-filter', function(elem) {
var filter = $(elem.target).data('filter-append-value');
$('#labelModal').modal('hide');

View File

@@ -70,7 +70,7 @@ var Unsee = (function(params) {
Counter.Error()
Grid.Clear();
Grid.Hide();
$(selectors.errors).html(haml.compileHaml(template)(context));
$(selectors.errors).html(Templates.Render(template, context));
$(selectors.errors).show();
Counter.Unknown();
Summary.Update({});
@@ -84,7 +84,7 @@ var Unsee = (function(params) {
if (window.console) {
console.error(err.stack);
}
renderError('internal-error', {
renderError('internalError', {
name: err.name,
message: err.message,
raw: err
@@ -96,7 +96,7 @@ var Unsee = (function(params) {
upgrade = function() {
renderError('reload-needed', {});
renderError('reloadNeeded', {});
setTimeout(function() {
location.reload();
}, 3000);
@@ -113,7 +113,7 @@ var Unsee = (function(params) {
upgrade();
} else if (resp['error']) {
Counter.Unknown();
renderError('update-error', {
renderError('updateError', {
error: 'Backend error',
message: resp['error'],
last_ts: Watchdog.GetLastUpdate()
@@ -149,7 +149,7 @@ var Unsee = (function(params) {
// if fatal error was already triggered we have error message
// so don't add new one
if (!Watchdog.IsFatal()) {
renderError('update-error', {
renderError('updateError', {
error: 'Backend error',
message: 'AJAX request failed',
last_ts: Watchdog.GetLastUpdate()
@@ -238,6 +238,7 @@ $(document).ready(function() {
trigger: 'hover'
});
Templates.Init();
UI.Init();
Unsee.Init();

View File

@@ -23,7 +23,7 @@ var Watchdog = (function() {
var now = moment().utc().unix();
if (now - lastTs > maxLag) {
$('#errors').html(haml.compileHaml('fatal-error')({
$('#errors').html(Templates.Render('fatalError', {
last_ts: lastTs,
seconds_left: fatalCountdown
})).show();

View File

@@ -0,0 +1,244 @@
<script type="application/json" id="alert-group-title">
<% if (Object.keys(group.labels).length > 0) { %>
<% var filters = [] %>
<% _.each(group.labels, function(label_val, label_key) { filters.push(label_key + '=' + label_val) }) %>
<% var groupLink = '?q=' + filters.join(',') %>
<span class="pull-left alert-group-link">
<a href="<%= groupLink %>" title="Link to this alert group", data-toggle="tooltip" data-placement="top">
<i class="fa fa-link"/>
</a>
</span>
<span class="badge pull-right">
<%- group.alerts.length %>
</span>
<div class="panel-title">
<% if (Object.keys(group.labels).length > 0) { %>
<% _.each(Alerts.SortMapByKey(group.labels), function(label) { %>
<% var attrs = Alerts.GetLabelAttrs(label.key, label.value) %>
<%= Templates.Render('buttonLabel', {elem: 'div', elemClass: 'label label-list', attrs: attrs, label: label}) %>
<% }) %>
<% } else { %>
<div class="label-list label"></div>
<% } %>
</div>
<% } %>
</script>
<script type="application/json" id="alert-group-annotations">
<% _.each(Alerts.SortMapByKey(alert.annotations), function(annotation) { %>
<div class="well well-sm annotation-well">
<i class="fa fa-question-circle text-muted" title="<%= annotation.key %>" data-toggle="tooltip" data-placement="top"/>
<% if (annotation.value) { %>
<%- annotation.value %>
<% } else { %>
<span class="text-muted">
[ missing annotation value ]
</span>
<% } %>
</div>
<% }) %>
</script>
<script type="application/json" id="alert-group-labels">
<% _.each(Alerts.SortMapByKey(alert.labels), function(label) { %>
<% if (group.labels[label.key] == undefined) { %>
<% var attrs = Alerts.GetLabelAttrs(label.key, label.value) %>
<%= Templates.Render('buttonLabel', {elem: 'span', attrs: attrs, label: label}) %>
<% } %>
<% }) %>
</script>
<script type="application/json" id="alert-group-elements">
<% var cls_indicator = 'incident-indicator-danger' %>
<% if (alert.silenced) { cls_indicator = 'incident-indicator-success' } %>
<div>
<% if (alert.generatorURL) { %>
<a class="label label-list label-default"
href="<%= alert.generatorURL %>"
target="_blank"
title="Go to the alert source"
data-toggle="tooltip"
data-placement="top">
<i class="fa fa-external-link"/>
source
</a>
<% } %>
<% _.each(alert.links, function(url, text) { %>
<a class="label label-list label-default"
href="<%= url %>"
target="_blank"
title="<%= url %>"
data-toggle="tooltip"
data-placement="top">
<i class="fa fa-external-link"/>
<%- text %>
</a>
<% }) %>
<% if (alert.silenced) { %>
<%= Templates.Render('buttonLabel', {elem: 'span', elemClass: 'label label-list label-success', label: {key: '@silenced', value: 'true', text: '@silenced: true'}}) %>
<% } else { %>
<%= Templates.Render('buttonLabel', {elem: 'span', elemClass: 'label label-list label-danger', label: {key: '@silenced', value: 'false', text: '@silenced: false'}}) %>
<% } %>
<a class="label label-list label-default label-age label-ts"
data-toggle="tooltip"
data-placement="top"
data-ts="<%= alert.startsAt %>">
<span class="label-ts-span">
<%- alert.startsAt %>
</span>
<div class="incident-indicator hidden <%= cls_indicator %>">
</div>
</a>
</div>
</script>
<script type="application/json" id="alert-group-silence">
<div>
<% var silence = silences[alert.silenced] %>
<% if (silence) { %>
<blockquote class="silence-comment">
<% if (silence.jiraURL) { %>
<a href="<%= silence.jiraURL %>" target="_blank">
<i class="fa fa-external-link"/>
<%- silence.comment %>
</a>
<% } else { %>
<%- silence.comment %>
<% } %>
<footer>
<cite>
<abbr class="label-ts" data-toggle="tooltip" data-placement="top" data-ts="<%= silence.startsAt %>">
<%- silence.createdBy %>
</abbr>
</cite>
</footer>
</blockquote>
<% } %>
</div>
</script>
<script type="application/json" id="alert-group-label-map">
<% var labelArr = [] %>
<% _.each(labelMap, function(label, text) { %>
<% labelArr.push({text: text, key: label.key, value: label.value, hits: label.hits}) %>
<% }) %>
<% labelArr.sort(function(a, b) { %>
<% if (a.hits < b.hits) return 1 %>
<% if (a.hits > b.hits) return -1 %>
<% if (a.text > b.text) return 1 %>
<% if (a.text < b.text) return -1 %>
<% return 0 %>
<% }) %>
<div class="panel-body incident-group-separator">
<div class="incident-group">
<% var skippedLabel = '+' + skipped %>
<% if (skipped == 1) { %>
<% skippedLabel += " alert" %>
<% } else { %>
<% skippedLabel += " alerts" %>
<% } %>
<div class="incident-group">
<span class="badge">
<%- skippedLabel %>
</span>
<% var rendered = 0 %>
<!--
can't use underscore each() here as it doesn't support breaking the loop
with 'return false', use jquery method
-->
<% $.each(labelArr, function(i, label) { %>
<% if (rendered > 8 && labelArr.length > 10) { %>
<span class="label label-list label-default">
<% var skippedCount = labelArr.length - rendered %>
<% if (skippedCount == 1) { %>
+<%- skippedCount %> label
<% } else { %>
+<%- skippedCount %> labels
<% } %>
</span>
<% return false %>
<% } else { %>
<% rendered++ %>
<% var attrs = Alerts.GetLabelAttrs(label.key, label.value) %>
<% if (label.hits > 1) { %>
<div class="label-trim-group">
<%= Templates.Render('buttonLabel', {elem: 'span', elemClass: 'label label-trim-tag', attrs: attrs, label: label}) %>
<span class="label label-default label-trim-count">
<%- label.hits %>
</span>
</div>
<% } else { %>
<%= Templates.Render('buttonLabel', {elem: 'span', attrs: attrs, label: label}) %>
<% } %>
<% } %>
<% }) %>
</div>
</div>
</div>
</script>
<script type="application/json" id="alert-group">
<div class="incident" id="<%= group.id %>" data-hash="<%= group.hash %>">
<% var cls_panel = 'panel-success' %>
<% if (group.unsilencedCount > 0) { cls_panel = 'panel-danger' } %>
<div class="panel <%= cls_panel %>">
<div class="panel-heading text-center">
<%= Templates.Render('alertGroupTitle', {group: group}) %>
</div>
<% var labelMap = {} %>
<% var skipped = 0 %>
<% _.each(group.alerts, function(alert, i) { %>
<% if (i > alert_limit - 1) { %>
<% skipped++ %>
<% _.each(alert.labels, function(label_val, label_key) { %>
<% var text = label_key + ': ' + label_val %>
<% if (group.labels[label_key] == undefined) { %>
<% if (labelMap[text] == undefined) { labelMap[text] = {key: label_key, value: label_val, hits: 0} } %>
<% labelMap[text].hits++ %>
<% } %>
<% }) %>
<% var silencedText = '@silenced: false' %>
<% var isSilenced = 'false' %>
<% if (alert.silenced) { %>
<% silencedText = '@silenced: true' %>
<% isSilenced = 'true' %>
<% } %>
<% if (labelMap[silencedText] == undefined) { labelMap[silencedText] = {key: '@silenced', value: isSilenced, hits: 0} } %>
<% labelMap[silencedText].hits++ %>
<% } else { %>
<% var cls_body = '' %>
<% if (i < group.alerts.length - 1) { cls_body = 'incident-group-separator' } %>
<div class="panel-body <%= cls_body %>">
<div class="incident-group">
<%= Templates.Render('alertGroupAnnotations', {alert: alert}) %>
<%= Templates.Render('alertGroupLabels', {alert: alert, group: group}) %>
<%= Templates.Render('alertGroupElements', {alert: alert}) %>
<% if (alert.silenced) { %>
<%= Templates.Render('alertGroupSilence', {alert: alert, silences: silences}) %>
<% } %>
</div>
</div>
<% } %>
<% }) %>
<% if (!$.isEmptyObject(labelMap)) { %>
<%= Templates.Render('alertGroupLabelMap', {labelMap: labelMap, skipped: skipped}) %>
<% } %>
</div>
</div>
</script>
<script type="application/json" id="label-button-filter">
<% if (elemClass == undefined) { var elemClass = '' } %>
<% if (attrs == undefined) { var attrs = {class: '', style: ''} } %>
<<%= elem %> class="<%= elemClass %> <%= attrs.class %>"
style="<%= attrs.style %>"
type="button"
data-label-type="filter"
data-label-key="<%= label.key %>"
data-label-val="<%= label.value %>"
data-toggle="modal"
data-target="#labelModal">
<%- label.text %>
</<%= elem %>>
</script>

View File

@@ -1,58 +0,0 @@
<script type="application/json" id="reload-needed">
%div.jumbotron
%h1.text-center
New version detected, reloading ...
%i.fa.fa-refresh.fa-spin
</script>
<script type="application/json" id="update-error">
%div.jumbotron
%h1.text-center
=error
%i.fa.fa-exclamation-circle.text-danger
-if (message) {
%div.text-center
%p
=message
-if (last_ts > 0) {
%div.text-center
%p
Last successful update was
=moment.unix(last_ts).fromNow()
</script>
<script type="application/json" id="fatal-error">
%div.jumbotron
%h1.text-center
Critical error
%i.fa.fa-exclamation-circle.text-danger
%div.text-center
%p
-var msg = 'Auto refresh is enabled but last update was ' + moment.unix(last_ts).fromNow();
=msg
%p
This page will auto reload in
%span#reload-counter
=seconds_left
seconds
</script>
<script type="application/json" id="internal-error">
%div.jumbotron
%h1.text-center
Internal error
%i.fa.fa-exclamation-circle.text-danger
%div.text-center
%p
-if (name) {
=name
-if (message) {
=message
-if (!name && !message) {
-var msg = raw.split('(');
-if (msg.length > 0) {
=msg[0]
</script>

View File

@@ -0,0 +1,78 @@
<script type="application/json" id="reload-needed">
<div class="jumbotron">
<h1 class="text-center">
New version detected, reloading ...
<i class="fa fa-refresh fa-spin"/>
</h1>
</div>
</script>
<script type="application/json" id="update-error">
<div class="jumbotron">
<h1 class="text-center">
<%- error %>
<% if (message) { %>
<div class="text-center">
<p>
<%- message %>
</p>
</div>
<% } %>
<% if (last_ts > 0) { %>
<div class="text-center">
<p>
Last successful update was <%= moment.unix(last_ts).fromNow() %>
</p>
</div>
<% } %>
</h1>
</div>
</script>
<script type="application/json" id="fatal-error">
<div class="jumbotron">
<h1 class="text-center">
Critical error
<i class="fa fa-exclamation-circle text-danger"/>
</h1>
<div class="text-center">
<p>
Auto refresh is enabled but last update was <%= moment.unix(last_ts).fromNow() %>
</p>
<p>
This page will auto reload in
<span id='reload-counter'>
<%- seconds_left %> seconds
</span>
</p>
</div>
</div>
</script>
<script type="application/json" id="internal-error">
<div class="jumbotron">
<h1 class="text-center">
Internal error
<i class="fa fa-exclamation-circle text-danger"/>
</h1>
<div class="text-center">
<p>
<% if (name) { %>
<%- name %>
<% } %>
<% if (message) { %>
<%- message %>
<% } %>
<% if (!name && !message) { %>
<% var msg = raw.split('(') %>
<% if (msg.length > 0) { %>
<%- msg[0] %>
<% } %>
<% } %>
</p>
</div>
</div>
</script>

View File

@@ -1,164 +0,0 @@
<script type="application/json" id="groups">
%div.incident{id: group.id, 'data-hash': group.hash}
-var cls_panel = 'panel-success';
-if (group.unsilencedCount > 0) {
-cls_panel = 'panel-danger';
%div.panel{class: cls_panel}
%div.panel-heading.text-center
-if (Object.keys(group.labels).length > 0) {
-var filters = [];
-$.each(group.labels, function(label_key, label_val) {
-filters.push(label_key + '=' + label_val);
-var groupLink = '?q=' + filters.join(',');
%span.pull-left.alert-group-link
%a{href: groupLink, title: 'Link to this alert group', 'data-toggle': 'tooltip', 'data-placement': 'top'}
%i.fa.fa-link
%span.badge.pull-right
=group.alerts.length
%div.panel-title
-if (Object.keys(group.labels).length > 0) {
-$.each(Alerts.SortMapByKey(group.labels), function(i, label) {
-var attrs = Alerts.GetLabelAttrs(label.key, label.value);
%div.label-list.label{class: attrs.class, style: attrs.style, 'data-label-type': "filter", 'data-label-key': label.key, 'data-label-val': label.value, type: 'button', 'data-toggle': 'modal', 'data-target': '#labelModal'}
=label.text
-else {
%div.label-list.label
-var labelMap = {};
-var skipped = 0;
-$.each(group.alerts, function(i, alert) {
-if (i > alert_limit - 1) {
-skipped++;
-$.each(alert.labels, function(label_key, label_val) {
-var text = label_key + ': ' + label_val;
-if (group.labels[label_key] == undefined) {
-if (labelMap[text] == undefined) {
-labelMap[text] = {key: label_key, value: label_val, hits: 0};
-labelMap[text].hits++;
-var silencedText = '@silenced: false';
-var isSilenced = 'false';
-if (alert.silenced) {
-silencedText = '@silenced: true';
-isSilenced = 'true';
-if (labelMap[silencedText] == undefined) {
-labelMap[silencedText] = {key: '@silenced', value: isSilenced, hits: 0};
-labelMap[silencedText].hits++;
-else {
-var cls_body = '';
-var cls_age = '';
-if (i < group.alerts.length - 1) {
-cls_body = 'incident-group-separator';
%div.panel-body{class: cls_body}
%div.incident-group
-var now = moment();
-$.each(Alerts.SortMapByKey(alert.annotations), function(i, annotation) {
%div.well.well-sm.annotation-well
%i.fa.fa-question-circle.text-muted{title: annotation.key, 'data-toggle': 'tooltip', 'data-placement': 'top'}
-if (annotation.value == '') {
%span.text-muted
[ missing annotation value ]
-else {
=annotation.value
-$.each(Alerts.SortMapByKey(alert.labels), function(i, label) {
-if (group.labels[label.key] == undefined) {
-var attrs = Alerts.GetLabelAttrs(label.key, label.value);
%span{class: attrs.class, style: attrs.style, 'data-label-type': "filter", 'data-label-key': label.key, 'data-label-val': label.value, type: 'button', 'data-toggle': 'modal', 'data-target': '#labelModal'}
=label.text
-var cls_age = '';
-var cls_body = '';
-var cls_indicator = 'incident-indicator-danger';
-if (alert.silenced) {
-cls_indicator = 'incident-indicator-success';
%div
-if (alert.generatorURL) {
%a.label.label-list.label-default{href: alert.generatorURL, target: '_blank', title: "Go to the alert source", 'data-toggle': 'tooltip', 'data-placement': 'top'}
%i.fa.fa-external-link
source
-$.each(alert.links, function(k, url) {
%a.label.label-list.label-default{href: url, target: '_blank', title: url, 'data-toggle': 'tooltip', 'data-placement': 'top'}
%i.fa.fa-external-link
=k
-if (alert.silenced) {
%span.label.label-list.label-success{'data-label-type': "filter", 'data-label-key': "@silenced", 'data-label-val': "true", type: 'button', 'data-toggle': 'modal', 'data-target': '#labelModal'}
@silenced: true
-else {
%span.label.label-list.label-danger{'data-label-type': "filter", 'data-label-key': "@silenced", 'data-label-val': "false", type: 'button', 'data-toggle': 'modal', 'data-target': '#labelModal'}
@silenced: false
%a.label.label-list.label-default.label-age.label-ts{'data-toggle': 'tooltip', 'data-placement': 'top', 'data-ts': alert.startsAt}
%span.label-ts-span
=alert.startsAt
%div.incident-indicator.hidden{class: cls_indicator}
-if (alert.silenced) {
%div
-var silence = silences[alert.silenced];
-if(silence) {
%blockquote.silence-comment
-if (silence.jiraURL) {
%a{target: '_blank', href: silence.jiraURL}
%i.fa.fa-external-link
=silence.comment
-else {
=silence.comment
%footer
%cite
%abbr{'data-toggle': 'tooltip', 'data-placement': 'bottom', 'data-ts': silence.startsAt}
=silence.createdBy
-if (!$.isEmptyObject(labelMap)) {
-labelArr = [];
-$.each(labelMap, function(text, label){
-labelArr.push({text: text, key: label.key, value: label.value, hits: label.hits});
-labelArr.sort(function(a, b) {
-if (a.hits < b.hits) return 1;
-if (a.hits > b.hits) return -1;
-if (a.text > b.text) return 1;
-if (a.text < b.text) return -1;
-return 0;
%div.panel-body.incident-group-separator
%div.incident-group
-var skippedLabel = '+' + skipped;
-if (skipped == 1) {
-skippedLabel += " alert";
-else {
-skippedLabel += " alerts";
%span.badge
=skippedLabel
-var rendered = 0;
-$.each(labelArr, function(i, label) {
-if (rendered > 8 && labelArr.length > 10) {
%span.label.label-list.label-default
-var skippedCount = labelArr.length - rendered;
-var text = "+" + skippedCount;
-if (skippedCount == 1) {
-text += ' label';
-else {
-text += ' labels';
=text
-return false;
-} else {
-rendered++;
-var attrs = Alerts.GetLabelAttrs(label.key, label.value);
-if (label.hits > 1) {
%div.label-trim-group
%span.label.label-trim-tag{class: attrs.class, style: attrs.style, 'data-label-type': "filter", 'data-label-key': label.key, 'data-label-val': label.value, type: 'button', 'data-toggle': 'modal', 'data-target': '#labelModal'}
=label.text
%span.label.label-default.label-trim-count
=label.hits
-else {
%span{class: attrs.class, style: attrs.style, 'data-label-type': "filter", 'data-label-key': label.key, 'data-label-val': label.value, type: 'button', 'data-toggle': 'modal', 'data-target': '#labelModal'}
=label.text
</script>

View File

@@ -163,7 +163,7 @@
</body>
</html>
{{ template "templates/groups.haml" }}
{{ template "templates/summary.haml" }}
{{ template "templates/errors.haml" }}
{{ template "templates/modal.haml" }}
{{ template "templates/alertgroup.html" }}
{{ template "templates/summary.html" }}
{{ template "templates/errors.html" }}
{{ template "templates/modal.html" }}

View File

@@ -10,6 +10,7 @@
<script type="text/javascript" src="{{ .WebPrefix }}static/grid.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/progress.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/summary.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/templates.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/watchdog.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/querystring.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/ui.js?_={{ .NowQ }}"></script>

View File

@@ -1,22 +0,0 @@
<script type="application/json" id="modal-title">
%button.close{type: "button", "data-dismiss": "modal"}
%i.fa.fa-close
%div.label-list.label{class: attrs.class, style: attrs.style}
=attrs.text
%span.badge.badge-primary
=counter
</script>
<script type="application/json" id="modal-body">
%table.table.table-striped
%caption.text-center
Quick filters
%tbody
-$.each(hints, function(i, hint){
%tr
%td.modal-row-filters
=hint
%td.modal-row-actions
%button.btn.btn-sm.btn-primary.pull-right.modal-button-filter{type: "button", 'data-filter-append-value': hint}
Apply
</script>

View File

@@ -0,0 +1,33 @@
<script type="application/json" id="modal-title">
<button class="close" type="button" data-dismiss="modal">
<i class="fa fa-close"/>
</button>
<div class="label-list label <%= attrs.class %>" style="<%= attrs.style %>">
<%- attrs.text %>
</div>
<span class="badge badge-primary">
<%- counter %>
</span>
</script>
<script type="application/json" id="modal-body">
<table class="table table-striped">
<caption class="text-center">
Quick filters
</caption>
<tbody>
<% _.each(hints, function(hint) { %>
<tr>
<td class="modal-row-filters">
<%- hint %>
</td>
<td class="modal-row-actions">
<button class="btn btn-sm btn-primary pull-right modal-button-filter" type="button" data-filter-append-value="<%= hint %>">
Apply
</button>
</td>
</tr>
<% }) %>
</tbody>
</table>
</script>

View File

@@ -1,22 +0,0 @@
<script type="application/json" id="breakdown">
%div.popover
%div.arrow
%h1.popover-title.text-center
%div.popover-content{id: 'breakdown-content'}
</script>
<script type="application/json" id="breakdown-content">
-if (tags.length > 0) {
%table.table
-$.each(tags, function(i, tag) {
%tr
%td
%span.label{class: tag.cls, style: tag.style}
=tag.name
%td{align: 'center'}
%span.breakdown-badge
=tag.val
-else {
%p.text-muted
Empty
</script>

View File

@@ -0,0 +1,30 @@
<script type="application/json" id="breakdown">
<div class="popover">
<div class="arrow"></div>
<h1 class="popover-title text-center"></h1>
<div class="popover-content" id="breakdown-content"></div>
</div>
</script>
<script type="application/json" id="breakdown-content">
<% if(tags.length > 0) { %>
<table class="table">
<% _.each(tags, function(tag) { %>
<tr>
<td>
<span class="label <%= tag.cls %>" style="<%= tag.style %>">
<%- tag.name %>
</span>
</td>
<td align="center">
<span class="breakdown-badge">
<%- tag.val %>
</span>
</td>
</tr>
<% }) %>
</table>
<% } else { %>
<p class="text-muted">Empty</p>
<% } %>
</script>

File diff suppressed because one or more lines are too long