Files
karma/assets/static/filters.js
Łukasz Mierzwa d92e08f3f6 Don't reload filters until entire applyFilterList() block was completed
setFilters() will trigger fetch of the new alerts with updated filter, it's being called after each filter is added to the filter bar, so each added filter ends up with AJAX call to /alerts.json, this means that when someone clicks on a history entry with complex rule it will trigger multiple subsequent requests. What's worse first request will have bigger response body (since it filters less) so it's likely that it will finish after the next request, leaving invalid alerts on the grid. This disable setFilters() if we apply filters in batch mode.
2017-08-14 20:10:44 -07:00

243 lines
7.5 KiB
JavaScript

"use strict";
const $ = window.$ = window.jQuery = require("jquery");
const sha1 = require("js-sha1");
const Cookies = require("js-cookie");
require("./jquery.typing-0.3.2.js");
require("corejs-typeahead");
require("bootstrap-tagsinput");
require("bootstrap-tagsinput/dist/bootstrap-tagsinput.css");
require("./bootstrap-tagsinput.less");
const autocomplete = require("./autocomplete");
const unsee = require("./unsee");
const querystring = require("./querystring");
const templates = require("./templates");
var selectors = {
filter: "#filter",
icon: "#filter-icon",
historyMenu: "#historyMenu"
};
var appendsEnabled = true;
var historyStorage;
const historyKey = "filterHistory";
function addBadge(text) {
$.each($("span.tag"), function(i, tag) {
if ($(tag).text() == text) {
var chksum = sha1(text);
$(tag).prepend("<span class='badge tag-badge' id='tag-counter-" + chksum + "' data-badge-id='" + chksum + "'></span>");
}
});
}
function getFilters() {
return $(selectors.filter).tagsinput("items");
}
function reloadBadges(filterData) {
$.each(filterData, function(i, filter) {
$.each($("span.tag-badge"), function(j, tag) {
if (sha1(filter.text) == $(tag).data("badge-id")) {
$(tag).html(filter.hits.toString());
if (filter.isValid === true) {
$(tag).parent().addClass("label-info").removeClass("label-danger");
} else {
$(tag).parent().addClass("label-danger").removeClass("label-info");
}
}
});
});
}
function addFilter(text) {
$(selectors.filter).tagsinput("add", text);
}
function clearFilters() {
$(selectors.filter).tagsinput("removeAll");
}
function setUpdating() {
// visual hint that alerts are reloaded due to filter change
$(selectors.icon).removeClass("fa-search fa-pause").addClass("fa-circle-o-notch fa-spin");
}
function updateDone() {
$(selectors.icon).removeClass("fa-circle-o-notch fa-spin fa-pause").addClass("fa-search");
}
function setPause() {
$(selectors.icon).removeClass("fa-circle-o-notch fa-spin fa-search").addClass("fa-pause");
}
function renderHistory() {
var historicFilters = [];
const currentFilterText = getFilters().join(",");
const history = historyStorage.getItem(historyKey);
if (history) {
historicFilters = history.split("\n");
}
var historyMenuHTML = templates.renderTemplate("historyMenu", {
activeFilter: currentFilterText,
defaultFilter: $(selectors.filter).data("default-filter"),
savedFilter: Cookies.get("defaultFilter.v2"),
filters: historicFilters
});
$(selectors.historyMenu).html(historyMenuHTML);
}
function appendFilterToHistory(text) {
// require non empty text and enabled appends
if (!text || !appendsEnabled) return false;
// final filter list we'll save to storage
var filterList = new Set([ text ]);
// get current history list from storage and append it to our final list
// of filters, but avoid duplicates
const history = historyStorage.getItem(historyKey);
if (history) {
const historyArr = history.split("\n");
for (var i = 0; i < historyArr.length; i++) {
filterList.add(historyArr[i]);
}
}
// truncate the history to up to 11 elements
const filterListTrunc = Array.from(filterList).slice(0, 10);
historyStorage.setItem(historyKey, filterListTrunc.join("\n"));
}
function setFilters() {
setUpdating();
// update location so it's easy to share it
querystring.update("q", getFilters().join(","));
// append filter to the history and render it
appendFilterToHistory(getFilters().join(","));
renderHistory();
// reload alerts
unsee.triggerReload();
}
function applyFilterList(filterList) {
// we need to add filters one by one, this would reload alerts on every
// add() so let's pause reloads and resume once we're done with updating
// filters
unsee.pause();
// disable history appends as it would record each new filter in the
// history
appendsEnabled = false;
$(selectors.filter).tagsinput("removeAll");
for (var i = 0; i < filterList.length; i++) {
$(selectors.filter).tagsinput("add", filterList[i]);
}
// enable everything again
appendsEnabled = true;
setFilters();
unsee.resume();
}
function init(historyStore) {
historyStorage = historyStore;
var initialFilter;
if ($(selectors.filter).data("default-used") == "false" || $(selectors.filter).data("default-used") === false) {
// user passed ?q=filter string
initialFilter = $(selectors.filter).val();
} else {
// no ?q=filter string, check if we have default filter cookie
initialFilter = Cookies.get("defaultFilter.v2");
if (initialFilter === undefined) {
// no cookie, use global default
initialFilter = $(selectors.filter).data("default-filter");
}
}
var initialFilterArr = initialFilter.split(",");
$(selectors.filter).val("");
$(".filterbar :input").tagsinput({
typeaheadjs: {
minLength: 1,
hint: true,
limit: 12,
name: "autocomplete",
source: autocomplete.getAutocomplete()
}
});
$.each(initialFilterArr, function(i, filter) {
$(selectors.filter).tagsinput("add", filter);
addBadge(filter);
});
$(selectors.filter).on("itemAdded itemRemoved", function(event) {
if (appendsEnabled) {
// if history appends are disabled then don't set filters yet
// we disable appends to have batch filter updates so we shouldn't
// set filters yet
setFilters();
}
// add counter badge to new tag
if (event.type == "itemAdded") {
addBadge(event.item);
}
});
$(selectors.filter).tagsinput("focus");
// stop when user is typing in the filter bar
$(".bootstrap-tagsinput").typing({
start: function(event) {
// ignore backspace & enter
if (event.keyCode != 8 && event.keyCode != 13) unsee.pause();
},
stop: function(event) {
// ignore enter
if (event.keyCode != 13) unsee.resume();
},
delay: 1000
});
// fix body padding if needed, input might endup using more than 1 line
$(".bootstrap-tagsinput").bind("DOMSubtreeModified", function() {
$("body").css("padding-top", $(".navbar").height());
});
$("input.tt-input").on("change keypress", function() {
$("body").css("padding-top", $(".navbar").height());
});
$(".filterbar").on("resize", function(){
// hack for fixing padding since input can grow and change height
$("body").css("padding-top", $(".navbar").height());
});
renderHistory();
$(selectors.historyMenu).on("click", "a.history-menu-item", function(event) {
var elem = $(event.target).parents("li.history-menu");
const filtersList = elem.find(".rawFilter").text().trim().split(",");
applyFilterList(filtersList);
});
}
exports.init = init;
exports.addFilter = addFilter;
exports.clearFilters = clearFilters;
exports.setFilters = setFilters;
exports.getFilters = getFilters;
exports.addBadge = addBadge;
exports.reloadBadges = reloadBadges;
exports.updateDone = updateDone;
exports.setUpdating = setUpdating;
exports.setPause = setPause;
exports.renderHistory = renderHistory;