Mercurial > code > home > repos > streamed-graph
changeset 36:8b4dc9e87b56
reindent to 2-spaces with prettier
author | drewp@bigasterisk.com |
---|---|
date | Sat, 28 Dec 2019 02:24:55 -0800 |
parents | 29d8ed02a275 |
children | 6a1d158d6ca4 |
files | .prettierrc src/index.ts src/json_ld_quads.test.ts src/json_ld_quads.ts src/streamed-graph.css src/streamed_graph_client.ts src/suffixLabels.ts |
diffstat | 7 files changed, 418 insertions(+), 379 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.prettierrc Sat Dec 28 02:24:55 2019 -0800 @@ -0,0 +1,3 @@ +{ + "tabWidth": 2 +} \ No newline at end of file
--- a/src/index.ts Sat Dec 28 02:01:23 2019 -0800 +++ b/src/index.ts Sat Dec 28 02:24:55 2019 -0800 @@ -1,107 +1,123 @@ - // these are just for timebank- move them out -import '@polymer/polymer/lib/elements/dom-bind.js'; +import "@polymer/polymer/lib/elements/dom-bind.js"; -import { PolymerElement, html } from '@polymer/polymer'; -import { render } from 'lit-html'; -import { N3Store } from "n3" -import { customElement, property, computed } from '@polymer/decorators'; +import { PolymerElement, html } from "@polymer/polymer"; +import { render } from "lit-html"; +import { N3Store } from "n3"; +import { customElement, property, computed } from "@polymer/decorators"; -import { GraphView } from './graph_view'; -import { StreamedGraphClient } from './streamed_graph_client'; -export { StreamedGraphClient } from './streamed_graph_client'; +import { GraphView } from "./graph_view"; +import { StreamedGraphClient } from "./streamed_graph_client"; +export { StreamedGraphClient } from "./streamed_graph_client"; -interface VersionedGraph { version: number, store: N3Store | undefined }; +interface VersionedGraph { + version: number; + store: N3Store | undefined; +} -@customElement('streamed-graph') +@customElement("streamed-graph") class StreamedGraph extends PolymerElement { - @property({ type: String }) - url: string = ''; + @property({ type: String }) + url: string = ""; - @property({ type: Object }) - graph!: VersionedGraph; + @property({ type: Object }) + graph!: VersionedGraph; - @property({ type: Boolean }) - expanded: boolean = false; + @property({ type: Boolean }) + expanded: boolean = false; - @computed('expanded') - get expandAction() { - return this.expanded ? '-' : '+'; - } + @computed("expanded") + get expandAction() { + return this.expanded ? "-" : "+"; + } - @property({ type: String }) - status: string = ''; + @property({ type: String }) + status: string = ""; - sg!: StreamedGraphClient; - graphView!: Element; - graphViewDirty = true; + sg!: StreamedGraphClient; + graphView!: Element; + graphViewDirty = true; - static get template() { - return html` - <link rel="stylesheet" href="../src/streamed-graph.css"> - <div id="ui"> - <span class="expander"><button on-click="toggleExpand">{{expandAction}}</button></span> - StreamedGraph <a href="{{url}}">[source]</a>: - {{status}} - </div> - <div id="graphView"></div>`; - } + static get template() { + return html` + <link rel="stylesheet" href="../src/streamed-graph.css" /> + <div id="ui"> + <span class="expander" + ><button on-click="toggleExpand">{{expandAction}}</button></span + > + StreamedGraph <a href="{{url}}">[source]</a>: {{status}} + </div> + <div id="graphView"></div> + `; + } - ready() { - super.ready(); - this.graph = { version: -1, store: undefined }; - this.graphView = (this.shadowRoot as ShadowRoot).getElementById("graphView") as Element; + ready() { + super.ready(); + this.graph = { version: -1, store: undefined }; + this.graphView = (this.shadowRoot as ShadowRoot).getElementById( + "graphView" + ) as Element; - this._onUrl(this.url); // todo: watch for changes and rebuild - if (this.expanded) { - this.redrawGraph(); - } + this._onUrl(this.url); // todo: watch for changes and rebuild + if (this.expanded) { + this.redrawGraph(); } + } - toggleExpand() { - this.expanded = !this.expanded; - if (this.expanded) { - this.redrawGraph() - } else { - this.graphViewDirty = false; - render(null, this.graphView); - } + toggleExpand() { + this.expanded = !this.expanded; + if (this.expanded) { + this.redrawGraph(); + } else { + this.graphViewDirty = false; + render(null, this.graphView); } + } - redrawGraph() { - this.graphViewDirty = true; - requestAnimationFrame(this._redrawLater.bind(this)); - } + redrawGraph() { + this.graphViewDirty = true; + requestAnimationFrame(this._redrawLater.bind(this)); + } - _onUrl(url: string) { - if (this.sg) { this.sg.close(); } - this.sg = new StreamedGraphClient( - url, - this.onGraphChanged.bind(this), - this.set.bind(this, 'status'), - [],//window.NS, - [] - ); + _onUrl(url: string) { + if (this.sg) { + this.sg.close(); } + this.sg = new StreamedGraphClient( + url, + this.onGraphChanged.bind(this), + this.set.bind(this, "status"), + [], //window.NS, + [] + ); + } - onGraphChanged() { - this.graph = { - version: this.graph.version + 1, - store: this.sg.store - }; - if (this.expanded) { - this.redrawGraph(); - } + onGraphChanged() { + this.graph = { + version: this.graph.version + 1, + store: this.sg.store + }; + if (this.expanded) { + this.redrawGraph(); } + } + + _redrawLater() { + if (!this.graphViewDirty) return; - _redrawLater() { - if (!this.graphViewDirty) return; - - if ((this.graph as VersionedGraph).store && this.graph.store) { - render(new GraphView(this.url, this.graph.store).makeTemplate(), this.graphView); - this.graphViewDirty = false; - } else { - render(html`<span>waiting for data...</span>`, this.graphView); - } + if ((this.graph as VersionedGraph).store && this.graph.store) { + render( + new GraphView(this.url, this.graph.store).makeTemplate(), + this.graphView + ); + this.graphViewDirty = false; + } else { + render( + html` + <span>waiting for data...</span> + `, + this.graphView + ); } -} \ No newline at end of file + } +}
--- a/src/json_ld_quads.test.ts Sat Dec 28 02:01:23 2019 -0800 +++ b/src/json_ld_quads.test.ts Sat Dec 28 02:24:55 2019 -0800 @@ -1,26 +1,29 @@ -import { eachJsonLdQuad } from './json_ld_quads'; -import { Literal, DataFactory } from 'n3'; +import { eachJsonLdQuad } from "./json_ld_quads"; +import { Literal, DataFactory } from "n3"; const { literal } = DataFactory; describe("eachJsonLdQuad", () => { - test("finds multiple graphs", () => { - }); - test("returns quads", async () => { - let results: Array<any> = []; - await eachJsonLdQuad([ + test("finds multiple graphs", () => {}); + test("returns quads", async () => { + let results: Array<any> = []; + await eachJsonLdQuad( + [ + { + "@id": "http://example.com/g1", + "@graph": [ { - "@id": "http://example.com/g1", - "@graph": [{ - "@id": "http://example.com/s1", - "http://example.com/p1": [{ "@value": "lit1" }] - }], + "@id": "http://example.com/s1", + "http://example.com/p1": [{ "@value": "lit1" }] } - ], (res: any) => results.push(res)); - expect(results).toHaveLength(1); - expect(results[0].subject.value).toEqual("http://example.com/s1"); - expect(results[0].predicate.value).toEqual("http://example.com/p1"); - expect((results[0].object as Literal).equals(literal("lit1"))).toBeTruthy(); - expect(results[0].graph.value).toEqual("http://example.com/g1"); - - }); -}); \ No newline at end of file + ] + } + ], + (res: any) => results.push(res) + ); + expect(results).toHaveLength(1); + expect(results[0].subject.value).toEqual("http://example.com/s1"); + expect(results[0].predicate.value).toEqual("http://example.com/p1"); + expect((results[0].object as Literal).equals(literal("lit1"))).toBeTruthy(); + expect(results[0].graph.value).toEqual("http://example.com/g1"); + }); +});
--- a/src/json_ld_quads.ts Sat Dec 28 02:01:23 2019 -0800 +++ b/src/json_ld_quads.ts Sat Dec 28 02:24:55 2019 -0800 @@ -1,46 +1,51 @@ import * as jsonld from "jsonld"; -import { JsonLd, JsonLdArray } from 'jsonld/jsonld-spec'; -import { Quad, NamedNode, DataFactory } from 'n3'; +import { JsonLd, JsonLdArray } from "jsonld/jsonld-spec"; +import { Quad, NamedNode, DataFactory } from "n3"; const { literal, quad, namedNode } = DataFactory; // const { rdf } = ns; const rdf = { type: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" }; function _emitQuad( - onQuad: (q: Quad) => void, - subjNode: NamedNode, - pred: string, - subj: any, - graphNode: NamedNode) { - let predNode: NamedNode; - if (pred === "@type") { - predNode = namedNode(rdf.type); - } - else { - predNode = namedNode(pred); - } - subj[pred as string].forEach(function (obj: any) { - const objNode = (obj['@id'] ? namedNode(obj['@id']) : - literal(obj['@value'], - obj['@language'] || obj['@type'])); - onQuad(quad(subjNode, predNode, objNode, graphNode)); - }); + onQuad: (q: Quad) => void, + subjNode: NamedNode, + pred: string, + subj: any, + graphNode: NamedNode +) { + let predNode: NamedNode; + if (pred === "@type") { + predNode = namedNode(rdf.type); + } else { + predNode = namedNode(pred); + } + subj[pred as string].forEach(function(obj: any) { + const objNode = obj["@id"] + ? namedNode(obj["@id"]) + : literal(obj["@value"], obj["@language"] || obj["@type"]); + onQuad(quad(subjNode, predNode, objNode, graphNode)); + }); } -export async function eachJsonLdQuad(jsonLdObj: object, onQuad: (q: Quad) => void) { - const expanded = await jsonld.expand(jsonLdObj); +export async function eachJsonLdQuad( + jsonLdObj: object, + onQuad: (q: Quad) => void +) { + const expanded = await jsonld.expand(jsonLdObj); - (expanded as JsonLdArray).forEach(function (g: JsonLd) { - var graph = (g as { '@id': string })['@id']; - var graphNode = namedNode(graph); - (g as { '@graph': JsonLdArray })['@graph'].forEach(function (subj: { [predOrId: string]: any; }) { - const subjNode = namedNode(subj['@id']); - for (let pred in subj) { - if (pred === '@id') { - continue; - } - _emitQuad(onQuad, subjNode, pred, subj, graphNode); - } - }); + (expanded as JsonLdArray).forEach(function(g: JsonLd) { + var graph = (g as { "@id": string })["@id"]; + var graphNode = namedNode(graph); + (g as { "@graph": JsonLdArray })["@graph"].forEach(function(subj: { + [predOrId: string]: any; + }) { + const subjNode = namedNode(subj["@id"]); + for (let pred in subj) { + if (pred === "@id") { + continue; + } + _emitQuad(onQuad, subjNode, pred, subj, graphNode); + } }); + }); }
--- a/src/streamed-graph.css Sat Dec 28 02:01:23 2019 -0800 +++ b/src/streamed-graph.css Sat Dec 28 02:24:55 2019 -0800 @@ -1,130 +1,141 @@ :host { - display: flex; - flex-direction: column; - padding: 2px 0; + display: flex; + flex-direction: column; + padding: 2px 0; } div#ui { - display: inline-block; - width: 30em; - border: 1px solid gray; - background: #BFBFBF; - color: black; + display: inline-block; + width: 30em; + border: 1px solid gray; + background: #bfbfbf; + color: black; +} +#ui button { + width: 2em; } -#ui button { width: 2em; } div#graphView { - } -div a { color: #007; } +div a { + color: #007; +} .expander { - display: inline-block; - padding: 3px; + display: inline-block; + padding: 3px; } /* graph view */ -@import url('https://fonts.googleapis.com/css?family=Allerta|Dosis|Jura&display=swap'); +@import url("https://fonts.googleapis.com/css?family=Allerta|Dosis|Jura&display=swap"); body.rdfBrowsePage { - background: black; - color: white; - font-family: 'Allerta', sans-serif; - font-size: 12px; + background: black; + color: white; + font-family: "Allerta", sans-serif; + font-size: 12px; } -body.rdfBrowsePage pre { - font-family: 'Allerta', sans-serif; +body.rdfBrowsePage pre { + font-family: "Allerta", sans-serif; } -body.rdfBrowsePage a { - color: #b1b1fd; - text-shadow: 1px 1px 0px #0400ff94; - text-decoration-color: #00007714; +body.rdfBrowsePage a { + color: #b1b1fd; + text-shadow: 1px 1px 0px #0400ff94; + text-decoration-color: #00007714; } section { - border: 1px solid gray; - padding: 4px; - font-family: 'Allerta', sans-serif; + border: 1px solid gray; + padding: 4px; + font-family: "Allerta", sans-serif; } .spoGrid { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } -.subject, .predicate { - display: flex; - align-items: baseline; +.subject, +.predicate { + display: flex; + align-items: baseline; } -.predicate, .object { - margin-left: 5px; +.predicate, +.object { + margin-left: 5px; } -.subject { border-top: 1px solid #2f2f2f; } +.subject { + border-top: 1px solid #2f2f2f; +} .literal { - border: 1px solid gray; - border-radius: 9px; - padding: 4px; - margin: 3px; + border: 1px solid gray; + border-radius: 9px; + padding: 4px; + margin: 3px; } -.subject > .node { border: 2px solid rgb(68, 141, 68); } +.subject > .node { + border: 2px solid rgb(68, 141, 68); +} .literalType { - vertical-align: super; - font-size: 80%; + vertical-align: super; + font-size: 80%; } .literal { - display: inline-block; - font-family: monospace; - font-size: 115%; + display: inline-block; + font-family: monospace; + font-size: 115%; } .resource { - display: inline-block; - background: lightblue; - border-radius: 6px; - padding: 1px 6px; - margin: 2px; + display: inline-block; + background: lightblue; + border-radius: 6px; + padding: 1px 6px; + margin: 2px; } .predicate > a { - color: #e49dfb; + color: #e49dfb; } .predicate > a::before { - content: "━"; - font-size: 125%; - font-weight: bolder; - padding-right: 2px; + content: "━"; + font-size: 125%; + font-weight: bolder; + padding-right: 2px; } .predicate > a::after { - content: "🠪"; + content: "🠪"; } -.comment { color: green; } +.comment { + color: green; +} table.typeBlock { - border-collapse: collapse; + border-collapse: collapse; } table.typeBlock th { - background: #1f1f1f; - border: 2px #333333 outset; + background: #1f1f1f; + border: 2px #333333 outset; } table.typeBlock td { - white-space: nowrap; - background: #2b2b2b; - border: 2px #4a4a4a outset; + white-space: nowrap; + background: #2b2b2b; + border: 2px #4a4a4a outset; } table.typeBlock td .literal { - padding-top: 1px; - padding-bottom: 1px; + padding-top: 1px; + padding-bottom: 1px; } .typeBlockScroll { - max-width: 100%; - overflow-x: auto; + max-width: 100%; + overflow-x: auto; } /* for my pages serving rdf data, not necessarily part of browse/ */ .served-resources { - margin-top: 4em; - border-top: 1px solid gray; - padding-top: 1em; + margin-top: 4em; + border-top: 1px solid gray; + padding-top: 1em; } .served-resources a { - padding-right: 2em; + padding-right: 2em; }
--- a/src/streamed_graph_client.ts Sat Dec 28 02:01:23 2019 -0800 +++ b/src/streamed_graph_client.ts Sat Dec 28 02:24:55 2019 -0800 @@ -1,128 +1,129 @@ // from /my/site/homepage/www/rdf/streamed-graph.js import { eachJsonLdQuad } from "./json_ld_quads"; -import { N3Store } from 'n3'; -import { Store } from 'n3'; +import { N3Store } from "n3"; +import { Store } from "n3"; export class StreamedGraphClient { - // holds a n3 Store, which is synced to a server-side - // store that sends patches over SSE + // holds a n3 Store, which is synced to a server-side + // store that sends patches over SSE - onStatus: (msg: string) => void; - onGraphChanged: () => void; - store: N3Store; - events!: EventSource; - constructor( - eventsUrl: string, - onGraphChanged: () => void, - onStatus: (status: string) => void, - prefixes: Array<Record<string, string>>, - staticGraphUrls: Array<string>) { - console.log('new StreamedGraph', eventsUrl); - this.onStatus = onStatus; - this.onGraphChanged = onGraphChanged; - this.onStatus('startup...'); + onStatus: (msg: string) => void; + onGraphChanged: () => void; + store: N3Store; + events!: EventSource; + constructor( + eventsUrl: string, + onGraphChanged: () => void, + onStatus: (status: string) => void, + prefixes: Array<Record<string, string>>, + staticGraphUrls: Array<string> + ) { + console.log("new StreamedGraph", eventsUrl); + this.onStatus = onStatus; + this.onGraphChanged = onGraphChanged; + this.onStatus("startup..."); - this.store = new Store(); + this.store = new Store(); + + // // Object.keys(prefixes).forEach((prefix) => { + // // this.store.setPrefix(prefix, prefixes[prefix]); + // // }); - // // Object.keys(prefixes).forEach((prefix) => { - // // this.store.setPrefix(prefix, prefixes[prefix]); - // // }); + this.connect(eventsUrl); + this.reconnectOnWake(); - this.connect(eventsUrl); - this.reconnectOnWake(); + // staticGraphUrls.forEach((url) => { + // fetch(url).then((response) => response.text()) + // .then((body) => { + // // parse with n3, add to output + // }); + // }); + } - // staticGraphUrls.forEach((url) => { - // fetch(url).then((response) => response.text()) - // .then((body) => { - // // parse with n3, add to output - // }); - // }); + reconnectOnWake() { + // it's not this, which fires on every mouse-in on a browser window, and doesn't seem to work for screen-turned-back-on + //window.addEventListener('focus', function() { this.connect(eventsUrl); }.bind(this)); + } - } + connect(eventsUrl: string) { + // need to exit here if this obj has been replaced - reconnectOnWake() { - // it's not this, which fires on every mouse-in on a browser window, and doesn't seem to work for screen-turned-back-on - //window.addEventListener('focus', function() { this.connect(eventsUrl); }.bind(this)); + this.onStatus("start connect..."); + this.close(); + if (this.events && this.events.readyState != EventSource.CLOSED) { + this.onStatus("zombie"); + throw new Error("zombie eventsource"); } - connect(eventsUrl: string) { - // need to exit here if this obj has been replaced + this.events = new EventSource(eventsUrl); - this.onStatus('start connect...'); - this.close(); - if (this.events && this.events.readyState != EventSource.CLOSED) { - this.onStatus('zombie'); - throw new Error("zombie eventsource"); - } - - this.events = new EventSource(eventsUrl); + this.events.addEventListener("error", ev => { + // todo: this is piling up tons of retries and eventually multiple connections + // this.testEventUrl(eventsUrl); + this.onStatus("connection lost- retrying"); + setTimeout(() => { + requestAnimationFrame(() => { + this.connect(eventsUrl); + }); + }, 3000); + }); - this.events.addEventListener('error', (ev) => { - // todo: this is piling up tons of retries and eventually multiple connections - // this.testEventUrl(eventsUrl); - this.onStatus('connection lost- retrying'); - setTimeout(() => { - requestAnimationFrame(() => { - this.connect(eventsUrl); - }); - }, 3000); - }); + this.events.addEventListener("fullGraph", async ev => { + this.onStatus("sync- full graph update"); + await this.replaceFullGraph((ev as MessageEvent).data); + this.onStatus(`synced ${this.store.size}`); + this.onGraphChanged(); + }); - this.events.addEventListener('fullGraph', async (ev) => { - this.onStatus('sync- full graph update'); - await this.replaceFullGraph((ev as MessageEvent).data); - this.onStatus(`synced ${this.store.size}`); - this.onGraphChanged(); - }); - - this.events.addEventListener('patch', async (ev) => { - this.onStatus('sync- updating'); - await this.patchGraph((ev as MessageEvent).data); - this.onStatus(`synced ${this.store.size}`); - this.onGraphChanged(); - }); - this.onStatus('connecting...'); - } + this.events.addEventListener("patch", async ev => { + this.onStatus("sync- updating"); + await this.patchGraph((ev as MessageEvent).data); + this.onStatus(`synced ${this.store.size}`); + this.onGraphChanged(); + }); + this.onStatus("connecting..."); + } - // these need some locks - async replaceFullGraph(jsonLdText: string) { - this.store = new Store(); - await eachJsonLdQuad(JSON.parse(jsonLdText), - this.store.addQuad.bind(this.store)); - } - - async patchGraph(patchJson: string) { - var patch = JSON.parse(patchJson).patch; + // these need some locks + async replaceFullGraph(jsonLdText: string) { + this.store = new Store(); + await eachJsonLdQuad( + JSON.parse(jsonLdText), + this.store.addQuad.bind(this.store) + ); + } - await eachJsonLdQuad(patch.deletes, - this.store.removeQuad.bind(this.store)); - await eachJsonLdQuad(patch.adds, - this.store.addQuad.bind(this.store)); - } + async patchGraph(patchJson: string) { + var patch = JSON.parse(patchJson).patch; - close() { - if (this.events) { - this.events.close(); - } + await eachJsonLdQuad(patch.deletes, this.store.removeQuad.bind(this.store)); + await eachJsonLdQuad(patch.adds, this.store.addQuad.bind(this.store)); + } + + close() { + if (this.events) { + this.events.close(); } + } - async testEventUrl(eventsUrl: string): Promise<void> { - return new Promise<void>((resolve, reject) => { - this.onStatus('testing connection'); - fetch(eventsUrl, { - method: "HEAD", - credentials: "include", - }).then((value) => { - if (value.status == 403) { - reject(); - return; - } - resolve(); - }).catch((err) => { - reject(); - }); + async testEventUrl(eventsUrl: string): Promise<void> { + return new Promise<void>((resolve, reject) => { + this.onStatus("testing connection"); + fetch(eventsUrl, { + method: "HEAD", + credentials: "include" + }) + .then(value => { + if (value.status == 403) { + reject(); + return; + } + resolve(); + }) + .catch(err => { + reject(); }); - } - -} \ No newline at end of file + }); + } +}
--- a/src/suffixLabels.ts Sat Dec 28 02:01:23 2019 -0800 +++ b/src/suffixLabels.ts Sat Dec 28 02:24:55 2019 -0800 @@ -1,78 +1,78 @@ -import { Term } from 'n3'; +import { Term } from "n3"; -type SuffixesNode = { usedBy?: string, children: Map<string, SuffixesNode> }; -type DisplayNode = { label?: string, link?: string }; +type SuffixesNode = { usedBy?: string; children: Map<string, SuffixesNode> }; +type DisplayNode = { label?: string; link?: string }; class SuffixLabels { - displayNodes: Map<string, DisplayNode>; - usedSuffixes: SuffixesNode; - constructor() { - this.displayNodes = new Map(); - this.usedSuffixes = { usedBy: undefined, children: new Map() }; + displayNodes: Map<string, DisplayNode>; + usedSuffixes: SuffixesNode; + constructor() { + this.displayNodes = new Map(); + this.usedSuffixes = { usedBy: undefined, children: new Map() }; + } + + planDisplayForNode(node: Term) { + const uri = node.value; + this._planDisplayForUri(uri); + } + + _planDisplayForUri(uri: string) { + if (this.displayNodes.has(uri)) { + return; } - planDisplayForNode(node: Term) { - const uri = node.value; - this._planDisplayForUri(uri); - }; + const segments = uri.split("/"); + let curs = this.usedSuffixes; + let label: string | undefined = undefined; - _planDisplayForUri(uri: string) { - if (this.displayNodes.has(uri)) { - return; - } + for (let i = segments.length - 1; i >= 0; i--) { + const seg = segments[i]; + if (curs.usedBy && curs.usedBy != uri) { + this._prependClashingUri(curs); + } - const segments = uri.split('/'); - let curs = this.usedSuffixes; - let label: string | undefined = undefined; + if (!curs.children.has(seg)) { + const child: SuffixesNode = { usedBy: undefined, children: new Map() }; + curs.children.set(seg, child); - for (let i = segments.length - 1; i >= 0; i--) { - const seg = segments[i]; - if (curs.usedBy && curs.usedBy != uri) { - this._prependClashingUri(curs); - } + if (label === undefined) { + label = SuffixLabels._tailSegments(uri, segments.length - i); + child.usedBy = uri; + } + } + curs = curs.children.get(seg)!; + } + this.displayNodes.set(uri, { label: label }); + } - if (!curs.children.has(seg)) { - const child: SuffixesNode = { usedBy: undefined, children: new Map() }; - curs.children.set(seg, child); - - if (label === undefined) { - label = SuffixLabels._tailSegments(uri, segments.length - i); - child.usedBy = uri; - } - } - curs = curs.children.get(seg)!; - } - this.displayNodes.set(uri, { label: label }); + _prependClashingUri(curs: SuffixesNode) { + // Claim: When a clash is discovered, only 1 uri needs to + // change its length, and there will be only one child node to + // follow, and the clashing uri can be changed to prepend that + // one child (since we'll see it again if that one wasn't + // enough). + const clashNode: DisplayNode = this.displayNodes.get(curs.usedBy!)!; + const nextLeftSeg = curs.children.entries().next().value; + if (nextLeftSeg[1].usedBy) { + throw new Error("unexpected"); } - _prependClashingUri(curs: SuffixesNode) { - // Claim: When a clash is discovered, only 1 uri needs to - // change its length, and there will be only one child node to - // follow, and the clashing uri can be changed to prepend that - // one child (since we'll see it again if that one wasn't - // enough). - const clashNode: DisplayNode = this.displayNodes.get(curs.usedBy!)!; - const nextLeftSeg = curs.children.entries().next().value; - if (nextLeftSeg[1].usedBy) { - throw new Error("unexpected"); - } + clashNode.label = nextLeftSeg[0] + "/" + clashNode.label; + nextLeftSeg[1].usedBy = curs.usedBy; + curs.usedBy = undefined; + } - clashNode.label = nextLeftSeg[0] + '/' + clashNode.label; - nextLeftSeg[1].usedBy = curs.usedBy; - curs.usedBy = undefined; + // a substring to show for this uri + getLabelForNode(node: string) { + return this.displayNodes.get(node)!.label; + } + + static _tailSegments(uri: string, n: number) { + let i = uri.length; + for (let rep = 0; rep < n; rep++) { + i = uri.lastIndexOf("/", i - 1); } - - // a substring to show for this uri - getLabelForNode(node: string) { - return this.displayNodes.get(node)!.label; - } + return uri.substr(i + 1); + } +} - static _tailSegments(uri: string, n: number) { - let i = uri.length; - for (let rep = 0; rep < n; rep++) { - i = uri.lastIndexOf('/', i - 1); - } - return uri.substr(i + 1); - } -}; - -export { SuffixLabels } +export { SuffixLabels };