+<!DOCTYPE html>
+<html>
+<head>
+ <title>dot map</title>
+
+ <script src="js/jquery-3.1.0.min.js"></script>
+ <script src="js/chart.umd.js"></script>
+ <script src="js/js.storage.min.js"></script>
+ <script src="js/kismet.ui.theme.js"></script>
+
+ <link rel="stylesheet" type="text/css" href="css/font-awesome.min.css">
+ <link rel="stylesheet" href="css/leaflet.css" />
+ <link rel="stylesheet" type="text/css" href="css/jquery.jspanel.min.css" />
+ <link rel="stylesheet" type="text/css" href="css/Control.Loading.css" />
+
+ <script src="js/leaflet.js"></script>
+ <script src="js/Leaflet.MultiOptionsPolyline.min.js"></script>
+ <script src="js/Control.Loading.js"></script>
+ <script src="js/chroma.min.js"></script>
+
+ <script src="js/js.storage.min.js"></script>
+ <script src="js/kismet.utils.js"></script>
+ <script src="js/kismet.units.js"></script>
+
+ <script src="js/datatables.min.js"></script>
+ <script src="js/dataTables.scrollResize.js"></script>
+
+ <script src="https://d3js.org/d3.v4.min.js"></script>
+
+ <style>
+
+ </style>
+</head>
+<body>
+ <div id="warning" class="warning">
+ <p><b>Warning!</b>
+ <p>To display the Russ map, your browser will connect to the Leaflet and Open Street Map servers to fetch the map tiles. This requires you have a functional Internet connection, and will reveal something about your location (the bounding region where planes have been seen.)
+ <p><input id="dontwarn" type="checkbox">Don't warn me again</input>
+ <p><button id="continue">Continue</button>
+ </div>
+ <div id="map"><svg style="width: 100%; height: 600px;"></svg></div>
+
+ <script>
+
+function getNeighbors(node) {
+ return links.reduce(function (neighbors, link) {
+ if (link.target.id === node.id) {
+ neighbors.push(link.source.id)
+ } else if (link.source.id === node.id) {
+ neighbors.push(link.target.id)
+ }
+ return neighbors
+ },
+ [node.id]
+ )
+}
+
+function isNeighborLink(node, link) {
+ return link.target.id === node.id || link.source.id === node.id
+}
+
+
+function getNodeColor(node, neighbors) {
+ if (Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1) {
+ return node.level === 1 ? 'blue' : 'green'
+ }
+
+ return node.level === 1 ? 'red' : 'gray'
+}
+
+function getLinkColor(node, link) {
+ return isNeighborLink(node, link) ? 'green' : '#E5E5E5'
+}
+
+function getTextColor(node, neighbors) {
+ return Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1 ? 'green' : 'white'
+}
+
+function selectNode(selectedNode) {
+ var neighbors = getNeighbors(selectedNode)
+
+ // we modify the styles to highlight selected nodes
+ nodeElements.attr('fill', function (node) { return getNodeColor(node, neighbors) })
+ textElements.attr('fill', function (node) { return getTextColor(node, neighbors) })
+ linkElements.attr('stroke', function (link) { return getLinkColor(selectedNode, link) })
+}
+
+ var window_visible = true;
+/*
+ // Visibility detection from https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
+ // Set the name of the hidden property and the change event for visibility
+ var hidden, visibilityChange;
+ if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
+ hidden = "hidden";
+ visibilityChange = "visibilitychange";
+ } else if (typeof document.msHidden !== "undefined") {
+ hidden = "msHidden";
+ visibilityChange = "msvisibilitychange";
+ } else if (typeof document.webkitHidden !== "undefined") {
+ hidden = "webkitHidden";
+ visibilityChange = "webkitvisibilitychange";
+ }
+
+ function handleVisibilityChange() {
+ if (document[hidden]) {
+ window_visible = false;
+ } else {
+ window_visible = true;
+ }
+ }
+
+ // Warn if the browser doesn't support addEventListener or the Page Visibility API
+ if (typeof document.addEventListener === "undefined" || hidden === undefined) {
+ ; // Do nothing
+ } else {
+ // Handle page visibility change
+ document.addEventListener(visibilityChange, handleVisibilityChange, false);
+ }
+*/
+ var urlparam = new URL(window.location.href);
+ var param_url = urlparam.searchParams.get('parent_url') + "/";
+ var param_prefix = urlparam.searchParams.get('local_uri_prefix', "");
+ var KISMET_PROXY_PREFIX = urlparam.searchParams.get('KISMET_PROXY_PREFIX', "");
+
+ if (param_prefix == 0)
+ param_prefix=""
+
+ var local_uri_prefix = param_url + param_prefix;
+ if (typeof(KISMET_URI_PREFIX) !== 'undefined')
+ local_uri_prefix = KISMET_URI_PREFIX;
+
+ var map_configured = false;
+
+ var markers = {};
+
+ var tid = -1;
+
+ var map = null;
+
+ function map_cb(d) {
+ data = kismet.sanitizeObject(d);
+
+ map_configured = true;
+ var nodes = d['nodes']
+ var links = d['links']
+
+ var width = window.innerWidth
+ var height = window.innerHeight
+
+ var svg = d3.select('svg')
+ .append("g")
+ svg.attr('width', width).attr('height', height)
+ .call(d3.zoom().on("zoom", function () {
+ svg.attr("transform", d3.event.transform)
+ }))
+ //.append("g")
+
+ // simulation setup with all forces
+ var linkForce = d3
+ .forceLink()
+ .id(function (link) { return link.id })
+ .strength(function (link) { return link.strength })
+
+ var simulation = d3
+ .forceSimulation()
+ //.force('link', linkForce)
+ .force('charge', d3.forceManyBody().strength(-10))
+ .force('center', d3.forceCenter(width / 2, height / 2))
+ .force('link', linkForce)
+
+ var dragDrop = d3.drag().on('start', function (node) {
+ node.fx = node.x
+ node.fy = node.y
+ }).on('drag', function (node) {
+ simulation.alphaTarget(0.7).restart()
+ node.fx = d3.event.x
+ node.fy = d3.event.y
+ }).on('end', function (node) {
+ if (!d3.event.active) {
+ simulation.alphaTarget(0)
+ }
+ node.fx = null
+ node.fy = null
+ })
+
+ var linkElements = svg.append("g")
+ .attr("class", "links")
+ .selectAll("line")
+ .data(links)
+ .enter().append("line")
+ .attr("stroke-width", 1)
+ .attr("stroke", "rgba(255, 255, 255, 5.0)")
+
+ var nodeElements = svg.append("g")
+ .attr("class", "nodes")
+ .selectAll("circle")
+ .data(nodes)
+ .enter().append("circle")
+ .attr("r", 10)
+ .attr("fill", getNodeColor)
+ .call(dragDrop)
+ .on('click', selectNode)
+
+ var textElements = svg.append("g")
+ .attr("class", "texts")
+ .selectAll("text")
+ .data(nodes)
+ .enter().append("text")
+ .text(function (node) { return node.label })
+ .attr("font-size", 15)
+ .attr("dx", 15)
+ .attr("dy", 4)
+
+ simulation.nodes(nodes).on('tick', () => {
+ nodeElements
+ .attr('cx', function (node) { return node.x })
+ .attr('cy', function (node) { return node.y })
+ textElements
+ .attr('x', function (node) { return node.x })
+ .attr('y', function (node) { return node.y })
+ linkElements
+ .attr('x1', function (link) { return link.source.x })
+ .attr('y1', function (link) { return link.source.y })
+ .attr('x2', function (link) { return link.target.x })
+ .attr('y2', function (link) { return link.target.y })
+ })
+
+ simulation.force("link").links(links)
+ }
+
+ var load_maps = kismet.getStorage('kismet.russ.maps_ok', false);
+
+ function poll_map() {
+ if (window_visible && !$('#map').is(':hidden') && load_maps) {
+ $.get(local_uri_prefix + KISMET_PROXY_PREFIX + "phy/DOT/map_data.json")
+ .done(function(d) {
+ console.log("sending to function");
+ map_cb(d);
+ });
+ //.always(function(d) {
+ // tid = setTimeout(function() { poll_map(); }, 2000);
+ //});
+ } else {
+ tid = setTimeout(function() { poll_map(); }, 2000);
+ }
+ }
+
+ // Set a global timeout
+ $.ajaxSetup({
+ timeout:5000,
+ xhrFields: {
+ withCredentials: true
+ }
+ });
+
+ if (load_maps)
+ $('#warning').hide();
+
+ $('#continue').on('click', function() {
+ if ($('#dontwarn').is(":checked"))
+ kismet.putStorage('kismet.russ.maps_ok', true);
+ $('#warning').hide();
+ load_maps = true;
+ });
+
+ poll_map();
+
+ </script>
+</body>
+</html>