define(function () { 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 = trueDefault(push); targets.forEach( function (t) { t.resetView(); }); if (push) { currentObject = undefined; saveState(); } } function gotoNode(d) { if (!d) return false; targets.forEach( function (t) { t.gotoNode(d); }); return true; } function gotoLink(d) { if (!d) return false; targets.forEach( function (t) { t.gotoLink(d); }); 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) { 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 (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]); targetSet = true; } } if (args[0] === "l") { id = args[1]; if (id in objects.links) { currentObject = { link: objects.links[id] }; gotoLink(objects.links[id]); 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; }); }; return self; }; });