history.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. document.addEventListener('DOMContentLoaded', main)
  2. function main() {
  3. getJSON("config.json").then( function (config) {
  4. moment.locale("de")
  5. var options = { worldCopyJump: true,
  6. zoomControl: false
  7. }
  8. var mapDiv = document.createElement("div")
  9. mapDiv.classList.add("map")
  10. document.body.insertBefore(mapDiv, document.body.firstChild)
  11. var map = L.map(mapDiv, options)
  12. var sidebar = mkSidebar(document.body)
  13. var infobox = new Infobox(sidebar)
  14. var gotoAnything = new gotoBuilder(config, infobox, showNodeinfo, showLinkinfo)
  15. var urls = [ config.dataPath + 'nodes.json',
  16. config.dataPath + 'graph.json'
  17. ]
  18. var p = Promise.all(urls.map(getJSON))
  19. p.then(handle_data(config, sidebar, infobox, map, gotoAnything))
  20. })
  21. }
  22. function handle_data(config, sidebar, infobox, map, gotoAnything) {
  23. return function (data) {
  24. var nodedict = data[0]
  25. var nodes = Object.keys(nodedict.nodes).map(function (key) { return nodedict.nodes[key] })
  26. nodes = nodes.filter( function (d) {
  27. return "firstseen" in d && "lastseen" in d
  28. })
  29. nodes.forEach( function(node) {
  30. node.firstseen = moment.utc(node.firstseen)
  31. node.lastseen = moment.utc(node.lastseen)
  32. })
  33. var now = moment()
  34. var age = moment(now).subtract(14, 'days')
  35. var newnodes = limit("firstseen", age, sortByKey("firstseen", nodes).filter(online))
  36. var lostnodes = limit("lastseen", age, sortByKey("lastseen", nodes).filter(offline))
  37. var onlinenodes = nodes.filter(online)
  38. var graph = data[1].batadv
  39. var graphnodes = data[0].nodes
  40. graph.nodes.forEach( function (d) {
  41. if (d.node_id in graphnodes)
  42. d.node = graphnodes[d.node_id]
  43. })
  44. graph.links.forEach( function (d) {
  45. if (graph.nodes[d.source].node)
  46. d.source = graph.nodes[d.source]
  47. else
  48. d.source = undefined
  49. if (graph.nodes[d.target].node)
  50. d.target = graph.nodes[d.target]
  51. else
  52. d.target = undefined
  53. })
  54. var links = graph.links.filter( function (d) {
  55. return d.source !== undefined && d.target !== undefined
  56. })
  57. links.forEach( function (d) {
  58. if (!("location" in d.source.node.nodeinfo && "location" in d.target.node.nodeinfo))
  59. return
  60. d.latlngs = []
  61. d.latlngs.push(L.latLng(d.source.node.nodeinfo.location.latitude, d.source.node.nodeinfo.location.longitude))
  62. d.latlngs.push(L.latLng(d.target.node.nodeinfo.location.latitude, d.target.node.nodeinfo.location.longitude))
  63. d.distance = d.latlngs[0].distanceTo(d.latlngs[1])
  64. })
  65. nodes.forEach( function (d) {
  66. d.neighbours = []
  67. })
  68. links.forEach( function (d) {
  69. d.source.node.neighbours.push({ node: d.target.node, link: d })
  70. d.target.node.neighbours.push({ node: d.source.node, link: d })
  71. })
  72. var markers = mkmap(map, sidebar, now, newnodes, lostnodes, onlinenodes, links, gotoAnything)
  73. gotoAnything.addMarkers(markers)
  74. showMeshstats(sidebar, nodes)
  75. mkNodesList(sidebar, config.showContact, "firstseen", gotoAnything.node, "Neue Knoten", newnodes)
  76. mkNodesList(sidebar, config.showContact, "lastseen", gotoAnything.node, "Verschwundene Knoten", lostnodes)
  77. mkLinkList(sidebar, gotoAnything.link, links)
  78. var historyDict = { nodes: {}, links: {} }
  79. nodes.forEach( function (d) {
  80. historyDict.nodes[d.nodeinfo.node_id] = d
  81. })
  82. links.forEach( function (d) {
  83. historyDict.links[linkId(d)] = d
  84. })
  85. gotoHistory(gotoAnything, historyDict, window.location.hash)
  86. window.onpopstate = function (d) {
  87. gotoHistory(gotoAnything, historyDict, d.state)
  88. }
  89. }
  90. }
  91. function mkSidebar(el) {
  92. var sidebar = document.createElement("div")
  93. sidebar.classList.add("sidebar")
  94. el.appendChild(sidebar)
  95. var button = document.createElement("button")
  96. sidebar.appendChild(button)
  97. button.classList.add("sidebarhandle")
  98. button.onclick = function () {
  99. sidebar.classList.toggle("hidden")
  100. }
  101. var container = document.createElement("div")
  102. container.classList.add("container")
  103. sidebar.appendChild(container)
  104. container.getWidth = function () {
  105. var small = window.matchMedia("(max-width: 60em)");
  106. return small.matches ? 0 : sidebar.offsetWidth
  107. }
  108. return container
  109. }
  110. function mkmap(map, sidebar, now, newnodes, lostnodes, onlinenodes, graph, gotoAnything) {
  111. function mkMarker(dict, iconFunc) {
  112. return function (d) {
  113. var opt = { icon: iconFunc(d),
  114. title: d.nodeinfo.hostname
  115. }
  116. var m = L.marker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], opt)
  117. m.on('click', gotoAnything.node(d, false))
  118. m.bindPopup(d.nodeinfo.hostname)
  119. dict[d.nodeinfo.node_id] = m
  120. return m
  121. }
  122. }
  123. var iconBase = { iconUrl: 'img/circlemarker.png',
  124. iconRetinaUrl: 'img/circlemarker@2x.png',
  125. iconSize: [17, 17],
  126. iconAnchor: [8, 8],
  127. popupAnchor: [0, -3]
  128. }
  129. var iconOnline = Object.assign({}, iconBase)
  130. iconOnline.className = "node-online"
  131. iconOnline = L.icon(iconOnline)
  132. var iconOffline = Object.assign({}, iconBase)
  133. iconOffline.className = "node-offline"
  134. iconOffline = L.icon(iconOffline)
  135. var iconNew = Object.assign({}, iconBase)
  136. iconNew.className = "node-new"
  137. iconNew = L.icon(iconNew)
  138. var iconOfflineAlert = Object.assign({}, iconBase)
  139. iconOfflineAlert.className = "node-offline node-alert"
  140. iconOfflineAlert = L.icon(iconOfflineAlert)
  141. L.control.zoom({ position: "topright" }).addTo(map)
  142. L.tileLayer("https://otile{s}-s.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg", {
  143. subdomains: "1234",
  144. type: "osm",
  145. attribution: "Map data Tiles &copy; <a href=\"https://www.mapquest.com/\" target=\"_blank\">MapQuest</a> <img src=\"https://developer.mapquest.com/content/osm/mq_logo.png\" />, Map data © OpenStreetMap contributors, CC-BY-SA",
  146. maxZoom: 18
  147. }).addTo(map)
  148. var markersDict = addLinksToMap(map, graph, gotoAnything)
  149. var nodes = newnodes.concat(lostnodes).filter(has_location)
  150. var markers = nodes.map(mkMarker(markersDict, function (d) {
  151. if (d.flags.online)
  152. return iconNew
  153. if (d.lastseen.isAfter(moment(now).subtract(1, 'days')))
  154. return iconOfflineAlert
  155. else
  156. return iconOffline
  157. }))
  158. var onlinemarkers = subtract(onlinenodes.filter(has_location), newnodes)
  159. .map(mkMarker(markersDict, function (d) { return iconOnline } ))
  160. var groupOnline = L.featureGroup(onlinemarkers).addTo(map)
  161. var group = L.featureGroup(markers).addTo(map)
  162. var bounds = group.getBounds()
  163. if (!bounds.isValid())
  164. bounds = groupOnline.getBounds()
  165. if (bounds.isValid())
  166. map.fitBounds(bounds, {paddingTopLeft: [sidebar.getWidth(), 0]})
  167. var funcDict = {}
  168. Object.keys(markersDict).map( function(k) {
  169. funcDict[k] = function (d) {
  170. var m = markersDict[k]
  171. var bounds
  172. if ("getBounds" in m)
  173. bounds = m.getBounds()
  174. else
  175. bounds = L.latLngBounds([m.getLatLng()])
  176. map.fitBounds(bounds, {paddingTopLeft: [sidebar.getWidth(), 0]})
  177. m.openPopup(bounds.getCenter())
  178. }
  179. })
  180. return funcDict
  181. }
  182. function addLinksToMap(map, graph, gotoAnything) {
  183. var markersDict = {}
  184. var scale = chroma.scale(chroma.interpolate.bezier(['green', 'yellow', 'red'])).domain([1, 5])
  185. graph = graph.filter( function (d) {
  186. return "distance" in d
  187. })
  188. var lines = graph.map( function (d) {
  189. var opts = { color: scale(d.tq).hex(),
  190. weight: 4
  191. }
  192. var line = L.polyline(d.latlngs, opts)
  193. line.bindPopup(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + "<br><strong>" + showDistance(d) + " / " + showTq(d) + "</strong>")
  194. line.on('click', gotoAnything.link(d, false))
  195. markersDict[linkId(d)] = line
  196. return line
  197. })
  198. var group = L.featureGroup(lines).addTo(map)
  199. return markersDict
  200. }
  201. function mkLinkList(el, gotoProxy, links) {
  202. if (links.length == 0)
  203. return
  204. var h2 = document.createElement("h2")
  205. h2.textContent = "Verbindungen"
  206. el.appendChild(h2)
  207. var table = document.createElement("table")
  208. var thead = document.createElement("thead")
  209. var tr = document.createElement("tr")
  210. var th1 = document.createElement("th")
  211. th1.textContent = "Knoten"
  212. tr.appendChild(th1)
  213. var th2 = document.createElement("th")
  214. th2.textContent = "TQ"
  215. tr.appendChild(th2)
  216. var th3 = document.createElement("th")
  217. th3.textContent = "Entfernung"
  218. th3.classList.add("sort-default")
  219. tr.appendChild(th3)
  220. thead.appendChild(tr)
  221. table.appendChild(thead)
  222. var tbody = document.createElement("tbody")
  223. links.forEach( function (d) {
  224. var row = document.createElement("tr")
  225. var td1 = document.createElement("td")
  226. var a = document.createElement("a")
  227. a.textContent = d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname
  228. a.href = "#"
  229. a.onclick = gotoProxy(d)
  230. td1.appendChild(a)
  231. row.appendChild(td1)
  232. if (d.vpn)
  233. td1.appendChild(document.createTextNode(" (VPN)"))
  234. var td2 = document.createElement("td")
  235. td2.textContent = showTq(d)
  236. row.appendChild(td2)
  237. var td3 = document.createElement("td")
  238. td3.textContent = showDistance(d)
  239. td3.setAttribute("data-sort", d.distance !== undefined ? -d.distance : 1)
  240. row.appendChild(td3)
  241. tbody.appendChild(row)
  242. })
  243. table.appendChild(tbody)
  244. new Tablesort(table)
  245. el.appendChild(table)
  246. }
  247. function mkNodesList(el, showContact, tf, gotoProxy, title, list) {
  248. if (list.length == 0)
  249. return
  250. var h2 = document.createElement("h2")
  251. h2.textContent = title
  252. el.appendChild(h2)
  253. var table = document.createElement("table")
  254. el.appendChild(table)
  255. var tbody = document.createElement("tbody")
  256. list.forEach( function (d) {
  257. var time = moment(d[tf]).fromNow()
  258. var row = document.createElement("tr")
  259. var td1 = document.createElement("td")
  260. var a = document.createElement("a")
  261. a.classList.add("hostname")
  262. a.classList.add(d.flags.online ? "online" : "offline")
  263. a.textContent = d.nodeinfo.hostname
  264. a.href = "#"
  265. a.onclick = gotoProxy(d)
  266. td1.appendChild(a)
  267. if (has_location(d)) {
  268. var span = document.createElement("span")
  269. span.classList.add("icon")
  270. span.classList.add("ion-location")
  271. td1.appendChild(span)
  272. }
  273. if ("owner" in d.nodeinfo && showContact) {
  274. var contact = d.nodeinfo.owner.contact
  275. td1.appendChild(document.createTextNode(" – " + contact + ""))
  276. }
  277. var td2 = document.createElement("td")
  278. td2.textContent = time
  279. row.appendChild(td1)
  280. row.appendChild(td2)
  281. tbody.appendChild(row)
  282. })
  283. table.appendChild(tbody)
  284. el.appendChild(table)
  285. }
  286. function showMeshstats(el, nodes) {
  287. var h2 = document.createElement("h2")
  288. h2.textContent = "Übersicht"
  289. el.appendChild(h2)
  290. var p = document.createElement("p")
  291. var totalNodes = sum(nodes.filter(online).map(one))
  292. var totalClients = sum(nodes.filter(online).map( function (d) {
  293. return d.statistics.clients
  294. }))
  295. var totalGateways = sum(nodes.filter(online).filter( function (d) {
  296. return d.flags.gateway
  297. }).map(one))
  298. p.textContent = totalNodes + " Knoten (online), " +
  299. totalClients + " Clients, " +
  300. totalGateways + " Gateways"
  301. p.appendChild(document.createElement("br"))
  302. p.appendChild(document.createTextNode("Diese Daten sind " + moment.utc(nodes.timestamp).fromNow(true) + " alt."))
  303. el.appendChild(p)
  304. }
  305. function Infobox(sidebar) {
  306. var self = this
  307. el = undefined
  308. function close() {
  309. destroy()
  310. pushHistory()
  311. }
  312. function destroy() {
  313. if (el && el.parentNode) {
  314. el.parentNode.removeChild(el)
  315. el = undefined
  316. }
  317. }
  318. self.create = function () {
  319. destroy()
  320. el = document.createElement("div")
  321. sidebar.insertBefore(el, sidebar.firstChild)
  322. el.scrollIntoView(false)
  323. el.classList.add("infobox")
  324. el.close = close
  325. el.destroy = destroy
  326. var closeButton = document.createElement("button")
  327. closeButton.classList.add("close")
  328. closeButton.onclick = close
  329. el.appendChild(closeButton)
  330. return el
  331. }
  332. return self
  333. }
  334. function showNodeinfo(config, infobox, gotoAnything, d) {
  335. var el = infobox.create()
  336. var h2 = document.createElement("h2")
  337. h2.textContent = d.nodeinfo.hostname
  338. var span = document.createElement("span")
  339. span.classList.add(d.flags.online ? "online" : "offline")
  340. span.textContent = " (" + (d.flags.online ? "online" : "offline, " + d.lastseen.fromNow(true)) + ")"
  341. h2.appendChild(span)
  342. el.appendChild(h2)
  343. var attributes = document.createElement("table")
  344. attributes.classList.add("attributes")
  345. attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null)
  346. attributeEntry(attributes, "In der Karte", has_location(d) ? "ja" : "nein")
  347. if (config.showContact)
  348. attributeEntry(attributes, "Kontakt", dictGet(d.nodeinfo, ["owner", "contact"]))
  349. attributeEntry(attributes, "Hardware", dictGet(d.nodeinfo, ["hardware", "model"]))
  350. attributeEntry(attributes, "Primäre MAC", dictGet(d.nodeinfo, ["network", "mac"]))
  351. attributeEntry(attributes, "Firmware", showFirmware(d))
  352. attributeEntry(attributes, "Uptime", showUptime(d))
  353. attributeEntry(attributes, "Teil des Netzes", showFirstseen(d))
  354. attributeEntry(attributes, "Arbeitsspeicher", showRAM(d))
  355. attributeEntry(attributes, "IP Adressen", showIPs(d))
  356. attributeEntry(attributes, "Clients", showClients(d))
  357. el.appendChild(attributes)
  358. if (d.neighbours.length > 0) {
  359. var h3 = document.createElement("h3")
  360. h3.textContent = "Nachbarknoten (" + d.neighbours.length + ")"
  361. el.appendChild(h3)
  362. var table = document.createElement("table")
  363. var thead = document.createElement("thead")
  364. var tr = document.createElement("tr")
  365. var th1 = document.createElement("th")
  366. th1.textContent = "Knoten"
  367. th1.classList.add("sort-default")
  368. tr.appendChild(th1)
  369. var th2 = document.createElement("th")
  370. th2.textContent = "TQ"
  371. tr.appendChild(th2)
  372. var th3 = document.createElement("th")
  373. th3.textContent = "Entfernung"
  374. tr.appendChild(th3)
  375. thead.appendChild(tr)
  376. table.appendChild(thead)
  377. var tbody = document.createElement("tbody")
  378. d.neighbours.forEach( function (d) {
  379. var tr = document.createElement("tr")
  380. var td1 = document.createElement("td")
  381. var a1 = document.createElement("a")
  382. a1.classList.add("hostname")
  383. a1.textContent = d.node.nodeinfo.hostname
  384. a1.href = "#"
  385. a1.onclick = gotoAnything.node(d.node)
  386. td1.appendChild(a1)
  387. if (d.link.vpn)
  388. td1.appendChild(document.createTextNode(" (VPN)"))
  389. if (has_location(d.node)) {
  390. var span = document.createElement("span")
  391. span.classList.add("icon")
  392. span.classList.add("ion-location")
  393. td1.appendChild(span)
  394. }
  395. tr.appendChild(td1)
  396. var td2 = document.createElement("td")
  397. var a2 = document.createElement("a")
  398. a2.href = "#"
  399. a2.textContent = showTq(d.link)
  400. a2.onclick = gotoAnything.link(d.link)
  401. td2.appendChild(a2)
  402. tr.appendChild(td2)
  403. var td3 = document.createElement("td")
  404. var a3 = document.createElement("a")
  405. a3.href = "#"
  406. a3.textContent = showDistance(d.link)
  407. a3.onclick = gotoAnything.link(d.link)
  408. td3.appendChild(a3)
  409. td3.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1)
  410. tr.appendChild(td3)
  411. tbody.appendChild(tr)
  412. })
  413. table.appendChild(tbody)
  414. new Tablesort(table)
  415. el.appendChild(table)
  416. }
  417. function showFirmware(d) {
  418. var release = dictGet(d.nodeinfo, ["software", "firmware", "release"])
  419. var base = dictGet(d.nodeinfo, ["software", "firmware", "base"])
  420. if (release === null || base === null)
  421. return
  422. return release + " / " + base
  423. }
  424. function showUptime(d) {
  425. if (!("uptime" in d.statistics))
  426. return
  427. return moment.duration(d.statistics.uptime, "seconds").humanize()
  428. }
  429. function showFirstseen(d) {
  430. if (!("firstseen" in d))
  431. return
  432. return d.firstseen.fromNow(true)
  433. }
  434. function showClients(d) {
  435. if (!d.flags.online)
  436. return
  437. return function (el) {
  438. el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine"))
  439. el.appendChild(document.createElement("br"))
  440. var span = document.createElement("span")
  441. span.classList.add("clients")
  442. span.textContent = " ".repeat(d.statistics.clients)
  443. el.appendChild(span)
  444. }
  445. }
  446. function showIPs(d) {
  447. var ips = dictGet(d.nodeinfo, ["network", "addresses"])
  448. if (ips === null)
  449. return
  450. ips.sort()
  451. return function (el) {
  452. ips.forEach( function (ip, i) {
  453. var link = !ip.startsWith("fe80:")
  454. if (i > 0)
  455. el.appendChild(document.createElement("br"))
  456. if (link) {
  457. var a = document.createElement("a")
  458. a.href = "http://[" + ip + "]/"
  459. a.textContent = ip
  460. el.appendChild(a)
  461. } else
  462. el.appendChild(document.createTextNode(ip))
  463. })
  464. }
  465. }
  466. function showRAM(d) {
  467. if (!("memory_usage" in d.statistics))
  468. return
  469. return function (el) {
  470. el.appendChild(showBar("memory-usage", d.statistics.memory_usage))
  471. }
  472. }
  473. }
  474. function attributeEntry(el, label, value) {
  475. if (value === null || value == undefined)
  476. return
  477. var tr = document.createElement("tr")
  478. var th = document.createElement("th")
  479. th.textContent = label
  480. tr.appendChild(th)
  481. var td = document.createElement("td")
  482. if (typeof value == "function")
  483. value(td)
  484. else
  485. td.appendChild(document.createTextNode(value))
  486. tr.appendChild(td)
  487. el.appendChild(tr)
  488. return td
  489. }
  490. function showBar(className, v) {
  491. var span = document.createElement("span")
  492. span.classList.add("bar")
  493. span.classList.add(className)
  494. var bar = document.createElement("span")
  495. bar.style.width = (v * 100) + "%"
  496. span.appendChild(bar)
  497. var label = document.createElement("label")
  498. label.textContent = (Math.round(v * 100)) + " %"
  499. span.appendChild(label)
  500. return span
  501. }
  502. function showLinkinfo(config, infobox, gotoAnything, d) {
  503. var el = infobox.create()
  504. var h2 = document.createElement("h2")
  505. a1 = document.createElement("a")
  506. a1.href = "#"
  507. a1.onclick = gotoAnything.node(d.source.node)
  508. a1.textContent = d.source.node.nodeinfo.hostname
  509. h2.appendChild(a1)
  510. h2.appendChild(document.createTextNode(" – "))
  511. a2 = document.createElement("a")
  512. a2.href = "#"
  513. a2.onclick = gotoAnything.node(d.target.node)
  514. a2.textContent = d.target.node.nodeinfo.hostname
  515. h2.appendChild(a2)
  516. el.appendChild(h2)
  517. var attributes = document.createElement("table")
  518. attributes.classList.add("attributes")
  519. attributeEntry(attributes, "TQ", showTq(d))
  520. attributeEntry(attributes, "Entfernung", showDistance(d))
  521. attributeEntry(attributes, "VPN", d.vpn ? "ja" : "nein")
  522. el.appendChild(attributes)
  523. }
  524. function pushHistory(d) {
  525. var s = "#!"
  526. if (d) {
  527. if ("node" in d)
  528. s += "n:" + d.node.nodeinfo.node_id
  529. if ("link" in d)
  530. s += "l:" + linkId(d.link)
  531. }
  532. window.history.pushState(s, undefined, s)
  533. }
  534. function gotoHistory(gotoAnything, dict, s) {
  535. if (!s.startsWith("#!"))
  536. return
  537. s = s.slice(2)
  538. var args = s.split(":")
  539. if (args[0] === "n") {
  540. var id = args[1]
  541. if (id in dict.nodes)
  542. gotoAnything.node(dict.nodes[id], true, false)()
  543. }
  544. if (args[0] === "l") {
  545. var id = args[1]
  546. if (id in dict.links)
  547. gotoAnything.link(dict.links[id], true, false)()
  548. }
  549. }
  550. function gotoBuilder(config, infobox, nodes, links) {
  551. var markers = {}
  552. var self = this
  553. function gotoNode(d, showMap, push) {
  554. showMap = trueDefault(showMap)
  555. push = trueDefault(push)
  556. if (showMap && d.nodeinfo.node_id in markers)
  557. markers[d.nodeinfo.node_id]()
  558. nodes(config, infobox, self, d)
  559. if (push)
  560. pushHistory( { node: d })
  561. return false
  562. }
  563. function gotoLink(d, showMap, push) {
  564. showMap = trueDefault(showMap)
  565. push = trueDefault(push)
  566. if (showMap && linkId(d) in markers)
  567. markers[linkId(d)]()
  568. links(config, infobox, self, d)
  569. if (push)
  570. pushHistory( { link: d })
  571. return false
  572. }
  573. function addMarkers(d) {
  574. markers = d
  575. }
  576. this.node = function (d, m, p) { return function () { return gotoNode(d, m, p) }}
  577. this.link = function (d, m, p) { return function () { return gotoLink(d, m, p) }}
  578. this.addMarkers = function (d) {
  579. markers = d
  580. }
  581. return this
  582. }