define(["helper"], function (helper) { "use strict"; return function () { var self = this; var objects = {nodes: {}, links: {}}; var targets = []; var views = {}; var currentView; var currentObject; var running = false; function saveState() { var e = []; if (currentView) { e.push("v:" + currentView); } if (currentObject) { if ("node" in currentObject) { e.push("n:" + encodeURIComponent(currentObject.node.nodeinfo.node_id)); } if ("link" in currentObject) { e.push("l:" + encodeURIComponent(currentObject.link.id)); } } var s = "#!" + e.join(";"); window.history.pushState(s, undefined, s); } function resetView(push) { push = helper.trueDefault(push); targets.forEach(function (t) { t.resetView(); }); if (push) { currentObject = undefined; saveState(); } } function gotoNode(d, update) { if (!d) { return false; } targets.forEach(function (t) { t.gotoNode(d, update); }); return true; } function gotoLink(d, update) { if (!d) { return false; } targets.forEach(function (t) { t.gotoLink(d, update); }); return true; } function gotoLocation(d) { if (!d) { return false; } targets.forEach(function (t) { if (!t.gotoLocation) { console.warn("has no gotoLocation", t); } t.gotoLocation(d); }); return true; } function loadState(s, update) { if (!s) { return false; } s = decodeURIComponent(s); if (!s.startsWith("#!")) { return false; } var targetSet = false; s.slice(2).split(";").forEach(function (d) { var args = d.split(":"); if (update !== true && args[0] === "v" && args[1] in views) { currentView = args[1]; views[args[1]](); } var id; if (args[0] === "n") { id = args[1]; if (id in objects.nodes) { currentObject = {node: objects.nodes[id]}; gotoNode(objects.nodes[id], update); targetSet = true; } } if (args[0] === "l") { id = args[1]; if (id in objects.links) { currentObject = {link: objects.links[id]}; gotoLink(objects.links[id], update); targetSet = true; } } }); return targetSet; } self.start = function () { running = true; if (!loadState(window.location.hash)) { resetView(false); } window.onpopstate = function (d) { if (!loadState(d.state)) { resetView(false); } }; }; self.view = function (d) { if (d in views) { views[d](); if (!currentView || running) { currentView = d; } if (!running) { return; } saveState(); if (!currentObject) { resetView(false); return; } if ("node" in currentObject) { gotoNode(currentObject.node); } if ("link" in currentObject) { gotoLink(currentObject.link); } } }; self.node = function (d) { return function () { if (gotoNode(d)) { currentObject = {node: d}; saveState(); } return false; }; }; self.link = function (d) { return function () { if (gotoLink(d)) { currentObject = {link: d}; saveState(); } return false; }; }; self.gotoLocation = gotoLocation; self.reset = function () { resetView(); }; self.addTarget = function (d) { targets.push(d); }; self.removeTarget = function (d) { targets = targets.filter(function (e) { return d !== e; }); }; self.addView = function (k, d) { views[k] = d; }; self.setData = function (data) { objects.nodes = {}; objects.links = {}; data.nodes.all.forEach(function (d) { objects.nodes[d.nodeinfo.node_id] = d; }); data.graph.links.forEach(function (d) { objects.links[d.id] = d; }); }; self.update = function () { loadState(window.location.hash, true); }; return self; }; });