maltrail/html/js/main.js
Miroslav Stampar 33b52532fe Minor patch
2026-01-06 18:38:58 +01:00

3055 lines
117 KiB
JavaScript

/*
* Copyright (c) 2014-2026 Maltrail developers (https://github.com/stamparm/maltrail/)
* See the file 'LICENSE' for copying permission
*/
// globals part
var _THREATS = {};
var _SOURCES = {};
var _TOP_SOURCES = [];
var _SOURCE_EVENTS = {};
var _SEVERITY_COUNT = {};
var _TRAILS = {};
var _FLOOD_TRAILS = {};
var _HOURS = {};
var _DATASET = [];
var _TRAILS_SORTED = null;
var _DELETE_DELETE_PRESS = false;
var _MAX_EVENTS_PER_HOUR = 0;
var _MAX_SPARKLINE_PER_HOUR = 0;
var _TOTAL_EVENTS = 0;
var _USER = null;
var IP_COUNTRY = {};
var CHECK_IP = {};
var TRAIL_TYPES = {};
var CONTEXT_MENU_ROW = null;
var SPARKLINE_WIDTH = 100;
var CHART_WIDTH = screen.width - 200;
var CHART_HEIGHT = screen.height - 340;
var PIE_FONT_SIZE = 10;
var MAX_SOURCES_ITEMS = 30;
var FLOOD_TRAIL_THRESHOLD = 50;
var LONG_TRAIL_THRESHOLD = 40;
var MAX_CONDENSED_ITEMS = 100;
var HIDDEN_THREAT_COUNT = 0;
var OTHER_COLOR = "#999";
var THREAT_INFIX = "~>";
var FLOOD_THREAT_PREFIX = "...";
var DGA_THREAT_INFIX = " dga "; // set by sensor (based on known DGAs)
var DNS_EXHAUSTION_THREAT_INFIX = " dns exhaustion "; // set by sensor
var DATA_PARTS_DELIMITER = ", ";
var SUSPICIOUS_THREAT_INFIX = "suspicious";
var HEURISTIC_THREAT_INFIX = "heuristic";
var FLOOD_UID_SUFFIX = "F0";
var DGA_UID_SUFFIX = "D0";
var DNS_EXHAUSTION_UID_SUFFIX = "N0";
var DEFAULT_STATUS_BORDER = "1px solid #a8a8a8";
var DEFAULT_FONT_FAMILY = "Verdana, Geneva, sans-serif";
var LOG_COLUMNS = { TIME: 0, SENSOR: 1, SRC_IP: 2, SRC_PORT: 3, DST_IP: 4, DST_PORT: 5, PROTO: 6, TYPE: 7, TRAIL: 8, INFO: 9, REFERENCE: 10 };
var LOG_COLUMNS_SIZE = 0;
var DATATABLES_COLUMNS = { THREAT: 0, SENSOR: 1, EVENTS: 2, SEVERITY: 3, FIRST_SEEN: 4, LAST_SEEN: 5, SPARKLINE: 6, SRC_IP: 7, SRC_PORT: 8, DST_IP: 9, DST_PORT: 10, PROTO: 11, TYPE: 12, TRAIL: 13, INFO: 14, REFERENCE: 15, TAGS: 16 };
var PORT_NAMES = { 1: "tcpmux", 2: "nbp", 4: "echo", 6: "zip", 7: "echo", 9: "discard", 11: "systat", 13: "daytime", 15: "netstat", 17: "qotd", 18: "msp", 19: "chargen", 20: "ftp-data", 21: "ftp", 22: "ssh", 23: "telnet", 25: "smtp", 37: "time", 39: "rlp", 42: "nameserver", 43: "whois", 49: "tacacs", 50: "re-mail-ck", 53: "dns", 57: "mtp", 65: "tacacs-ds", 67: "bootps", 68: "bootpc", 69: "tftp", 70: "gopher", 77: "rje", 79: "finger", 80: "http", 87: "link", 88: "kerberos", 95: "supdup", 98: "linuxconf", 101: "hostnames", 102: "iso-tsap", 104: "acr-nema", 105: "csnet-ns", 106: "poppassd", 107: "rtelnet", 109: "pop2", 110: "pop3", 111: "sunrpc", 113: "auth", 115: "sftp", 117: "uucp-path", 119: "nntp", 123: "ntp", 129: "pwdgen", 135: "dcom-rpc", 137: "netbios-ns", 138: "netbios-dgm", 139: "netbios-ssn", 143: "imap2", 161: "snmp", 162: "snmp-trap", 163: "cmip-man", 164: "cmip-agent", 174: "mailq", 177: "xdmcp", 178: "nextstep", 179: "bgp", 191: "prospero", 194: "irc", 199: "smux", 201: "at-rtmp", 202: "at-nbp", 204: "at-echo", 206: "at-zis", 209: "qmtp", 210: "z3950", 213: "ipx", 220: "imap3", 345: "pawserv", 346: "zserv", 347: "fatserv", 369: "rpc2portmap", 370: "codaauth2", 371: "clearcase", 372: "ulistserv", 389: "ldap", 406: "imsp", 427: "svrloc", 443: "https", 444: "snpp", 445: "smb", 464: "kpasswd", 465: "urd", 487: "saft", 500: "isakmp", 502: "modbus", 512: "exec", 513: "login", 514: "shell", 515: "printer", 517: "talk", 518: "ntalk", 520: "route", 525: "timed", 526: "tempo", 530: "courier", 531: "conference", 532: "netnews", 533: "netwall", 538: "gdomap", 540: "uucp", 543: "klogin", 544: "kshell", 546: "dhcpv6-client", 547: "dhcpv6-server", 548: "afpovertcp", 549: "idfp", 554: "rtsp", 556: "remotefs", 563: "nntps", 587: "submission", 607: "nqs", 610: "npmp-local", 611: "npmp-gui", 612: "hmmp-ind", 623: "ipmi", 628: "qmqp", 631: "ipp", 636: "ldaps", 655: "tinc", 706: "silc", 749: "kerberos-adm", 750: "kerberos4", 751: "kerberos-master", 752: "passwd-server", 754: "krb-prop", 760: "krbupdate", 765: "webster", 775: "moira-db", 777: "moira-update", 779: "moira-ureg", 783: "spamd", 808: "omirr", 871: "supfilesrv", 873: "rsync", 901: "swat", 989: "ftps-data", 990: "ftps", 992: "telnets", 993: "imaps", 994: "ircs", 995: "pop3s", 1080: "socks", 1093: "proofd", 1094: "rootd", 1099: "rmiregistry", 1109: "kpop", 1127: "supfiledbg", 1178: "skkserv", 1194: "openvpn", 1210: "predict", 1214: "kazaa", 1236: "rmtcfg", 1241: "nessus", 1300: "wipld", 1313: "xtel", 1314: "xtelw", 1352: "lotusnote", 1433: "mssql", 1434: "ms-sql-m", 1524: "ingreslock", 1525: "prospero-np", 1529: "support", 1645: "datametrics", 1646: "sa-msg-port", 1649: "kermit", 1677: "groupwise", 1701: "l2f", 1723: "pptp", 1812: "radius", 1813: "radius-acct", 1863: "msnp", 1900: "upnp", 1957: "unix-status", 1958: "log-server", 1959: "remoteping", 2000: "cisco-sccp", 2003: "cfinger", 2010: "search", 2049: "nfs", 2053: "knetd", 2086: "gnunet", 2101: "rtcm-sc104", 2102: "zephyr-srv", 2103: "zephyr-clt", 2104: "zephyr-hm", 2105: "eklogin", 2111: "kx", 2119: "gsigatekeeper", 2121: "iprop", 2135: "gris", 2150: "ninstall", 2401: "cvspserver", 2430: "venus", 2431: "venus-se", 2432: "codasrv", 2433: "codasrv-se", 2583: "mon", 2600: "zebrasrv", 2601: "zebra", 2602: "ripd", 2603: "ripngd", 2604: "ospfd", 2605: "bgpd", 2606: "ospf6d", 2607: "ospfapi", 2608: "isisd", 2628: "dict", 2792: "f5-globalsite", 2811: "gsiftp", 2947: "gpsd", 2988: "afbackup", 2989: "afmbackup", 3050: "gds-db", 3128: "squid", 3130: "icpv2", 3260: "iscsi-target", 3306: "mysql", 3389: "rdesktop", 3493: "nut", 3632: "distcc", 3689: "daap", 3690: "svn", 4031: "suucp", 4094: "sysrqd", 4190: "sieve", 4224: "xtell", 4353: "f5-iquery", 4369: "epmd", 4373: "remctl", 4500: "ipsec-nat-t", 4557: "fax", 4559: "hylafax", 4569: "iax", 4600: "distmp3", 4691: "mtn", 4899: "radmin-port", 4949: "munin", 5002: "rfe", 5050: "mmcc", 5051: "enbd-cstatd", 5052: "enbd-sstatd", 5060: "sip", 5061: "sip-tls", 5151: "pcrd", 5190: "aol", 5222: "xmpp-client", 5269: "xmpp-server", 5308: "cfengine", 5351: "nat-pmp", 5353: "mdns", 5354: "noclog", 5355: "hostmon", 5357: "wsdapi", 5432: "postgresql", 5555: "rplay", 5556: "freeciv", 5631: "pc-anywhere", 5666: "nrpe", 5667: "nsca", 5672: "amqp", 5674: "mrtd", 5675: "bgpsim", 5680: "canna", 5688: "ggz", 5800: "vnc", 5900: "vnc", 5901: "vnc-1", 5902: "vnc-2", 5903: "vnc-3", 6000: "x11", 6001: "x11-1", 6002: "x11-2", 6003: "x11-3", 6004: "x11-4", 6005: "x11-5", 6006: "x11-6", 6007: "x11-7", 6346: "gnutella-svc", 6347: "gnutella-rtr", 6379: "redis", 6444: "sge-qmaster", 6445: "sge-execd", 6446: "mysql-proxy", 6514: "syslog-tls", 6566: "sane-port", 6667: "ircd", 7000: "afs3-fileserver", 7001: "afs3-callback", 7002: "afs3-prserver", 7003: "afs3-vlserver", 7004: "afs3-kaserver", 7005: "afs3-volser", 7006: "afs3-errors", 7007: "afs3-bos", 7008: "afs3-update", 7009: "afs3-rmtsys", 7100: "font-service", 7547: "cwmp", 8021: "zope-ftp", 8080: "http-alt", 8081: "tproxy", 8088: "omniorb", 8118: "privoxy", 8338: "maltrail", 8339: "tsusen", 8443: "https-alt", 8990: "clc-build-daemon", 9098: "xinetd", 9101: "bacula-dir", 9102: "bacula-fd", 9103: "bacula-sd", 9200: "wap-wsp", 9359: "mandelspawn", 9418: "git", 9667: "xmms2", 9673: "zope", 10000: "webmin", 10050: "zabbix-agent", 10051: "zabbix-trapper", 10080: "amanda", 10081: "kamanda", 10082: "amandaidx", 10083: "amidxtape", 10809: "nbd", 11112: "dicom", 11201: "smsqp", 11211: "memcached", 11371: "hkp", 13720: "bprd", 13721: "bpdbm", 13722: "bpjava-msvc", 13724: "vnetd", 13782: "bpcd", 13783: "vopied", 15345: "xpilot", 17001: "sgi-cmsd", 17002: "sgi-crsd", 17003: "sgi-gcd", 17004: "sgi-cad", 17185: "vxworks", 17500: "db-lsp", 20011: "isdnlog", 20012: "vboxd", 22125: "dcap", 22128: "gsidcap", 22273: "wnn6", 24554: "binkp", 27017: "mongo", 27374: "asp", 30865: "csync2", 53413: "netis", 57000: "dircproxy", 60177: "tfido", 60179: "fido" };
var SEARCH_TIP_TIMER = 0;
var DRAW_SPARKLINES_TIMER = 0;
var PAPAPARSE_COMPLETE_TIMER = 0;
var REPORT_URL = "http://23.254.203.53/report.php" // NOTE: Right click / Report false positive
var SEARCH_TIP_URL = "https://www.vincentde.com/search?q=${query}"
//var SEARCH_TIP_URL = "https://www.searchencrypt.com/search/?q=%22${query}%22"; // Reference: https://kinsta.com/blog/alternative-search-engines/
//var SEARCH_TIP_URL = "https://duckduckgo.com/?q=${query}";
//var SEARCH_TIP_URL = "https://www.google.com/cse?cx=011750002002865445766%3Ay5klxdomj78&ie=UTF-8&q=${query}";
var DAY_SUFFIXES = { 1: "st", 2: "nd", 3: "rd" };
var DOT_COLUMNS = [ LOG_COLUMNS.SENSOR, LOG_COLUMNS.SRC_PORT, LOG_COLUMNS.SRC_IP, LOG_COLUMNS.DST_IP, LOG_COLUMNS.DST_PORT, LOG_COLUMNS.TRAIL, LOG_COLUMNS.PROTO ];
var DATA_CONDENSING_COLUMNS = [ LOG_COLUMNS.SRC_PORT, LOG_COLUMNS.DST_IP, LOG_COLUMNS.DST_PORT, LOG_COLUMNS.PROTO ];
var SPARKLINE_COLOR = "#ff0000";
var NONCE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var NONCE_LENGTH = 12;
var CHUNK_SIZE = 20 * 1024 * 1024; // 20MB
var CTRL_CLICK_PRESSED = false;
var CTRL_DATES = [];
var PREFERRED_TRAIL_COLORS = { DNS: "#3366cc", IP: "#dc3912", URL: "#ffad33", UA: "#9900cc" };
var SEVERITY = { LOW: 1, MEDIUM: 2, HIGH: 3 };
var SEVERITY_COLORS = { 1: "#8ba8c0", 2: "#f0ad4e", 3: "#d9534f"};
var CHART_TOOLTIP_FORMAT = "<%= datasetLabel %>: <%= value %>";
var INFO_SEVERITY_KEYWORDS = { "malware": SEVERITY.HIGH, "adversary": SEVERITY.HIGH, "ransomware": SEVERITY.HIGH, "reputation": SEVERITY.LOW, "attacker": SEVERITY.LOW, "spammer": SEVERITY.LOW, "compromised": SEVERITY.LOW, "crawler": SEVERITY.LOW, "scanning": SEVERITY.LOW }
var STORAGE_KEY_ACTIVE_STATUS_BUTTON = "STORAGE_KEY_ACTIVE_STATUS_BUTTON";
var STORAGE_KEY_EDIT_ALIASES = "STORAGE_KEY_EDIT_ALIASES";
var STORAGE_KEY_HIDDEN_THREATS = "STORAGE_KEY_HIDDEN_THREATS";
var COMMA_ENCODE_TRAIL_TYPES = { UA: true, URL: true};
var TOOLTIP_FOLDING_REGEX = /([^\s]{60})/g;
var REPLACE_SINGLE_CLOUD_WITH_BRACES = false;
var IP_ALIASES = {};
var DEMO = false;
$("body").loader("show");
$("#graph_close").on("click", graphClose);
$("#spanToggleHeatmap").on("click", toggleHeatmap);
$("#logo").on("click", resetView);
for (var column in LOG_COLUMNS) if (LOG_COLUMNS.hasOwnProperty(column)) LOG_COLUMNS_SIZE++;
var _ = {};
for (var i = 0; i < DATA_CONDENSING_COLUMNS.length; i++) {
_[DATA_CONDENSING_COLUMNS[i]] = true;
}
DATA_CONDENSING_COLUMNS = _;
window.onkeydown = function(event) {
CTRL_DATES.length = 0;
CTRL_CLICK_PRESSED = event.ctrlKey;
};
window.onkeyup = function(event) {
CTRL_CLICK_PRESSED = false;
};
+// Handle window resize to adjust table
$(window).resize(function() {
if (typeof $.fn.dataTable !== 'undefined') {
$('#details').DataTable().columns.adjust();
}
});
// Retrieve (and parse) log data
$(document).ready(function() {
$("#noscript").remove();
// assign buttons
$("#btnDrawThreats").on("click", function() {
drawInfo("Threats");
});
$("#btnDrawEvents").on("click", function() {
drawInfo("Events");
});
$("#btnDrawSeverity").on("click", function() {
drawInfo("Severity");
});
$("#btnDrawSources").on("click", function() {
drawInfo("Sources");
});
$("#btnDrawTrails").on("click", function() {
drawInfo("Trails");
});
// Reference: http://tosbourn.com/a-fix-for-window-location-origin-in-internet-explorer/
if (!window.location.origin)
window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
DEMO = !window.location.origin.startsWith('http') || (window.location.origin == "https://maltraildemo.github.io");
if (!DEMO) {
initCalHeatmap();
initDialogs();
}
Papa.SCRIPT_PATH = "/js/papaparse.min.js"
Papa.RemoteChunkSize = CHUNK_SIZE; // 10 MB (per one chunk request)
Chart.defaults.global.tooltipFontFamily = DEFAULT_FONT_FAMILY;
Chart.defaults.global.tooltipTitleFontFamily = DEFAULT_FONT_FAMILY;
Chart.defaults.global.scaleLabel = "<%=numberWithCommas(value)%>";
Chart.defaults.global.scaleFontFamily = DEFAULT_FONT_FAMILY;
Chart.defaults.global.animationSteps = 10;
$("#header_container").sticky({ topSpacing: 0 });
$("#graph_close").css("left", CHART_WIDTH / 2 - 11)
init(location.origin + "/events?date=" + formatDate(new Date()), new Date());
// Reference: https://stackoverflow.com/a/20471268
$(document).bind("mousedown", function (e) {
if (!$(e.target).parents(".custom-menu").length > 0) {
$(".custom-menu").hide(100);
}
});
$(".custom-menu li").click(function() {
var table = $("#details").dataTable();
var hidden_threats = $.jStorage.get(STORAGE_KEY_HIDDEN_THREATS, {});
var threat = $(CONTEXT_MENU_ROW).find("td:first").text();
switch ($(this).attr("data-action")) {
case "hide_threat":
hidden_threats[threat] = true;
HIDDEN_THREAT_COUNT += 1;
$.jStorage.set(STORAGE_KEY_HIDDEN_THREATS, hidden_threats);
table.api().row(CONTEXT_MENU_ROW).remove().draw();
break;
case "report_false_positive":
$.ajax({
type: "POST",
url: REPORT_URL,
data: $(CONTEXT_MENU_ROW).find("td").map(function() { return $(this).text(); }).get().join('|'),
success: function(result) { alertify.success("Threat successfully reported to developers"); }
})
break;
}
$(".custom-menu").hide(100);
});
});
function initDialogs() {
var options = {
autoOpen: false,
resizable: false,
//autoResize: true,
width: "auto",
modal: true,
buttons: {
Cancel: function() {
$(this).dialog("close");
},
"Log In": function() {
var SHA256 = new Hashes.SHA256;
var nonce = generateNonce();
$.ajax({
type: "POST",
url: "login",
dataType: "text",
data: "username=" + $(this).find("#username")[0].value.trim() + "&hash=" + SHA256.hex(SHA256.hex($(this).find("#password")[0].value.trim()) + nonce) + "&nonce=" + nonce,
cache: false,
beforeSend: function() {
$("input").prop("disabled", true);
$(".ui-dialog-buttonpane button").button("disable");
},
complete: function(response) {
$("input").prop("disabled", false);
$(".ui-dialog-buttonpane button").button("enable");
if(response.status === 401) {
alertify.error("Wrong username and/or password");
$("#login_dialog input").val("");
$("#login_dialog").effect("highlight", { color: 'red' }, 500);
$("#username").focus();
}
else if (response.status === 0) {
alertify.error("Network connection issue");
}
else {
window.location.href = "/";
}
}
});
}
},
close: function(event, ui) {
$(this).dialog('destroy').remove();
}
};
$("#login_link").click(function() {
$("body").loader("hide");
if ($("#login_dialog").length === 0) {
$('<div id="login_dialog" title="Authentication"><table><tbody><tr><td style="display: inline-block !important">Username:</td><td style="display: inline-block !important"><input id="username" name="username"></td></tr><tr><td style="display: inline-block !important">Password:</td><td style="display: inline-block !important"><input id="password" name="password" type="password" autocomplete="off"></td></tr></tbody></table></div>').appendTo('body').dialog(options);
} else {
$("#login_dialog").dialog("open");
}
$("#login_dialog input").val("");
$("#login_dialog").dialog("open")
.keyup(function(e) {
// Reference: http://stackoverflow.com/questions/868889/submit-jquery-ui-dialog-on-enter
if (e.keyCode === $.ui.keyCode.ENTER) {
$(this).parent().find('.ui-dialog-buttonpane button:last').click();
return false;
}
});
});
}
$(window).resize(function() {
$("#login_dialog").dialog("option", "position", {my: "center", at: "center", of: window});
});
function checkAuthentication() {
$.ajax({
type: "GET",
url: "whoami",
dataType: "text",
cache: false,
complete: function(response) {
if(response.status === 404) {
document.title = "Maltrail";
document.body.hidden = true;
throw new Error("Maltrail should be accessed ONLY at its server instance's address (e.g. http://127.0.0.1:8338)");
}
else if ((response.status === 200) && (typeof response.responseText !== "undefined") && (response.responseText.length > 0)) {
_USER = response.responseText;
if (_USER !== '?') {
$("#login_link").html("Log Out (" + _USER + ")");
$("#login_link").off("click");
$("#login_link").click(function() {
window.location.href = "logout";
});
}
else {
$("#login_link").html("");
}
if (window.location.search) {
var refresh = window.location.search.match(/refresh=(\d+)/);
if ((refresh !== null) && (parseInt(refresh[1]) > 0)) {
setTimeout(function(){
window.location.reload(true);
}, 1000 * parseInt(refresh[1]));
}
}
}
else if (window.location.origin.startsWith('http')) {
_USER = "";
document.title = "Maltrail (unauthorized)";
setTimeout(function() {
$("#login_link").click();
}, 1000);
}
}
});
}
function toggleHeatmap() {
if ($("#heatmap_container").is(":visible"))
$("#heatmap_container").hide();
else {
$("#heatmap_container").removeClass("hidden").show();
$(".graph-legend").attr("width", 14); // dirty patch for ugly legend width (missing one pixel)
scrollTo("#header_container-sticky-wrapper");
}
}
function graphClose() {
$("#chart_area").empty();
resetStatusButtons();
}
function initCalHeatmap() {
var start = new Date();
//start.setYear(start.getYear() - 1);
start.setDate(start.getDate() - 90);
var cal = new CalHeatMap();
try {
cal.init({
domain: "month",
subdomain: "day",
itemSelector: "#cal-heatmap",
range: 4,
cellSize: 13,
legendCellSize: 13,
legendHorizontalPosition: "right",
legendVerticalPosition: "center",
legendOrientation: "vertical",
maxDate: new Date(),
itemName: ["event", "events"],
domainLabelFormat: "%Y-%m",
legend: [ 500, 1000, 5000, 10000 ], // more than 4 will make it unusable (>last are not colorized)
legendMargin: [ 0, 0, 0, 20 ],
label: {
position: "bottom"
},
data: window.location.origin + "/counts?from={{d:start}}&to={{d:end}}",
highlight: [ "now" ],
subDomainTitleFormat: {
empty: "No events on {date}",
filled: "~{count} (total) events on {date}"
},
start: start,
onClick: function(date, nb) {
this.highlight(date);
if (!CTRL_CLICK_PRESSED) {
this.highlight(date);
query(date);
}
else {
CTRL_DATES.push(date);
this.highlight(CTRL_DATES);
if (CTRL_DATES.length === 2)
query(date, CTRL_DATES[0]);
}
}
});
}
catch(err) {
}
finally {
$("#heatmap-previous").on("click", function() {
cal.previous();
});
$("#heatmap-next").on("click", function() {
cal.next();
});
}
}
function charTrim(str, chr) {
while (str.substr(0, 1) === chr)
str = str.substr(1);
while (str.substr(str.length - 1) === chr)
str = str.substr(0, str.length - 1);
return str;
}
// Reference: http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// Reference: http://en.wikipedia.org/wiki/Private_network
function isLocalAddress(ip) {
if (ip.startsWith("10.") || ip.startsWith("192.168.") || ip.startsWith("127."))
return true;
else if (ip.startsWith("172.")) {
var _ = parseInt(ip.split(".")[1]);
return ((_ >= 16) && (_ <= 31));
}
else if (ip === "::1")
return true;
else
return false;
}
// Reference: https://www.w3resource.com/javascript-exercises/javascript-array-exercise-4.php
var last = function(array, n) {
if (array == null)
return void 0;
if (n == null)
return array[array.length - 1];
return array.slice(Math.max(array.length - n, 0));
};
var entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
};
// Reference: http://stackoverflow.com/a/12034334
function escapeHtml(string) {
return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
}
// Reference: http://24ways.org/2010/calculating-color-contrast/
function getContrast50(hexcolor) {
return (parseInt(hexcolor, 16) > 0xffffff / 2) ? "black": "white";
}
// Reference: http://stackoverflow.com/questions/340209/generate-colors-between-red-and-green-for-a-power-meter
function getPercentageColor(percentage) {
var power = percentage / 100.0;
if ((0 <= power) && (power < 0.5)) {
green = 1.0;
red = 2 * power;
}
if ((0.5 <= power) && (power <= 1)) {
red = 1.0;
green = 1.0 - 2 * (power - 0.5);
}
red = Math.round(red * 255);
green = Math.round(green * 255);
return "#" + pad(red.toString(16), 2) + pad(green.toString(16), 2) + "00";
}
function getContrastYIQ(hexcolor){
if (hexcolor.charAt(0) === "#")
hexcolor = hexcolor.slice(1);
var r = parseInt(hexcolor.substr(0, 2), 16);
var g = parseInt(hexcolor.substr(2, 2), 16);
var b = parseInt(hexcolor.substr(4, 2), 16);
var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
return (yiq >= 128) ? "black" : "white";
}
function getTagHtml(tag) {
var retval = "";
if (tag.length > 0) {
var color = getHashColor(tag);
retval = String.prototype.concat.apply("", ['<span class="tag ', getContrastYIQ(color), '-label-text" style="background-color: ', color, '">', tag, '</span>']);
}
return retval;
}
function getHashColor(value) {
return "#" + pad(value.hashCode().toString(16), 6).substring(0, 6);
}
// Reference: http://stackoverflow.com/a/6969486
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
function getThreatUID(threat) { // e.g. 192.168.0.1~>shv4.no-ip.biz
if (threat.startsWith(FLOOD_THREAT_PREFIX))
return pad(threat.hashCode().toString(16), 6).substr(0, 6) + FLOOD_UID_SUFFIX;
else if (threat.indexOf(DGA_THREAT_INFIX) > -1)
return pad(threat.hashCode().toString(16), 6).substr(0, 6) + DGA_UID_SUFFIX;
else if (threat.indexOf(DNS_EXHAUSTION_THREAT_INFIX) > -1)
return pad(threat.hashCode().toString(16), 6).substr(0, 6) + DNS_EXHAUSTION_UID_SUFFIX;
else
return pad(threat.hashCode().toString(16), 8);
}
function resetView() {
var table = $("#details").dataTable();
$("body").loader("show");
$("#main_container").toggleClass("hidden", true);
$("#heatmap_container").hide();
$("#calendar_container").hide();
table.fnFilter("");
graphClose();
window.location.href = window.location.href;
}
function init(url, from, to) {
var csv = "";
document.title = "Maltrail (loading...)";
$("body").loader("show");
$("#main_container").toggleClass("hidden", true);
$("#heatmap_container").hide();
$("#calendar_container").hide();
$(".alertify-log").remove();
_THREATS = {};
_SOURCES = {};
_SOURCE_EVENTS = {};
_TRAILS = {};
_FLOOD_TRAILS = {};
_HOURS = {};
_SEVERITY_COUNT = {}
TRAIL_TYPES = {};
_DATASET.length = 0;
_TOTAL_EVENTS = 0;
_CHUNK_COUNT = 0;
for (var severity in SEVERITY)
_SEVERITY_COUNT[SEVERITY[severity]] = 0;
if (DEMO) {
$(".bottom").html($(".bottom").html().replace(/ \(.+\)/, ""));
csv = getDemoCSV();
$("#login_link").toggleClass("hidden", true);
$("#login_splitter").toggleClass("hidden", true);
}
else {
$("#login_link").toggleClass("hidden", false);
$("#login_splitter").toggleClass("hidden", false);
$.ajax({
type: "GET",
url: "ping",
dataType: "text",
cache: false,
complete: function(response) {
if ((typeof response.responseText === "undefined") || (response.responseText.length === 0))
alertify.error("No connection to the server");
else
checkAuthentication();
}
});
}
Papa.parse(DEMO ? csv : url, {
download: !DEMO,
delimiter: ' ',
//newline: '\n',
worker: !DEMO,
skipEmptyLines: true,
chunk: function(results) {
var title = document.title.replace(/\s?\.\s?/g, '.');
var parts = title.split('.');
var total = results.data.length;
var _ = _CHUNK_COUNT % parts.length;
var trailSources = { };
if (_ < parts.length - 1)
parts[_] += " ";
else
parts[_] = " " + parts[_];
document.title = parts.join('.');
_CHUNK_COUNT += 1;
for (var i = 0; i < total; i++) {
var data = results.data[i];
if (data.length !== LOG_COLUMNS_SIZE)
continue;
var trail = data[LOG_COLUMNS.TRAIL];
var type = data[LOG_COLUMNS.TYPE];
if (type.match(/^[A-Z]+$/) === null)
continue;
if (!(type in TRAIL_TYPES))
TRAIL_TYPES[type] = PREFERRED_TRAIL_COLORS[type] || getHashColor(type);
if (type in COMMA_ENCODE_TRAIL_TYPES)
trail = trail.replace(/\,/g, "&#44;");
trail = data[LOG_COLUMNS.TRAIL] = trail.replace(/\\\(/g, "&#40;").replace(/\\\)/g, "&#41;")
var _ = trail.replace(/\([^)]+\)/g, "");
if (!(_ in trailSources))
trailSources[_] = {};
trailSources[_][data[LOG_COLUMNS.SRC_IP]] = true;
_ += " (" + type + ")";
if (!(_ in _TRAILS))
_TRAILS[_] = 1;
else
_TRAILS[_] += 1;
}
for (var _ in trailSources) {
if (Object.size(trailSources[_]) > FLOOD_TRAIL_THRESHOLD)
_FLOOD_TRAILS[charTrim(_, '.')] = true;
}
for (var i = 0; i < results.data.length; i++) {
var threat_text, threat_data, match, _;
var data = results.data[i]
if (data.length !== LOG_COLUMNS_SIZE)
continue;
var time = data[LOG_COLUMNS.TIME];
var src_ip = data[LOG_COLUMNS.SRC_IP];
var type = data[LOG_COLUMNS.TYPE];
var info = data[LOG_COLUMNS.INFO];
var reference = data[LOG_COLUMNS.REFERENCE];
if (type.match(/^[A-Z]+$/) === null)
continue;
_ = data[LOG_COLUMNS.TRAIL];
_ = charTrim(charTrim(_.replace(/\([^)]+\)/g, ""), ' '), '.');
var flood = _ in _FLOOD_TRAILS;
var dga = info.indexOf(DGA_THREAT_INFIX) > -1;
var dns_exhaustion = info.indexOf(DNS_EXHAUSTION_THREAT_INFIX) > -1;
var heuristic = reference.indexOf(HEURISTIC_THREAT_INFIX) > -1;
if (dns_exhaustion)
threat_text = info + THREAT_INFIX + _;
else if (flood)
threat_text = FLOOD_THREAT_PREFIX + THREAT_INFIX + _;
else if (dga)
threat_text = src_ip + THREAT_INFIX + info;
else if (heuristic)
threat_text = src_ip + THREAT_INFIX + _ + info;
else
threat_text = src_ip + THREAT_INFIX + _;
_TOTAL_EVENTS += 1;
if (!(threat_text in _THREATS))
threat_data = _THREATS[threat_text] = [1, [time], time, time, data]; // count, times, minTime, maxTime, (threat)data
else {
match = time.match(/ (\d+:\d+)/);
threat_data = _THREATS[threat_text];
threat_data[0] += 1;
if (match !== null)
reduced = time.match(/ (\d+:\d+)/)[1];
else
reduced = time;
if (reduced != last(threat_data[1]))
threat_data[1].push(reduced);
if (time < threat_data[2])
threat_data[2] = time;
else if (time > threat_data[3])
threat_data[3] = time;
}
_ = threat_data[4];
for (var j = 0; j < DOT_COLUMNS.length; j++) {
var column = DOT_COLUMNS[j];
var condensed = (column in DATA_CONDENSING_COLUMNS) && data[column].contains(',');
if (condensed || (data[column] !== _[column])) {
if (typeof _[column] === "string") {
var original = _[column];
_[column] = {};
if (condensed) {
var parts = original.split(',');
for (var k = 0; k < Math.min(parts.length, MAX_CONDENSED_ITEMS); k++) {
_[column][parts[k]] = true;
}
if (parts.length > MAX_CONDENSED_ITEMS)
_[column]["..."] = true;
}
else
_[column][original] = true;
}
if (typeof data[column] === "string") {
if (condensed) {
var parts = data[column].split(',');
for (var k = 0; k < parts.length; k++) {
if (Object.keys(_[column]).length >= MAX_CONDENSED_ITEMS)
_[column]["..."] = true;
else
_[column][parts[k]] = true;
}
}
else {
if (Object.keys(_[column]).length < MAX_CONDENSED_ITEMS)
_[column][data[column]] = true;
else
_[column]["..."] = true;
}
}
}
}
if (!(src_ip in _SOURCES))
_SOURCES[src_ip] = 1;
else
_SOURCES[src_ip] += 1;
if (!(src_ip in _SOURCE_EVENTS)) {
_SOURCE_EVENTS[src_ip] = {};
for (var key in TRAIL_TYPES)
_SOURCE_EVENTS[src_ip][key] = 0;
}
_SOURCE_EVENTS[src_ip][type] += 1;
match = time.match(/(\d{4})-(\d{2})-(\d{2})\ (\d{2}):(\d{2}):(\d{2})/);
if (match !== null) {
var date = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), parseInt(match[4]), parseInt(match[5]), parseInt(match[6]));
var hour = Math.floor(date.getTime() / 60 / 60 / 1000);
if (!(hour in _HOURS)) {
_HOURS[hour] = {};
for (var item in TRAIL_TYPES)
_HOURS[hour][item] = 0;
}
_HOURS[hour][type] += 1;
if (!(threat_text in _HOURS[hour]))
_HOURS[hour][threat_text] = 0;
_HOURS[hour][threat_text] += 1;
}
}
},
complete: function() {
clearTimeout(PAPAPARSE_COMPLETE_TIMER);
PAPAPARSE_COMPLETE_TIMER = setTimeout(function() {
var hidden_threats = $.jStorage.get(STORAGE_KEY_HIDDEN_THREATS, {});
// threat sensor first_time last_time count src_ip src_port dst_ip dst_port proto type trail info reference tags
for (var threat_text in _THREATS) {
var threatUID = getThreatUID(threat_text);
var threat_data = _THREATS[threat_text];
// Skip invalid threat data
if (!threat_data || threat_data.length < 5) {
continue;
}
var count = threat_data[0];
var times = threat_data[1];
var minTime = threat_data[2];
var maxTime = threat_data[3];
var sparkline_data = [];
var data = threat_data[4];
var row = [];
var severity = SEVERITY.MEDIUM;
var stored_locally = $.jStorage.get(threatUID);
var tagData = "";
if (threatUID in hidden_threats) {
HIDDEN_THREAT_COUNT += 1;
continue;
}
if (stored_locally !== null)
tagData = stored_locally.tagData;
for (var i = 0; i < DOT_COLUMNS.length; i++) {
var column = DOT_COLUMNS[i];
if (typeof data[column] !== "string") {
var _ = [];
for (var entry in data[column]) {
if ((column === LOG_COLUMNS.TRAIL) && (data[LOG_COLUMNS.TYPE] === "IP") && (entry.indexOf('(') === -1))
continue;
_.push(entry.replace(DATA_PARTS_DELIMITER, DATA_PARTS_DELIMITER.replace(" ", "")));
}
if ((column === LOG_COLUMNS.SRC_PORT) || (column === LOG_COLUMNS.DST_PORT))
_.sort(function(a, b) {
a = parseInt(a);
b = parseInt(b);
return a < b ? -1 : (a > b ? 1 : 0);
});
else if ((column === LOG_COLUMNS.SRC_IP) || (column === LOG_COLUMNS.DST_IP))
_.sort(function(a, b) {
a = _ipSortingValue(a);
b = _ipSortingValue(b);
return a < b ? -1 : (a > b ? 1 : 0);
});
else
_.sort();
data[column] = _.join(DATA_PARTS_DELIMITER);
}
}
var min_ = null;
var max_ = null;
var _ = [];
for (var hour in _HOURS) {
if (min_ === null)
min_ = hour;
else
min_ = Math.min(min_, hour);
if (max_ === null)
max_ = hour;
else
max_ = Math.max(max_, hour);
}
if ((min_ !== null) && (max_ !== null)) {
var ms = 60 * 60 * 1000;
min_ = dayStart(min_ * ms) / ms;
max_ = dayEnd(max_ * ms) / ms;
for (var hour = min_; hour <= max_; hour++) {
if (!(hour in _HOURS))
_HOURS[hour] = {};
}
}
for (var hour in _HOURS)
_.push([hour >>> 0, _HOURS[hour][threat_text]]);
_.sort(function(a, b) {
a = a[0];
b = b[0];
return a < b ? -1 : (a > b ? 1 : 0);
});
for (var i = 0; i < 24; i++)
sparkline_data.push(0);
var total_days = Math.round(_.length / 24);
for (var i = 0; i < _.length; i++) {
sparkline_data[Math.floor(i / total_days)] += (_[i][1] | 0);
//_MAX_SPARKLINE_PER_HOUR = Math.max(_MAX_SPARKLINE_PER_HOUR, _[i][1] | 0);
}
if (data[LOG_COLUMNS.REFERENCE].contains("(custom)"))
severity = SEVERITY.HIGH;
else if (data[LOG_COLUMNS.REFERENCE].contains("(remote custom)"))
severity = SEVERITY.HIGH;
else if (data[LOG_COLUMNS.INFO].contains("potential malware site"))
severity = SEVERITY.MEDIUM;
else if (data[LOG_COLUMNS.REFERENCE].contains("malwaredomainlist"))
severity = SEVERITY.HIGH;
else if (data[LOG_COLUMNS.INFO].contains("malware distribution"))
severity = SEVERITY.MEDIUM;
else if (data[LOG_COLUMNS.INFO].contains("mass scanner"))
severity = SEVERITY.LOW;
else {
for (var keyword in INFO_SEVERITY_KEYWORDS)
if (data[LOG_COLUMNS.INFO].contains(keyword)) {
severity = INFO_SEVERITY_KEYWORDS[keyword];
break;
}
}
_SEVERITY_COUNT[severity] += 1;
row = [threatUID, data[LOG_COLUMNS.SENSOR], [count, times], severity, minTime, maxTime, sparkline_data.join(","), data[LOG_COLUMNS.SRC_IP], data[LOG_COLUMNS.SRC_PORT], data[LOG_COLUMNS.DST_IP], data[LOG_COLUMNS.DST_PORT], data[LOG_COLUMNS.PROTO], data[LOG_COLUMNS.TYPE], data[LOG_COLUMNS.TRAIL], data[LOG_COLUMNS.INFO], data[LOG_COLUMNS.REFERENCE], tagData];
_DATASET.push(row);
}
if (DEMO) {
alertify.log("Showing demo data");
document.title = "Maltrail (demo)";
$("#period_label").html("(demo)");
}
else {
if (_DATASET.length > 0)
alertify.success("Processed " + numberWithCommas(_TOTAL_EVENTS) + " events");
else
alertify.log("No events found");
var period = "";
if (typeof from !== "undefined") {
period += formatDate(from);
if (typeof to !== "undefined")
period += "_" + formatDate(to);
}
if (document.title.indexOf("unauthorized") === -1)
document.title = "Maltrail (" + period + ")";
scrollTo("#main_container");
var _ = moment(dayStart(from)).from(dayStart(new Date()));
if (_.indexOf("seconds") != -1)
_ = "today";
$("#period_label").html("<b>" + period + "</b> (" + _ + ")");
}
if (HIDDEN_THREAT_COUNT > 0)
alertify.log(HIDDEN_THREAT_COUNT + " threats are hidden in details table");
try {
initDetails();
initVisual();
}
catch(err) {
alert(err);
}
$("#main_container").toggleClass("hidden", false);
$("#main_container").children().toggleClass("hidden", false); // Reference: http://stackoverflow.com/a/4740050
$(".dynamicsparkline").parent().children().toggleClass("hidden", false);
$.sparkline_display_visible();
$("#chart_area").empty();
if (jQuery.isEmptyObject(_HOURS))
$("li.status-button").css("cursor", "default");
else
$("li.status-button").css("cursor", "pointer");
if ($.jStorage.get(STORAGE_KEY_ACTIVE_STATUS_BUTTON) !== null)
drawInfo($.jStorage.get(STORAGE_KEY_ACTIVE_STATUS_BUTTON))
else
resetStatusButtons();
$("#calendar_container").show();
$("#header_container").show();
$("body").loader("hide");
}, 500);
}
});
}
function resetStatusButtons() {
$.jStorage.deleteKey(STORAGE_KEY_ACTIVE_STATUS_BUTTON);
$("li.status-button").each(function() {
$(this).css("text-shadow", "-1px -1px 0 rgba(0, 0, 0, 0.50), -1px 1px 0 rgba(0, 0, 0, 0.50), 1px 1px 0 rgba(0, 0, 0, 0.50), 1px -1px 0 rgba(0, 0, 0, 0.50)");
$(this).css("border", "3px solid rgba(0, 0, 0, 0.50)");
});
$("#graph_close").hide();
}
function scrollTo(id) {
if ($(id).length > 0)
$("html, body").animate({
scrollTop: $(id).offset().top
}, 300);
}
function addrToInt(value) {
var _ = value.split('.');
return (_[0] << 24) + (_[1] << 16) + (_[2] << 8) + _[3];
}
function makeMask(bits) {
return 0xffffffff ^ (1 << 32 - bits) - 1;
}
function netmaskValidate(netmask) {
if (!netmask || typeof netmask !== 'string') {
return false;
}
var match = netmask.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/);
return match !== null;
}
function searchTipToTab(query) {
var win = window.open(SEARCH_TIP_URL.replace("${query}", query), '_blank');
// Reference: http://stackoverflow.com/a/19851803
if(win) {
// Browser has allowed it to be opened
win.focus();
} else {
// Browser has blocked it
alert('Please allow popups for this site');
}
}
function tagInputKeyUp(event, forcedelete) {
var table, position;
var tagData = null;
var newTag = event.target.value;
if (event.target.parentNode === null)
return;
if ((typeof forcedelete !== "undefined") || (event.keyCode === 8)) { // appendFilter or Delete
if ((typeof newTag === "undefined") || (newTag.length === 0)) {
if ((event.keyCode === 8) && (_DELETE_DELETE_PRESS !== true)) {
_DELETE_DELETE_PRESS = true;
return;
}
table = $("#details").dataTable();
position = table.fnGetPosition(event.target.parentNode);
tagData = table.fnGetData(event.target.parentNode);
if (tagData.length > 0) {
if (event.target.classList.contains("tag-input")) {
var i = tagData.lastIndexOf('|');
if (i > 0)
tagData = tagData.substring(0, i);
else
tagData = "";
}
else if (event.target.classList.contains("tag")) {
var tag = event.target.innerHTML;
var regex = new RegExp("(^|\\|)" + escapeRegExp(tag) + "($|\\|)");
tagData = charTrim(tagData.replace(regex, '|'), '|');
}
}
}
}
else if ((typeof event.keyCode === "undefined") || (event.keyCode === 13)) { // blur or Enter
if (newTag.length > 0) {
table = $("#details").dataTable();
newTag = newTag.replace(/[^a-zA-Z0-9_]/g, "");
position = table.fnGetPosition(event.target.parentNode);
tagData = table.fnGetData(event.target.parentNode);
if (!(new RegExp("(^|\\|)" + escapeRegExp(newTag) + "($|\\|)").test(tagData)))
tagData = tagData + '|' + newTag;
//$(event.target).before(getTagHtml(newTag));
//newTag = "";
}
}
_DELETE_DELETE_PRESS = false;
if (tagData !== null) {
var api = table.api();
var threat = table.fnGetData(position[0], DATATABLES_COLUMNS.THREAT);
var row = api.row(position[0]);
var data = row.data();
$.jStorage.set(threat, { tagData: tagData });
data[DATATABLES_COLUMNS.TAGS] = tagData;
// dirty patch for #14900 (reproducible on Chromium - blur being thrown after the Enter has been processed)
try {
if (row && typeof row.invalidate === 'function') {
row.invalidate();
}
} catch(err) {
console.log("Table redraw issue:", err.message);
}
api.draw(false);
}
}
function stopPropagation(event) {
if (event.stopPropagation) {
event.stopPropagation(); // W3C model
} else {
event.cancelBubble = true; // IE model
}
}
function _sort(obj) {
var tuples = [];
for (var key in obj) {
if (typeof obj[key] !== "function")
tuples.push([key, obj[key]]);
}
tuples.sort(function(a, b) {
a = a[1];
b = b[1];
return a > b ? -1 : (a < b ? 1 : 0);
});
return tuples;
}
function _ipSortingValue(a) {
var x = "";
var match = a.match(/\d+\.\d+\.\d+\.\d+/);
if (match !== null) {
var m = match[0].split(".");
for (var i = 0; i < m.length; i++) {
var item = m[i];
if(item.length === 1) {
x += "00" + item;
} else if(item.length === 2) {
x += "0" + item;
} else {
x += item;
}
}
}
return x;
}
function _ipCompareValues(a, b) {
// Reference: http://stackoverflow.com/a/949970
return _ipSortingValue(a) - _ipSortingValue(b);
}
function copyEllipsisToClipboard(event) {
if (event.button === 0) { // left mouse button
var target = $(event.target);
var text = target.parent().title || '';
var html = target.parent().html() || '';
var left = html.search(/^<[^>]*ellipsis/) !== -1;
var common = html.replace(/<span class="(ipcat|hidden)">[^<]+<\/span>/g, "").replace(/<[^>]+>/g, "");
if (!text) {
var tooltip = $(".ui-tooltip");
if (tooltip.length > 0) {
text = tooltip.text();
if (common) {
var _ = text.split(DATA_PARTS_DELIMITER);
for (var i = 0; i < _.length; i++) {
if (left)
_[i] += common;
else {
if (!common.endsWith(' '))
_[i] = common + _[i];
}
}
text = _.join(DATA_PARTS_DELIMITER);
}
}
tooltip.remove();
}
window.prompt("Copy details to clipboard (press Ctrl+C)", text);
}
}
function copyEventsToClipboard(event) {
var target = $(event.target);
var text = target.parent().find("span").text();
window.prompt("Copy details to clipboard (press Ctrl+C)", text);
}
function appendFilter(filter, event, istag) {
try {
var table = $("#details").dataTable();
var currentFilter = table.api().search();
// Reference: http://stackoverflow.com/a/3076685
if (typeof event !== "undefined") {
stopPropagation(event);
if (event.button === 0) { // left mouse button
if (!(new RegExp("\\b" + escapeRegExp(filter) + "\\b").test(currentFilter))) {
currentFilter = currentFilter + " " + filter;
table.fnFilter(currentFilter.trim());
// table.DataTable().columns(11).search(currentFilter.trim()).draw();
}
}
else if ((istag === true) && (event.button === 1)) { // middle mouse button
tagInputKeyUp(event, true);
event.preventDefault();
}
}
else {
if (!(new RegExp("\\b" + escapeRegExp(filter) + "\\b").test(currentFilter))) {
currentFilter = currentFilter + " " + filter;
table.fnFilter(currentFilter.trim());
}
}
$(".searchtip").remove();
clearTimeout(SEARCH_TIP_TIMER);
$(".ui-tooltip").remove();
$(".custom-menu").hide();
$('#details_filter label input').get(0).scrollLeft = $('#details_filter label input').get(0).scrollWidth;
//$('#details_filter label input').get(0).focus();
}
catch(err) {
}
}
// DataTables part
function initDetails() {
var details = $("#details").dataTable( {
bDestroy: true,
bAutoWidth: false,
bStateSave: true,
data: _DATASET,
columns: [
{ "title": "threat", "type": "threat", "class": "center" },
{ "title": "sensor", "class": "center" },
{ "title": "events", "type": "events", "class": "right" },
{ "title": "severity", "type": "severity", "class": "center" },
{ "title": "first_seen", "class": "center", "sType": "date-custom" },
{ "title": "last_seen", "class": "center", "sType": "date-custom" },
{ "title": "sparkline", "type": "sparkline", "class": "center" },
{ "title": "src_ip", "type": "ip-address", "class": "right" },
{ "title": "src_port", "type": "port", "class": "center" },
{ "title": "dst_ip", "type": "ip-address", "class": "right" },
{ "title": "dst_port", "type": "port", "class": "center" },
{ "title": "proto", "class": "center" },
{ "title": "type", "class": "center" },
{ "title": "trail", "class": "trail" },
{ "title": "info" },
{ "title": "reference" },
{ "title": "tags" }
],
search: {
caseInsensitive: false
},
iDisplayLength: 25,
aLengthMenu: [ [10, 25, 50, 100, 200], [10, 25, 50, 100, 200] ],
aaSorting: [ [DATATABLES_COLUMNS.LAST_SEEN, 'desc'] ],
bDeferRender: true,
searchDelay: 500,
columnDefs: [
{
orderData: [DATATABLES_COLUMNS.SPARKLINE, DATATABLES_COLUMNS.SEVERITY, DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.SPARKLINE
},
{
orderData: [DATATABLES_COLUMNS.SENSOR, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.SENSOR
},
{
orderData: [DATATABLES_COLUMNS.SEVERITY, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.SEVERITY
},
{
orderData: [DATATABLES_COLUMNS.INFO, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.INFO
},
{
orderData: [DATATABLES_COLUMNS.REFERENCE, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.REFERENCE
},
{
orderData: [DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.SRC_IP
},
{
orderData: [DATATABLES_COLUMNS.DST_IP, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.DST_IP
},
{
orderData: [DATATABLES_COLUMNS.TYPE, DATATABLES_COLUMNS.EVENTS], targets: DATATABLES_COLUMNS.TYPE
},
{
orderSequence: [ "desc", "asc" ],
targets: [ DATATABLES_COLUMNS.EVENTS, DATATABLES_COLUMNS.SEVERITY, DATATABLES_COLUMNS.LAST_SEEN, DATATABLES_COLUMNS.SPARKLINE ]
},
// {
// orderable: false,
// targets: DATATABLES_COLUMNS.SPARKLINE
// },
{
render: function (data, type, row) {
var name = "";
if (data === SEVERITY.LOW)
name = "low"
else if (data === SEVERITY.HIGH)
name = "high"
else if (data === SEVERITY.MEDIUM)
name = "medium";
var retval = "<span class='severity' style='background-color: " + SEVERITY_COLORS[data] + "' value='" + data + "'>" + name + "</span>"
return retval;
},
targets: DATATABLES_COLUMNS.SEVERITY
},
{
render: function (data, type, row) {
if (data.indexOf(',') > -1) {
var parts = data.split(DATA_PARTS_DELIMITER);
for (var i = 0; i < parts.length; i++) {
if (parts[i] in PORT_NAMES)
parts[i] = parts[i] + " (" + PORT_NAMES[parts[i]] + ")";
}
data = "<span title='" + parts.join(DATA_PARTS_DELIMITER) + "' class='ellipsis'></span>";
}
else {
if (data in PORT_NAMES)
data = data + " (" + PORT_NAMES[data] + ")";
}
return data;
},
targets: [ DATATABLES_COLUMNS.SRC_PORT, DATATABLES_COLUMNS.DST_PORT ]
},
{
render: function (data, type, row) {
if (data.indexOf(',') > -1)
data = "<span title='" + data + "' class='ellipsis'></span>";
else if (data in IP_ALIASES) {
data = "<span class='ipcat'>" + IP_ALIASES[data] + "</span>" + data;
}
else {
var stored = $.jStorage.get(STORAGE_KEY_EDIT_ALIASES);
if (stored !== null) {
if (data in stored)
data = "<span class='ipcat'>" + escapeHtml(stored[data]) + "</span>" + data;
}
}
return data;
},
targets: [ DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.DST_IP ]
},
{
render: function (data, type, row) {
if (data.indexOf(',') > -1)
data = "<span title='" + data + "' class='ellipsis'></span>" + "<span class='hidden'>" + data + "</span>";
return data;
},
targets: [ DATATABLES_COLUMNS.SENSOR, DATATABLES_COLUMNS.PROTO ]
},
{
render: function (data, type, row) {
var info = row[DATATABLES_COLUMNS.INFO];
data = data.replace(/</g, "&lt;").replace(/>/g, "&gt;");
if ((data.indexOf(',') > -1) || (data.replace(/&#\d+;/g, "|").length > LONG_TRAIL_THRESHOLD)) {
var common = "";
var title = "";
var left = false;
if ((data.indexOf('(') > -1) && (data.indexOf(')') > -1)) {
var parts = data.split(DATA_PARTS_DELIMITER);
for (var i = 0; i < parts.length; i++) {
var index = parts[i].indexOf('(');
if (index > -1) {
if (index === 0)
left = true;
common = parts[i].replace(/\(.+\)/g, "");
break;
}
}
}
var parts = data.split(DATA_PARTS_DELIMITER);
for (var i = 0; i < parts.length; i++)
parts[i] = parts[i].replace(common, "").replace(/[()]/g, ""); // replace only first occurrence
title = parts.join(DATA_PARTS_DELIMITER);
// title = data.replace(common, "").replace(/[()]/g, "");
// title = data.split(common).join("").replace(/[()]/g, "");
if (common.startsWith('.'))
title = title.split(common.substr(1)).join("");
try {
title = decodeURIComponent(title);
}
catch(err) {
}
finally {
title = title.replace(/"/g, '%22');
}
// Reference: https://stackoverflow.com/questions/3340802/add-line-break-within-tooltips
title = title.replace(TOOLTIP_FOLDING_REGEX, "$1&#10;");
common = '<span class="trail-text">' + common + '</span>';
if (left)
data = "<span title=\"" + title + "\" class='ellipsis'></span>" + common;
else
data = common + "<span title=\"" + title + "\" class='ellipsis'></span>";
}
else {
if (REPLACE_SINGLE_CLOUD_WITH_BRACES)
data = data.replace('(', '{').replace(')', '}');
if (info.contains("sinkholed"))
data = data.replace('(', '').replace(')', '');
data = '<span class="trail-text">' + data + '</span>';
}
return data;
},
targets: [ DATATABLES_COLUMNS.TRAIL ]
},
{
render: function ( data, type, row ) {
return '<div class="event-data">' + data[0] + '</div><span class="hidden">' + data[1].join(DATA_PARTS_DELIMITER) + '</span>';
},
targets: DATATABLES_COLUMNS.EVENTS
},
{
render: function ( data, type, row ) {
var color = data in TRAIL_TYPES ? TRAIL_TYPES[data] : getHashColor(data);
return '<span class="label-type white-label-text" style="background-color: ' + color + '">' + data + '</span>';
},
targets: DATATABLES_COLUMNS.TYPE
},
{
render: function (data, type, row) {
var parts = data.split(' ');
if (parts.length > 1) {
var day = parts[0].split('-')[2];
var dayint = parseInt(day);
var suffix = (dayint > 10 && dayint < 20) ? "th" : DAY_SUFFIXES[dayint % 10] || "th";
return "<div title='" + data + "'><span class='time-day'>" + day + "<sup>" + suffix + "</sup></span> " + parts[1].split('.')[0] + "</div>";
}
else
return data;
},
targets: [ DATATABLES_COLUMNS.FIRST_SEEN, DATATABLES_COLUMNS.LAST_SEEN ]
},
{
render: function (data, type, row) {
var value = 0;
var items = data.split(",");
for (var i = 0; i < items.length; i++)
if (items[i] !== "0")
value += 1;
return "<div class='sparkline hidden' value='" + value + "'>" + data + "</div>";
},
targets: DATATABLES_COLUMNS.SPARKLINE
},
{
render: function (data, type, row) {
return '<div class="label-type ' + getContrastYIQ(data.substring(0, 6)) + '-label-text" style="background-color: #' + data.substring(0, 6) + '">' + data + '</div>';
},
targets: DATATABLES_COLUMNS.THREAT
},
{
render: function (data, type, row) {
if (data.contains('(+')) {
var duplicates = data.match(/ \((\+\d+)\)$/);
if (duplicates !== null) {
data = data.replace(duplicates[0], "");
return ((data.substr(0, 1) != '(') ? '<i>' + data + '</i>': data) + '<span class="duplicates">' + duplicates[1] + '</span>';
}
duplicates = data.match(/ \(\+(.+)\)$/);
if (duplicates !== null) {
var items = duplicates[1].split(',');
data = data.replace(duplicates[0], "");
return ((data.substr(0, 1) != '(') ? '<i>' + data + '</i>': data) + '<span title="' + duplicates[1].replace(/,([^ ])/g, ", $1") + '" class="duplicates">+' + items.length + '</span>' + '<span class="searchable">' + duplicates[1] + "</span>";
}
}
return (data.substr(0, 1) != '(') ? '<i>' + data + '</i>': data;
},
targets: DATATABLES_COLUMNS.REFERENCE
},
{
render: function (data, type, row) {
var retval = "";
var tags = data.split('|');
for (var index in tags) {
var tag = tags[index];
if ((typeof tag !== "function") && (tag.length > 0)) {
retval += getTagHtml(tag);
}
}
retval += "<input class='tag-input' type='text'>";
return retval;
},
targets: DATATABLES_COLUMNS.TAGS
},
{
width: "1%",
targets: [ DATATABLES_COLUMNS.THREAT, DATATABLES_COLUMNS.SENSOR, DATATABLES_COLUMNS.EVENTS, DATATABLES_COLUMNS.SEVERITY, DATATABLES_COLUMNS.FIRST_SEEN, DATATABLES_COLUMNS.LAST_SEEN, DATATABLES_COLUMNS.SPARKLINE, DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.SRC_PORT, DATATABLES_COLUMNS.DST_IP, DATATABLES_COLUMNS.DST_PORT, DATATABLES_COLUMNS.PROTO, DATATABLES_COLUMNS.TYPE ]
}
],
oLanguage: {
sLengthMenu: "_MENU_ threats per page", // Reference: http://www.sprymedia.co.uk/dataTables/example_language.html
sZeroRecords: "No matching threats found",
sInfo: "Showing _START_ to _END_ of <span class='details_total'>_TOTAL_</span> threats",
sInfoEmpty: "Showing 0 to 0 of <span class='details_total'>0</span> total threats",
sInfoFiltered: "(<span style='color: red'>filtered</span> from _MAX_ total threats)",
sSearchPlaceholder: "Filter",
sSearch: ""
},
dom: 'T<"clear">lfrtip',
tableTools: {
aButtons: [
{
sExtends: "text",
sButtonText: "Clear",
fnClick: function (nButton, oConfig, oFlash) {
var table = $("#details").dataTable();
var settings = table.fnSettings();
table.fnFilter("");
// if ((settings._iDisplayLength > 0) && (settings._iDisplayLength < 30))
// $("html, body").animate({ scrollTop: $(document).height() }, "slow");
}
},
"print",
{
sExtends: "collection",
sButtonText: "Tools",
aButtons: [
{
sExtends: "text",
sButtonText: "Edit custom IP aliases",
fnClick: function ( nButton, oConfig, oFlash ) {
function _initAliases() {
$('#table_aliases').editableTableWidget();
$('#table_aliases td').on('change', function(event, newValue) {
if (event.target.cellIndex === 0)
return (newValue.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) !== null);
});
};
var html = '<table id="table_aliases" class="dataTable"><thead><tr class="ui-widget-header"><th>IP</th><th>Alias</th></tr></thead><tbody>';
var stored = $.jStorage.get(STORAGE_KEY_EDIT_ALIASES);
if (stored !== null) {
$.each( stored, function( ip, alias ) {
html += '<tr class="alias"><td>' + ip + '</td><td>' + alias + '</td></tr>'
});
}
html += '</tbody></table>'
$('<div id="dialog-aliases" title="Aliases"></div>').appendTo('body')
.html(html)
.dialog({
resizable: false,
width: "auto",
height: "auto",
modal: true,
buttons: {
Add: function() {
$('#table_aliases tr:last').after('<tr class="alias"><td>&nbsp;</td><td>&nbsp;</td></tr>');
_initAliases();
$("#table_aliases tr:last td:first").focus().click();
},
Done: function() {
$(this).dialog("close");
$('#dialog-aliases').remove();
window.location.reload(false);
}
},
open: function(event, ui) {
_initAliases();
},
close: function(event, ui) {
var result = {};
$(this).find(".alias").each(function() {
var ip = $(this).find("td").eq(0).text();
var alias = $(this).find("td").eq(1).text();
if ($.trim(ip) && $.trim(alias))
result[ip] = alias;
});
$.jStorage.set(STORAGE_KEY_EDIT_ALIASES, result);
$(this).dialog("destroy").remove();
}
});
}
},
{
sExtends: "text",
sButtonText: "Flush local storage",
fnClick: function ( nButton, oConfig, oFlash ) {
$('<div id="dialog-confirm" title="Flush local storage?"></div>').appendTo('body')
.html('<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>These items will be permanently deleted and<br>won\'t be recoverable. Are you sure?</p>')
.dialog({
resizable: false,
width: "auto",
height: "auto",
modal: true,
buttons: {
Cancel: function() {
$(this).dialog("close");
},
"Delete all items": function() {
$(this).dialog("close");
$.jStorage.flush();
location.reload();
}
},
close: function(event, ui) {
$(this).dialog('destroy').remove();
}
});
}
}
]
}
]
},
fnStateSaveParams: function (oSettings, oData) {
oData.start = 0;
},
fnDrawCallback: function(oSettings) {
$(".ui-tooltip").remove();
$(".custom-menu").hide();
clearTimeout(DRAW_SPARKLINES_TIMER);
$(".sparkline:contains(',')").sparkline('html', { type: 'bar', barWidth: 2, barColor: SPARKLINE_COLOR, disableInteraction: false, tooltipClassname: "sparkline-tooltip" }); //, chartRangeMin: 0, chartRangeMax: _MAX_SPARKLINE_PER_HOUR });
$(".tag-input").keyup(function(event) {
tagInputKeyUp(event);
}).blur(function(event) {
tagInputKeyUp(event);
});
try {
var sparklines = $(".sparkline");
(function drawSparklines() {
var hidden = sparklines.filter(".hidden");
hidden.filter(':lt(10)').removeClass("hidden");
$.sparkline_display_visible();
if (hidden.length) {
DRAW_SPARKLINES_TIMER = setTimeout(drawSparklines, 100);
}
}
)();
}
catch(err) {
}
},
fnRowCallback: function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
function nslookup(event, ui) {
var elem = $(this);
var html = elem.parent().html();
var match = html.match(/\d+\.\d+\.\d+\.\d+/) || html.match(/[\w:]*:[\w:]*:[\w:]*/);
if (match !== null) {
var ip = match[0];
$.ajax("https://stat.ripe.net/data/dns-chain/data.json?resource=" + ip, { dataType:"jsonp", ip: ip})
.done(function(json) {
var _ = json.data.reverse_nodes[this.ip];
if ((typeof _ === "undefined") || (_.length === 0) || (_ === "localhost")) {
_ = "-";
}
_ = String(_);
if (_.length > 40) {
_ = _.substring(0, 40) + "<br>" + _.substring(40);
}
var msg = "<p><b>" + escapeHtml(_) + "</b></p>";
ui.tooltip.find(".ui-tooltip-content").html(msg + "please wait...");
$.ajax("https://stat.ripe.net/data/whois/data.json?resource=" + ip, { dataType:"jsonp", ip: ip, msg: msg })
.done(function(json) {
// Reference: http://bugs.jqueryui.com/ticket/8740#comment:21
var found = null;
var msg = "";
if (json.data.records !== undefined)
for (var i = json.data.records.length - 1; i >= 0 ; i--) {
if ((json.data.records[i][0].key.toLowerCase().indexOf("inetnum") != -1) || (json.data.records[i][0].key.toLowerCase().indexOf("netrange") != -1)){
found = i;
break;
}
}
if (found !== null) {
for (var j = 0; j < json.data.records[found].length; j++) {
msg += json.data.records[found][j].key + ": " + json.data.records[found][j].value;
msg += "<br>";
}
msg = msg.replace(/(-\+)+/g, "--").replace(/(-\+)+/g, "--");
}
ui.tooltip.find(".ui-tooltip-content").html(this.msg + msg);
});
});
}
}
$('[title]', nRow).tooltip();
$.each([DATATABLES_COLUMNS.TRAIL], function(index, value) {
var cell = $('td:eq(' + value + ')', nRow);
if (cell === null)
return false;
var html = cell.html();
if (html === null)
return false;
if (html.indexOf('ipcat') > -1)
return false;
var match = html.match(/\d+\.\d+\.\d+\.\d+/);
if (match === null)
return false;
var interval = null;
var img = "";
var ip = match[0];
if (!DEMO && !isLocalAddress(ip)) {
if (!(ip in CHECK_IP)) {
CHECK_IP[ip] = null;
$.ajax("/check_ip?address=" + ip, { dataType: "jsonp", ip: ip, cell: cell })
.done(function(json) {
var span_ip = $(json.ipcat.length > 0 ? "<span class='ipcat'></span>" : "<span class='ipcat hidden'></span>").html(json.ipcat);
CHECK_IP[this.ip] = json;
this.cell.append(span_ip);
if (json.worst_asns === "true")
this.cell.append($("<span class='worst_asns' title='malicious ASN'></span>").tooltip());
});
}
else if (CHECK_IP[ip] !== null) {
var json = CHECK_IP[ip];
var span_ip = $(json.ipcat.length > 0 ? "<span class='ipcat'></span>" : "<span class='ipcat hidden'></span>").html(json.ipcat);
cell.append(span_ip);
if (json.worst_asns === "true")
cell.append($("<span class='worst_asns' title='malicious ASN'></span>").tooltip());
}
else {
interval = setInterval(function(ip, cell) {
if (typeof cell === "undefined")
clearInterval(interval);
else {
html = cell.html();
if (CHECK_IP[ip] !== null) {
if (html.indexOf("ipcat") === -1) {
var json = CHECK_IP[ip];
var span_ip = $(json.ipcat.length > 0 ? "<span class='ipcat'></span>" : "<span class='ipcat hidden'></span>").html(json.ipcat);
cell.append(span_ip);
if (json.worst_asns === "true")
cell.append($("<span class='worst_asns' title='malicious ASN'></span>").tooltip());
}
clearInterval(interval);
}
}
}, 500, ip, cell);
}
}
});
$.each([DATATABLES_COLUMNS.SRC_IP, DATATABLES_COLUMNS.DST_IP], function(index, value) {
var cell = $('td:eq(' + value + ')', nRow);
if (cell === null)
return false;
var html = cell.html();
if (html === null)
return false;
if ((html.indexOf('flag') > -1) || (html.indexOf('lan') > -1) || (html.indexOf(',') > -1) || (html.indexOf('ellipsis') > -1))
return false;
var match = html.match(/\d+\.\d+\.\d+\.\d+/) || html.match(/[\w:]*:[\w:]*:[\w:]*/);
if (match === null)
return false;
var interval = null;
var img = "";
var ip = match[0];
var options = { content: "please wait...", open: nslookup, position: { my: "left center", at: "right+10 top-50" } };
if (!isLocalAddress(ip)) {
if (!(ip in IP_COUNTRY)) {
IP_COUNTRY[ip] = null;
$.ajax("https://stat.ripe.net/data/geoloc/data.json?resource=" + ip, { dataType:"jsonp", ip: ip, html: html, cell: cell })
.done(function(json) {
var span_ip = $("<span title=''/>").html(this.html + " ");
var country = null;
try {
country = json.data.located_resources[0].locations[0].country.toLowerCase().split('-')[0];
}
catch(err) {
try {
country = json.data.located_resources[0].location.toLowerCase().split('-')[0];
}
catch(err) {
}
}
if ((country !== null) && (country !== "ano")) {
IP_COUNTRY[this.ip] = country;
img = '<img src="images/blank.gif" class="flag flag-' + IP_COUNTRY[this.ip] + '" title="' + IP_COUNTRY[this.ip].toUpperCase() + '">';
}
else {
IP_COUNTRY[this.ip] = "unknown";
img = '<img src="images/blank.gif" class="flag flag-unknown" title="UNKNOWN">';
}
span_ip.tooltip(options);
this.cell.html("").append(span_ip).append($(img).tooltip());
});
}
else if (IP_COUNTRY[ip] !== null) {
img = ' <img src="images/blank.gif" class="flag flag-' + IP_COUNTRY[ip] + '" title="' + IP_COUNTRY[ip].toUpperCase() + '">';
var span_ip = $("<span title=''/>").html(html + " ");
span_ip.tooltip(options);
if (typeof cell !== "undefined")
cell.html("").append(span_ip).append($(img).tooltip());
}
else {
interval = setInterval(function(ip, cell) {
if (typeof cell === "undefined")
clearInterval(interval);
else {
html = cell.html();
if (typeof html === "undefined")
clearInterval(interval);
else if (IP_COUNTRY[ip] !== null) {
if (html.indexOf("flag-") === -1) {
img = ' <img src="images/blank.gif" class="flag flag-' + IP_COUNTRY[ip] + '" title="' + IP_COUNTRY[ip].toUpperCase() + '">';
var span_ip = $("<span title=''/>").html(html + " ");
span_ip.tooltip(options);
cell.html("").append(span_ip).append($(img).tooltip());
}
clearInterval(interval);
}
}
}, 1000, ip, cell);
}
}
else {
img = '<img src="images/lan.gif" height="11px" style="margin-bottom: 0px" title="LAN">';
cell.html(html + " ").append($(img).tooltip());
}
});
}
});
$(details).find("thead").addClass("noselect");
details.off("mouseenter"); // clear previous
details.on("mouseenter", ".trail-text", function(event) {
var cell = event.target;
if (event.target.classList.contains("trail-text")) {
clearTimeout(SEARCH_TIP_TIMER);
SEARCH_TIP_TIMER = setTimeout(function(cell, event) {
if ((event.buttons === 0) && ($(".ui-tooltip").length === 0)) {
var query = cell[0].innerHTML.replace(/<span class="ipcat.+span>/g, "").replace(/<[^>]+>/g, "").replace(/[\d.]+ \(([^)]+)\)/, "$1").replace(/[()]/g, "").replace(/^www\./g, "").replace(/:\d+$/, "").replace(/^([^\/]*\.[^\/]*)\/.+/, "$1").trim();
$(".searchtip").remove();
$("body").append(
$('<div class="ui-tooltip searchtip"><div><img src="images/newtab.png" style="cursor: pointer" title="open in new tab"><img src="images/close.png" style="cursor: pointer; width: 16px; height: 16px" title="close"></div><iframe src="' + SEARCH_TIP_URL.replace("${query}", query) + '"></iframe><div>')
.css('position', 'absolute')
.show()
.position({ my: "right+10 top-200", of: event })
.on("mouseleave", function(){
$(".searchtip").remove();
})
);
$(".searchtip img[title='open in new tab']").on("click", function() { searchTipToTab(query); });
$(".searchtip img[title='close']").on("click", function() { $(".searchtip").remove(); });
}
}, 2000, $(this), event);
$(cell).on("mouseleave", function(mouseenter) {
clearTimeout(SEARCH_TIP_TIMER);
});
}
});
details.off("click", ".trail");
details.on("click", ".trail", function(event) {
clearTimeout(SEARCH_TIP_TIMER);
});
details.off("dblclick"); // clear previous
details.on("dblclick", "td", function (){
var table = $("#details").dataTable();
var filter = "";
if ($(this).find(".info-input").length > 0)
filter = $(this).find(".info-input")[0].value;
else if ($(this).find(".time-day").length > 0)
filter = $(this).find("div")[0].lastChild.textContent;
else if ($(this).find(".trail-text").length > 0)
filter = $(this).find(".trail-text")[0].lastChild.textContent.replace(/\([^)]+\)/g, "");
else if ($(this).find(".duplicates").length > 0)
filter = this.innerHTML.replace(/<span.+/g, " ").replace(/<.+?>/g, " ");
else if (this.innerHTML.indexOf("ellipsis") > -1) {
match = this.innerHTML.match(/title=["']([^"']+)/);
if (match !== null)
filter = match[1].replace(/,\ /g, " ");
else {
var tooltip = $(".ui-tooltip");
if (tooltip.length > 0)
filter = tooltip.html().replace(/,\ /g, " ");
}
}
else {
filter = this.innerHTML.replace(/<span class="ipcat">.+?<\/span>/g, "").replace(/<.+?>/g, " ");
}
filter = filter.replace(/\s+/g, " ").trim();
filter = $('<div/>').html(filter).text();
appendFilter(filter);
});
details.off("mouseup"); // clear previous
details.on("mouseup", "td", function (event) {
if (event.button === 0) { // left mouse button
if (event.target.classList.contains("tag"))
appendFilter(event.target.innerHTML, event, true);
else if (event.target.classList.toString().startsWith("severity"))
appendFilter(event.target.innerHTML, event);
else if (event.target.classList.contains("label-type"))
appendFilter(event.target.innerHTML, event);
}
else if (event.button === 1) { // middle mouse button
if (event.target.classList.contains("tag"))
appendFilter(event.target.innerHTML, event, true);
}
else if (event.button === 2) { // right mouse button/click
stopPropagation(event);
}
});
details.on("mouseup", ".event-data", copyEventsToClipboard);
details.on("mouseup", ".ellipsis", copyEllipsisToClipboard);
details.off("contextmenu"); // clear previous
details.on("contextmenu", "td", function (event) {
event.preventDefault();
$(".ui-tooltip").remove();
CONTEXT_MENU_ROW = $(event.target).closest("tr");
// Show contextmenu
$(".custom-menu").finish().toggle(100).
// In the right position (the mouse)
css({
top: event.pageY + "px",
left: event.pageX + "px"
});
});
}
function generateNonce() {
if (window.crypto && window.crypto.getRandomValues) {
const array = new Uint32Array(1);
const result = [];
for (let i = 0; i < NONCE_LENGTH; i++) {
crypto.getRandomValues(array);
result.push(NONCE_ALPHABET.charAt(array[0] % NONCE_ALPHABET.length));
}
return result.join('');
} else {
let retval = "";
for(let i = 0; i < NONCE_LENGTH; i++)
retval += NONCE_ALPHABET.charAt(Math.floor(Math.random() * NONCE_ALPHABET.length));
return retval;
}
}
String.prototype.hashCode = function() {
return murmurhash3_32_gc(this, 13);
};
// Reference: http://stackoverflow.com/a/1026087
String.prototype.capitalizeFirstLetter = function() {
return this.charAt(0).toUpperCase() + this.slice(1);
};
// Reference: http://stackoverflow.com/a/12710609
Array.prototype.insert = function (index, item) {
this.splice(index, 0, item);
};
Array.prototype.clean = function(deleteValue) {
for (var i = 0; i < this.length; i++) {
if (this[i] === deleteValue) {
this.splice(i, 1);
i--;
}
}
return this;
};
if (typeof String.prototype.startsWith !== "function") {
String.prototype.startsWith = function (str){
return this.indexOf(str) === 0;
};
}
if (typeof String.prototype.contains !== "function") {
String.prototype.contains = function (str){
return this.indexOf(str) !== -1;
};
}
// Reference: http://stackoverflow.com/a/2548133
if (typeof String.prototype.endsWith !== "function") {
String.prototype.endsWith = function(suffix) {
return this.indexOf(suffix, this.length - suffix.length) !== -1;
};
}
// Reference: http://stackoverflow.com/a/6700
Object.size = function(obj) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key))
size++;
}
return size;
};
jQuery.fn.dataTableExt.afnFiltering.push(
function(settings, data, dataIndex) {
return true;
}
);
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
// Reference: http://cdn.datatables.net/plug-ins/3cfcc339e89/sorting/date-euro.js
"date-custom-pre": function ( a ) {
var x;
if ( $.trim(a) !== '' ) {
// extract timestamp from "<div title='yyyy-mm-dd hh:mm:ss.ususus'><span class='time-day'>dd<sup>th</sup></span> hh:mm:ss</div>"
var frTimestamp = $.trim(a).split("'")[1];
var frDatea = frTimestamp.split(' ');
var frTimea = frDatea[1].split('.')[0].split(':');
var frUseca = frDatea[1].split('.')[1];
var frDatea2 = frDatea[0].split('-');
x = (frDatea2[0] + frDatea2[1] + frDatea2[2] + frTimea[0] + frTimea[1] + frTimea[2] + frUseca) * 1;
}
else {
x = Infinity;
}
return x;
},
"date-custom-asc": function ( a, b ) {
return a - b;
},
"date-custom-desc": function ( a, b ) {
return b - a;
}
});
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
// Reference: https://cdn.datatables.net/plug-ins/3cfcc339e89/sorting/ip-address.js
"ip-address-pre": function (a) {
return _ipSortingValue(a);
},
"ip-address-asc": function ( a, b ) {
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
},
"ip-address-desc": function ( a, b ) {
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
});
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
"events-pre": function (a) {
return parseInt(a.replace(/<[^>]+>/g, ""));
},
"events-asc": function ( a, b ) {
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
},
"events-desc": function ( a, b ) {
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
});
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
"severity-pre": function (a) {
var x = 0;
var match = a.match(/value=["'](\d+)/);
if (match !== null) {
x = parseInt(match[1]);
}
return x;
},
"severity-asc": function ( a, b ) {
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
},
"severity-desc": function ( a, b ) {
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
});
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
// Reference: https://cdn.datatables.net/plug-ins/3cfcc339e89/sorting/ip-address.js
"sparkline-pre": function ( a ) {
return parseInt($(a).attr("value"));
},
"sparkline-asc": function ( a, b ) {
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
},
"sparkline-desc": function ( a, b ) {
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
});
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
// Reference: https://cdn.datatables.net/plug-ins/3cfcc339e89/sorting/ip-address.js
"port-pre": function ( a ) {
var x = 0;
var match = a.match(/\d+/);
if (match !== null)
x = parseInt(match[0]);
return x;
},
"port-asc": function ( a, b ) {
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
},
"port-desc": function ( a, b ) {
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
});
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
"threat-pre": function ( a ) {
var x = "";
var match = a.match(/([0-9a-fA-F]{8})/);
if (match !== null)
//x = parseInt(match[1], 16);
x = match[1];
return x;
},
"threat-asc": function ( a, b ) {
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
},
"threat-desc": function ( a, b ) {
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
});
function setChartScale(options, maxValue) {
if (maxValue > 0) {
stepValue = Math.pow(10, Math.floor(Math.log(maxValue) / Math.LN10));
options.scaleOverride = true;
options.scaleStartValue = 0;
while ((stepValue > 0) && (stepValue === Math.floor(stepValue))) {
options.scaleStepWidth = stepValue;
options.scaleSteps = Math.ceil(maxValue / stepValue);
if ((options.scaleSteps >= 5) || (stepValue === 1))
break;
else
stepValue = stepValue / 2;
}
}
}
function drawInfo(type) {
//var color = $("#" + type.toLowerCase() + "_count").parent()[0].style["background-color"];
//$("li.status-button").css("opacity", "0.6");
$("#chart_area").empty();
if (jQuery.isEmptyObject(_HOURS))
return;
if ($("#" + type.toLowerCase() + "_count").parent()[0].style.border.contains("white")) {
resetStatusButtons();
return;
}
resetStatusButtons();
$("#" + type.toLowerCase() + "_count").parent().css("text-shadow", "none");
$("#" + type.toLowerCase() + "_count").parent().css("border", "3px solid white");
$("#graph_close").removeClass("hidden").show();
$.jStorage.set(STORAGE_KEY_ACTIVE_STATUS_BUTTON, type);
if (type === "Events") {
var ticks = {};
var labels = [];
var first = true;
var datasets = [];
var total_days = Math.round(Object.size(_HOURS) / 24);
for (var type in TRAIL_TYPES) {
var _ = [];
ticks[type] = [];
for (var hour in _HOURS)
_.push([hour >>> 0, _HOURS[hour][type]]);
_.sort(function(a, b) {
a = a[0];
b = b[0];
return a < b ? -1 : (a > b ? 1 : 0);
});
for (var i = 0; i < _.length; i++) {
var date = new Date(_[i][0] * 60 * 60 * 1000);
if (first) {
if (i % total_days === 0) {
var label = "";
if (total_days > 2) {
label += pad(date.getFullYear(), 4) + "-" + pad(date.getMonth(), 2) + "-" + pad(date.getDate(), 2) + " ";
}
label += pad(date.getHours(), 2) + "h";
labels.push(label);
}
else
labels.push("");
}
ticks[type].push(_[i][1] | 0);
}
for (var i = ticks[type].length - 1; i >= 0; i--) {
if (ticks[type][i] === 0)
ticks[type][i] = null;
else
break;
}
first = false;
datasets.push(
{
label: type,
strokeColor: TRAIL_TYPES[type],
pointColor: TRAIL_TYPES[type],
pointHighlightFill: "#fff",
data: ticks[type]
}
);
}
var data = {
labels: labels,
datasets: datasets
};
var options = {
//scaleGridLineColor: "#abb2b4",
scaleShowVerticalLines: false,
scaleShowHorizontalLines: false, // because StackedBar doesn't show them properly
datasetFill: false,
bezierCurve: false,
pointDotRadius: Math.max(1, 5 - total_days),
//scaleShowGridLines: false
//tooltipTemplate: "<%if (label){%><%=label.replace(/[^0-9]/, '')%>:00h-<%=label.replace(/[^0-9]/, '')%>:59h: <%}%><%= value %> events",
tooltipTemplate: CHART_TOOLTIP_FORMAT,
multiTooltipTemplate: CHART_TOOLTIP_FORMAT, // Reference: http://stackoverflow.com/a/24622442
pointHitDetectionRadius: 5
};
//setChartScale(options, _MAX_EVENTS_PER_HOUR);
var ctx = $('<canvas id="chart_canvas" width="' + CHART_WIDTH + '" height="' + CHART_HEIGHT + '"></canvas>').appendTo('#chart_area')[0].getContext("2d");
var chart = new Chart(ctx);
var line = chart.Line(data, options);
chart.canvas.onclick = function(evt) {
var activePoints = line.getPointsAtEvent(evt);
if (activePoints.length > 0) {
var filter = '" ' + activePoints[0].label.replace(/[^0-9]/g, "") + ':"';
var table = $("#details").dataTable();
table.fnFilter(filter);
scrollTo("#details");
}
};
}
else if (type === "Trails") {
var data = [];
var other = 0;
var _ = {};
for (var type in TRAIL_TYPES)
_[type] = [];
var threshold = 0;
for (var i = 0; i < _TRAILS_SORTED.length; i++)
threshold += _TRAILS_SORTED[i][1];
threshold = threshold / 100;
for (var i = 0; i < _TRAILS_SORTED.length; i++) {
var item = _TRAILS_SORTED[i];
if (item[1] >= threshold) {
var match = item[0].match(/(.*?)\ \(([^)]+)\)/);
var type = match[2];
var count = item[1];
data.push({value: count, label: item[0], color: type in TRAIL_TYPES ? TRAIL_TYPES[type] : getHashColor(type)});
}
else
other += item[1];
}
if (other > 0)
data.push({ value: other, label: "Other (<1%)", color: OTHER_COLOR });
var pie = new d3pie("chart_area", {
header: {
title: {
fontSize: 1,
font: DEFAULT_FONT_FAMILY
},
subtitle: {
color: "#999999",
fontSize: 1,
font: DEFAULT_FONT_FAMILY
},
titleSubtitlePadding: 0
},
footer: {
color: "#999999",
fontSize: 1,
font: DEFAULT_FONT_FAMILY,
location: "bottom-left"
},
size: {
canvasHeight: CHART_HEIGHT,
canvasWidth: CHART_WIDTH
},
data: {
content: data
},
labels: {
inner: {
hideWhenLessThanPercentage: 101
},
mainLabel: {
font: DEFAULT_FONT_FAMILY,
fontSize: PIE_FONT_SIZE
},
percentage: {
color: "#ffffff",
font: DEFAULT_FONT_FAMILY,
decimalPlaces: 0
},
value: {
color: "#adadad",
font: DEFAULT_FONT_FAMILY,
fontSize: PIE_FONT_SIZE
},
lines: {
enabled: true
}
},
tooltips: {
enabled: true,
type: "placeholder",
string: "{label}: {value}, {percentage}%",
styles: {
font: DEFAULT_FONT_FAMILY
}
},
effects: {
load: {
speed: 500
},
pullOutSegmentOnClick: {
effect: "none"
}
},
misc: {
gradient: {
enabled: true,
percentage: 100
}
},
callbacks: {
onClickSegment: function(a) {
var filter = a.data.label.replace(/\(([A-Z]+)\)/g, "$1");
if (!filter.startsWith("Other")) {
var table = $("#details").dataTable();
table.fnFilter(filter);
scrollTo("#details");
}
}
}
});
}
else if (type === "Sources") {
var labels = [];
var values = [];
var maxValue = 0;
var datasets = {};
var options = {
scaleShowVerticalLines: false,
scaleShowHorizontalLines: false,
tooltipTemplate: CHART_TOOLTIP_FORMAT,
multiTooltipTemplate: CHART_TOOLTIP_FORMAT // Reference: http://stackoverflow.com/a/24622442
};
for (var key in TRAIL_TYPES)
datasets[key] = { fillColor: TRAIL_TYPES[key], label: key, data: [] };
for (var i = 0; i < _TOP_SOURCES.length; i++) {
labels.push(_TOP_SOURCES[i][0]);
maxValue = Math.max(_TOP_SOURCES[i][1], maxValue);
for (var key in TRAIL_TYPES)
datasets[key].data.push(_SOURCE_EVENTS[_TOP_SOURCES[i][0]][key]);
}
_ = [];
for (var key in TRAIL_TYPES) {
_.insert(0, datasets[key]); // StackedBar is drawing from last to first (for some strange reason)
}
var data = {
labels: labels,
datasets: _
};
var ctx = $('<canvas id="chart_canvas" width="' + CHART_WIDTH + '" height="' + CHART_HEIGHT + '"></canvas>').appendTo('#chart_area')[0].getContext("2d");
var chart = new Chart(ctx);
var bar = chart.StackedBar(data, options);
chart.canvas.onclick = function(evt) {
var activeBars = bar.getBarsAtEvent(evt);
if (activeBars.length > 0) {
var filter = activeBars[0].label;
if (filter.toLowerCase() != "other") {
var table = $("#details").dataTable();
table.fnFilter(filter);
scrollTo("#details");
}
}
};
}
else if (type === "Severity") {
var data = [];
for (var severity in _SEVERITY_COUNT) {
var count = _SEVERITY_COUNT[severity];
var color = SEVERITY_COLORS[severity];
var label = "";
for (var key in SEVERITY)
if (SEVERITY[key] == severity)
label = key.toLowerCase();
data.push({value: count, label: label, color: color})
}
var pie = new d3pie("chart_area", {
"header": {
"title": {
"fontSize": 1,
"font": DEFAULT_FONT_FAMILY
},
"subtitle": {
"color": "#999999",
"fontSize": 1,
"font": DEFAULT_FONT_FAMILY
},
"titleSubtitlePadding": 0
},
"footer": {
"color": "#999999",
"fontSize": 1,
"font": DEFAULT_FONT_FAMILY,
"location": "bottom-left"
},
"size": {
"canvasHeight": CHART_HEIGHT,
"canvasWidth": CHART_WIDTH
},
"data": {
"content": data
},
"labels": {
"inner": {
"hideWhenLessThanPercentage": 101
},
"mainLabel": {
"font": DEFAULT_FONT_FAMILY,
"fontSize": PIE_FONT_SIZE
},
"percentage": {
"color": "#ffffff",
"font": DEFAULT_FONT_FAMILY,
"decimalPlaces": 0
},
"value": {
"color": "#adadad",
"font": DEFAULT_FONT_FAMILY,
"fontSize": PIE_FONT_SIZE
},
"lines": {
"enabled": true
}
},
"tooltips": {
"enabled": true,
"type": "placeholder",
"string": "{label}: {value}, {percentage}%",
"styles": {
"font": DEFAULT_FONT_FAMILY
}
},
"effects": {
"load": {
"speed": 500
},
"pullOutSegmentOnClick": {
"effect": "none"
}
},
"misc": {
"gradient": {
"enabled": true,
"percentage": 100
}
},
"callbacks": {
onClickSegment: function(a) {
var filter = a.data.label;
var table = $("#details").dataTable();
table.fnFilter(filter);
scrollTo("#details");
}
}
});
}
else if (type === "Threats") {
var data = [];
var threshold = 0;
for (var i = 0; i < _THREATS_SORTED.length; i++)
threshold += _THREATS_SORTED[i][1];
threshold = threshold / 100;
var other = 0;
for (var i = 0; i < _THREATS_SORTED.length; i++) {
var item = _THREATS_SORTED[i];
if (item[1] >= threshold) {
//var threat = item[0];
var threat = item[0].replace(/\ \([^)]+\)/g, "");
var count = item[1];
var color = "#" + threat.substr(0, 6);
data.push({value: count, label: threat, color: color})
}
else
other += item[1];
}
if (other > 0)
data.push({value: other, label: "Other (<1%)", color: OTHER_COLOR})
var pie = new d3pie("chart_area", {
"header": {
"title": {
"fontSize": 1,
"font": DEFAULT_FONT_FAMILY
},
"subtitle": {
"color": "#999999",
"fontSize": 1,
"font": DEFAULT_FONT_FAMILY
},
"titleSubtitlePadding": 0
},
"footer": {
"color": "#999999",
"fontSize": 1,
"font": DEFAULT_FONT_FAMILY,
"location": "bottom-left"
},
"size": {
"canvasHeight": CHART_HEIGHT,
"canvasWidth": CHART_WIDTH
},
"data": {
"content": data
},
"labels": {
"inner": {
"hideWhenLessThanPercentage": 101
},
"mainLabel": {
"font": DEFAULT_FONT_FAMILY,
"fontSize": PIE_FONT_SIZE
},
"percentage": {
"color": "#ffffff",
"font": DEFAULT_FONT_FAMILY,
"decimalPlaces": 0
},
"value": {
"color": "#adadad",
"font": DEFAULT_FONT_FAMILY,
"fontSize": PIE_FONT_SIZE
},
"lines": {
"enabled": true
}
},
"tooltips": {
"enabled": true,
"type": "placeholder",
"string": "{label}: {value}, {percentage}%",
"styles": {
"font": DEFAULT_FONT_FAMILY
}
},
"effects": {
"load": {
"speed": 500
},
"pullOutSegmentOnClick": {
"effect": "none"
}
},
"misc": {
"gradient": {
"enabled": true,
"percentage": 100
}
},
"callbacks": {
onClickSegment: function(a) {
var filter = a.data.label.substr(0, 8);
if (!filter.startsWith("Other")) {
var table = $("#details").dataTable();
table.fnFilter(filter);
scrollTo("#details");
}
}
}
});
}
}
function initVisual() {
var sparklines = {}
var min_ = null;
var max_ = null;
var sliceColors = [];
var total = {};
var data = [];
var other = 0;
var severityMax = null;
var severityMaxName = null;
var severitySum = 0;
_MAX_EVENTS_PER_HOUR = 0;
_TRAILS_SORTED = _sort(_TRAILS);
for (var type in TRAIL_TYPES) {
sparklines[type] = [];
total[type] = 0;
}
// Severity sparkline
for (var severity in _SEVERITY_COUNT) {
if (_SEVERITY_COUNT[severity] * severity > severityMax) {
severityMax = _SEVERITY_COUNT[severity] * severity;
for (var key in SEVERITY)
if (SEVERITY[key] == severity)
severityMaxName = key.toLowerCase();
}
severitySum += _SEVERITY_COUNT[severity];
data.push(_SEVERITY_COUNT[severity]);
sliceColors.push(SEVERITY_COLORS[severity])
}
if (severitySum === 0) {
data = [1];
sliceColors = [OTHER_COLOR];
}
total["Severity"] = severityMaxName;
var options = { type: 'pie', sliceColors: sliceColors, minSpotColor: "", maxSpotColor: "", spotColor: "", highlightSpotColor: "", highlightLineColor: "", tooltipClassname: "", width: '30', height: '30', offset: -90, disableInteraction: true };
$("#severity_sparkline").sparkline(data, options);
// Trails sparkline
var threshold = 0;
for (var i = 0; i < _TRAILS_SORTED.length; i++)
threshold += _TRAILS_SORTED[i][1];
threshold = threshold / 100;
options.sliceColors = [];
data = [];
for (var i = 0; i < _TRAILS_SORTED.length; i++) {
if (_TRAILS_SORTED[i][1] >= threshold) {
var type = _TRAILS_SORTED[i][0].match(/\(([A-Z]+)\)/)[1];
data.push(_TRAILS_SORTED[i][1]);
options.sliceColors.push(type in TRAIL_TYPES ? TRAIL_TYPES[type] : getHashColor(type));
}
else
other += _TRAILS_SORTED[i][1];
}
if (other > 0) {
data.push(other);
options.sliceColors.push(OTHER_COLOR);
}
if (data.length === 0) {
data.push(1);
options.sliceColors.push(OTHER_COLOR);
}
total["Trails"] = _TRAILS_SORTED.length;
$("#trails_sparkline").sparkline(data, options);
// Threats sparkline
var _ = {};
for (var threat in _THREATS) {
var threatUID = getThreatUID(threat);
var count = _THREATS[threat][0];
var type = _THREATS[threat][4][LOG_COLUMNS.TYPE];
total[type] += count;
_[threatUID] = count;
}
_THREATS_SORTED = _sort(_);
var other = 0;
data = [];
options.sliceColors = [];
var threshold = 0;
for (var i = 0; i < _THREATS_SORTED.length; i++)
threshold += _THREATS_SORTED[i][1];
threshold = threshold / 100;
for (var i = 0; i < _THREATS_SORTED.length; i++) {
if (_THREATS_SORTED[i][1] >= threshold) {
data.push(_THREATS_SORTED[i][1]);
options.sliceColors.push("#" + _THREATS_SORTED[i][0].substr(0, 6));
}
else
other += _THREATS_SORTED[i][1];
}
if (other > 0) {
data.push(other);
options.sliceColors.push(OTHER_COLOR);
}
if (data.length === 0) {
data.push(1);
options.sliceColors.push(OTHER_COLOR);
}
total["Threats"] = _THREATS_SORTED.length;
$("#threats_sparkline").sparkline(data, options);
// Events sparklines
for (var hour in _HOURS) {
if (min_ === null)
min_ = hour;
else
min_ = Math.min(min_, hour);
if (max_ === null)
max_ = hour;
else
max_ = Math.max(max_, hour);
}
if ((min_ !== null) && (max_ !== null)) {
var ms = 60 * 60 * 1000;
min_ = dayStart(min_ * ms) / ms;
max_ = dayEnd(max_ * ms) / ms;
for (var hour = min_; hour <= max_; hour++) {
if (!(hour in _HOURS)) {
for (var key in sparklines)
sparklines[key].push(0);
_HOURS[hour] = {};
for (var type in TRAIL_TYPES)
_HOURS[hour][type] = 0;
}
else
for (var key in sparklines) {
_MAX_EVENTS_PER_HOUR = Math.max(_MAX_EVENTS_PER_HOUR, _HOURS[hour][key] | 0);
sparklines[key].push(_HOURS[hour][key] | 0);
}
}
for (var key in sparklines) {
for (var i = sparklines[key].length - 1; i >= 0; i--) {
if (sparklines[key][i] === 0)
sparklines[key][i] = null;
else
break;
}
}
}
else {
for (var key in sparklines) {
sparklines[key].push(0);
sparklines[key].push(0);
}
}
// Sources sparkline
_ = [];
_TOP_SOURCES = [];
total["Sources"] = Object.size(_SOURCES);
sorted = _sort(_SOURCES);
other = 0;
var top = {};
var ips = [];
var zero = [];
other_events = {};
for (var key in TRAIL_TYPES) {
zero.push(0);
other_events[key] = 0;
}
for (var i = 0; i < sorted.length; i++) {
if (i < MAX_SOURCES_ITEMS) {
top[sorted[i][0]] = sorted[i][1];
ips.push(sorted[i][0]);
}
else {
for (var key in TRAIL_TYPES)
other_events[key] += _SOURCE_EVENTS[sorted[i][0]][key];
other += sorted[i][1];
}
}
ips.sort(_ipCompareValues);
_ = [zero];
for (var i = 0; i < ips.length; i++) {
var _type_counts = [];
for (var key in TRAIL_TYPES)
_type_counts.push(_SOURCE_EVENTS[ips[i]][key] | 0);
_.push(_type_counts);
_TOP_SOURCES.push([ips[i], _type_counts]);
}
if (other > 0) {
var _other_counts = [];
for (var key in TRAIL_TYPES)
_other_counts.push(other_events[key] | 0);
_.push(_other_counts);
_TOP_SOURCES.push(["Other", other]);
_SOURCE_EVENTS["Other"] = other_events;
}
_.push(zero); // because of 0 value display problem
var barWidth = Math.min(6, Math.max(2, Math.floor((SPARKLINE_WIDTH / (_.length + 2)) - 1)));
var barColors = [];
for (var key in TRAIL_TYPES)
barColors.push(TRAIL_TYPES[key]);
$('#sources_sparkline').sparkline(_, { width: SPARKLINE_WIDTH, height: '30', type: 'bar', barColor: '#ffffff', barWidth: barWidth, disableInteraction: true, zeroColor: "rgba(0, 0, 0, 0)", stackedBarColor: barColors});
var options = { fillColor: false, minSpotColor: "", maxSpotColor: "", spotColor: "", highlightSpotColor: "", highlightLineColor: "", tooltipClassname: "", chartRangeMin: 0, chartRangeMax: _MAX_EVENTS_PER_HOUR, width: SPARKLINE_WIDTH, height: '30', lineWidth: 2 };
total["Events"] = 0;
var found = false;
for (var key in total) {
if (key.toUpperCase() === key) {
options.lineColor = TRAIL_TYPES[key];
$('#events_sparkline').sparkline(sparklines[key], options);
options.composite = true;
total["Events"] += total[key];
found = true;
}
}
if (!found) {
$('#events_sparkline').sparkline([], options);
}
else {
options.lineColor = "rgba(255, 255, 255, 0.25)";
options.lineWidth = 1;
$('#events_sparkline').sparkline([0, 0], options);
}
sum = 0;
$("[id$=_count]").each(function() {
sum += parseInt(total[$(this).attr("id").replace(/_count/, "").capitalizeFirstLetter()]) | 0;
});
for (var key in total)
$("#" + key.toLowerCase() + "_count").html((sum > 0) ? numberWithCommas(total[key]) : '-')
}
function timestamp(str){
return new Date(str).getTime();
}
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
function formatDate(value) {
return value.getFullYear() + "-" + pad(value.getMonth() + 1, 2) + "-" + pad(value.getDate(), 2);
};
function parseDate(value) {
var retval = new Date(0);
var match = value.match(/(\d{4})-(\d{2})-(\d{2})/);
if (match !== null)
retval = new Date(match[1], match[2] - 1, match[3]);
return retval;
};
function getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
function dayStart(tick_seconds) {
var value = new Date(tick_seconds);
value.setHours(0);
value.setMinutes(0);
value.setSeconds(0);
value.setMilliseconds(0);
return Math.floor(value.getTime());
}
function dayEnd(tick_seconds) {
var value = new Date(tick_seconds);
value.setHours(23);
value.setMinutes(59);
value.setSeconds(59);
value.setMilliseconds(999);
return Math.floor(value.getTime());
}
$(document).keyup(function(e){
var key = e.which || e.keyCode;
if (key === 37) { // left
$("#details_previous").click();
}
else if (key === 39) { // right
$("#details_next").click();
}
});
$(document).ready(function() {
var from = dayStart(new Date().getTime());
var to = dayStart(new Date().getTime());
if (getParameterByName("from"))
from = dayStart(parseDate(getParameterByName("from")));
if (getParameterByName("to"))
to = dayStart(parseDate(getParameterByName("to")));
});
function query(date1, date2) {
if (date2 === undefined) {
var url = location.origin + "/events?date=" + formatDate(date1);
init(url, date1);
}
else {
var d1, d2;
if (date1 < date2) {
d1 = date1; d2 = date2;
}
else {
d1 = date2; d2 = date1;
}
var url = location.origin + "/events?date=" + formatDate(d1) + "_" + formatDate(d2);
init(url, d1, d2);
}
}