# HG changeset patch
# User drewp@bigasterisk.com
# Date 1575446955 28800
# Node ID a7ba8627a7b6190768b3df1bc2e153e02b6a8da9
# Parent 6cd3aaf431b6fa2d60fe06020355d566b294c0cc
still trying to make imports work. add other files too
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 package-lock.json
--- a/package-lock.json Tue Dec 03 21:48:36 2019 -0800
+++ b/package-lock.json Wed Dec 04 00:09:15 2019 -0800
@@ -3908,6 +3908,11 @@
"thenify-all": "^1.0.0"
}
},
+ "n3": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/n3/-/n3-1.3.4.tgz",
+ "integrity": "sha512-Eu5EVYGncuwiTlOV1J6p3OFBNSfI84D+fW0o8o5s2aRowO3yRcM4SvqPTOKzCCJutRvaXP0J9GIzwrP6tINm2Q=="
+ },
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 package.json
--- a/package.json Tue Dec 03 21:48:36 2019 -0800
+++ b/package.json Wed Dec 04 00:09:15 2019 -0800
@@ -9,7 +9,8 @@
"@webcomponents/webcomponentsjs": "^2.4.0",
"async": "^3.1.0",
"jsonld": "^1.8.1",
- "lit-html": "^1.1.2"
+ "lit-html": "^1.1.2",
+ "n3": "^1.3.4"
},
"devDependencies": {
"@types/node": "^12.12.14",
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 src/graph_view.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/graph_view.js Wed Dec 04 00:09:15 2019 -0800
@@ -0,0 +1,202 @@
+// from /my/site/homepage/www/rdf/browse/graphView.js
+
+
+import { SuffixLabels } from './suffixLabels.js';
+
+const groupByRdfType = (graph) => {
+ const env = graph.store.rdf;
+ const rdfType = env.createNamedNode('rdf:type');
+ const byType = new Map(); // type : subjs
+ const untyped = new Set(); // subjs
+ graph.quadStore.quads({}, (q) => {
+ let subjType = null;
+ graph.quadStore.quads({subject: q.subject,
+ predicate: rdfType},
+ (q2) => { subjType = q2.object; });
+ if (subjType){
+ subjType = subjType.toString();
+ if (!byType.has(subjType)) {
+ byType.set(subjType, new Set());
+ }
+ byType.get(subjType).add(q.subject.toString());
+ } else {
+ untyped.add(q.subject.toString());
+ }
+
+ });
+ return {byType: byType, untyped: untyped};
+};
+
+const graphView = (graph) => {
+ const env = graph.store.rdf;
+
+ const labels = new SuffixLabels();
+ graph.quadStore.quads({}, (q) => {
+ if (q.subject.interfaceName == "NamedNode") { labels.planDisplayForNode(q.subject); }
+ if (q.predicate.interfaceName == "NamedNode") { labels.planDisplayForNode(q.predicate); }
+ if (q.object.interfaceName == "NamedNode") { labels.planDisplayForNode(q.object); }
+ if (q.object.interfaceName == "Literal" && q.object.datatype) { labels.planDisplayForNode(env.createNamedNode(q.object.datatype)); }
+ });
+
+ const rdfNode = (n) => {
+ if (n.interfaceName == "Literal") {
+ let dtPart = "";
+ if (n.datatype) {
+ dtPart = html`
+ ^^
+ ${rdfNode(env.createNamedNode(n.datatype))}
+ `;
+ }
+ return html`${n.nominalValue}${dtPart}`;
+ }
+ if (n.interfaceName == "NamedNode") {
+ let dn = labels.getLabelForNode(n);
+ if (dn.match(/XMLSchema#.*/)) { dn = dn.replace('XMLSchema#', 'xsd:'); }
+ if (dn.match(/rdf-schema#.*/)) { dn = dn.replace('rdf-schema#', 'rdfs:'); }
+ return html`${dn}`;
+ }
+
+ return html`[${n.interfaceName} ${n.toNT()}]`;
+ }
+
+ const objBlock = (obj) => {
+ return html`
+
+ ${rdfNode(obj)}
+
+ `;
+ };
+
+ /// bunch of table rows
+ const predBlock = (subj, pred) => {
+ const objsSet = new Set();
+ graph.quadStore.quads({ subject: subj, predicate: pred }, (q) => {
+
+ if (q.object.length) {
+ console.log(q.object)
+ }
+ objsSet.add(q.object);
+ });
+ const objs = Array.from(objsSet.values());
+ objs.sort();
+ return html`
+ ${rdfNode(pred)}
+
+ ${objs.map(objBlock)}
+
+
+ `;
+ };
+
+ const {byType, untyped} = groupByRdfType(graph);
+ const typedSubjs = Array.from(byType.keys());
+ typedSubjs.sort();
+
+ const untypedSubjs = Array.from(untyped.values());
+ untypedSubjs.sort();
+
+ const subjBlock = (subj) => {
+ const subjNode = env.createNamedNode(subj);
+ const predsSet = new Set();
+ graph.quadStore.quads({ subject: subjNode }, (q) => {
+ predsSet.add(q.predicate);
+ });
+ const preds = Array.from(predsSet.values());
+ preds.sort();
+ return html`
+ ${rdfNode(subjNode)}
+
+
+ ${preds.map((p) => { return predBlock(subjNode, p); })}
+
+
+ `;
+ };
+ const byTypeBlock = (typeUri) => {
+ const subjs = Array.from(byType.get(typeUri));
+ subjs.sort();
+
+ const graphCells = new Map(); // [subj, pred] : objs
+ const preds = new Set();
+
+ subjs.forEach((subj) => {
+ graph.quadStore.quads({subject: env.createNamedNode(subj)}, (q) => {
+ preds.add(q.predicate.toString());
+ const cellKey = subj + '|||' + q.predicate.toString();
+ if (!graphCells.has(cellKey)) {
+ graphCells.set(cellKey, new Set());
+ }
+ graphCells.get(cellKey).add(q.object);
+ });
+ });
+ const predsList = Array.from(preds);
+ predsList.splice(predsList.indexOf('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), 1);
+ // also pull out label, which should be used on 1st column
+ predsList.sort();
+
+ const thead = () => {
+ const predColumnHead = (pred) => {
+ return html`${rdfNode(env.createNamedNode(pred))} | `;
+ };
+ return html`
+
+
+ |
+ ${predsList.map(predColumnHead)}
+
+ `;
+ };
+
+ const instanceRow = (subj) => {
+ const cell = (pred) => {
+ const objs = graphCells.get(subj + '|||' + pred);
+ if (!objs) {
+ return html` | `;
+ }
+ const objsList = Array.from(objs);
+ objsList.sort();
+ const draw = (obj) => {
+ return html`${rdfNode(obj)}
`
+ };
+ return html`${objsList.map(draw)} | `;
+ };
+
+ return html`
+
+ ${rdfNode(env.createNamedNode(subj))} |
+ ${predsList.map(cell)}
+
+ `;
+ };
+
+ return html`
+ [icon] ${rdfNode(env.createNamedNode(typeUri))} resources
+
+ `;
+ };
+
+ return html`
+
+
+
+
+
+
+
+ ${typedSubjs.map(byTypeBlock)}
+
+ ${untypedSubjs.map(subjBlock)}
+
+
+ `;
+}
+export { graphView }
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 src/json_ld_quads.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/json_ld_quads.ts Wed Dec 04 00:09:15 2019 -0800
@@ -0,0 +1,50 @@
+import { Quad, Node } from "./rdf_types";
+import * as jsonld from "jsonld";
+
+function quadFromExpandedStatement(rdfEnv: any, subj: any, pred: string, obj: any, graphNode: any): Quad {
+ return {
+ subject: rdfEnv.createNamedNode(subj['@id']),
+ predicate: rdfEnv.createNamedNode(pred),
+ object: (obj['@id'] ? rdfEnv.createNamedNode(obj['@id']) :
+ rdfEnv.createLiteral(obj['@value'], obj['@language'], obj['@type'])),
+ graph: graphNode,
+ };
+}
+function quadFromTypeStatement(rdfEnv: any, subj: any, obj: any, graphNode: any): Quad {
+ return {
+ subject: rdfEnv.createNamedNode(subj['@id']),
+ predicate: rdfEnv.createNamedNode('rdf:type'),
+ object: rdfEnv.createNamedNode(obj),
+ graph: graphNode,
+ };
+}
+
+export function eachJsonLdQuad(rdfEnv: any, jsonLdObj: object, onQuad: (Quad) => void, done: () => void) {
+ jsonld.expand(jsonLdObj, function onExpand(err, expanded) {
+ if (err) {
+ throw new Error();
+ }
+ (expanded as [object]).forEach(function (g) {
+ var graph = g['@id'];
+ var graphNode = rdfEnv.createNamedNode(graph) as Node;
+ g['@graph'].forEach(function (subj) {
+ for (let pred in subj) {
+ if (pred.match(/^[^@]/)) {
+ subj[pred].forEach(function (obj) {
+ onQuad(quadFromExpandedStatement(rdfEnv, subj, pred, obj, graphNode));
+ });
+ }
+ else {
+ if (pred === "@type") {
+ subj[pred].forEach((obj) => {
+ onQuad(quadFromTypeStatement(rdfEnv, subj, obj, graphNode));
+ });
+ }
+ }
+ }
+ });
+ });
+ done();
+ });
+}
+;
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 src/rdf_types.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rdf_types.ts Wed Dec 04 00:09:15 2019 -0800
@@ -0,0 +1,7 @@
+export type Node = any; // todo
+export type Quad = {
+ subject: Node;
+ predicate: Node;
+ object: Node;
+ graph: Node,
+};
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 src/streamed-graph.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streamed-graph.ts Wed Dec 04 00:09:15 2019 -0800
@@ -0,0 +1,91 @@
+
+// these are just for timebank- move them out
+import '@polymer/polymer/lib/elements/dom-bind.js';
+
+
+import { PolymerElement, html } from '@polymer/polymer';
+import { customElement, property, computed } from '@polymer/decorators';
+import { render } from 'lit-html';
+// import { graphView } from '/rdf/browse/graphView.js';
+import { StreamedGraphClient } from './streamed_graph_client';
+
+@customElement('streamed-graph')
+class StreamedGraph extends PolymerElement {
+ @property({ type: String })
+ url: string = '';
+
+ @property({ type: Object })
+ graph: Object;
+
+ @property({ type: Boolean })
+ expanded: Boolean = false;
+
+ @computed('expanded')
+ get expandAction() {
+ return this.expanded ? '-' : '+';
+ }
+
+ @property({ type: String })
+ status: String = '';
+
+ sg: StreamedGraphClient;
+ graphView: Element;
+ graphViewDirty = true;
+
+ static get template() {
+ return html`
+
+
+
+ StreamedGraph
[source]:
+ {{status}}
+
+ graph here
+
`;
+ }
+
+ ready() {
+ super.ready();
+ this.graphView = this.shadowRoot.getElementById("graphView");
+ }
+
+ toggleExpand(ev) {
+ 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));
+ }
+
+ _onUrl(url) {
+ // 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, graph: this.sg };
+ if (this.expanded) {
+ this.redrawGraph();
+ }
+ }
+
+ _redrawLater() {
+ if (!this.graphViewDirty) return;
+ //render(graphView(this.graph.graph), this.graphView);
+ this.graphViewDirty = false;
+ }
+
+
+}
\ No newline at end of file
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 src/streamed_graph_client.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streamed_graph_client.ts Wed Dec 04 00:09:15 2019 -0800
@@ -0,0 +1,168 @@
+// from /my/site/homepage/www/rdf/streamed-graph.js
+
+import * as async from "async";
+import * as jsonld from "jsonld";
+
+//import eachJsonLdQuad from "./json_ld_quads";
+import { Store, DataFactory } from "n3"
+
+///
+const EventSource = window.EventSource;
+
+export class StreamedGraphClient {
+ // onStatus: (msg: string) => void;
+ // onGraphChanged: () => void;
+ // store: Store;
+ // events: EventSource;
+ constructor(
+ eventsUrl: string,
+ onGraphChanged: () => void,
+ onStatus: (status: string) => void,
+ prefixes: Array>,
+ staticGraphUrls: Array) {
+ console.log('new StreamedGraph', eventsUrl);
+ // holds a rdfstore.js store, which is synced to a server-side
+ // // store that sends patches over SSE
+ // this.onStatus = onStatus;
+ // this.onGraphChanged = onGraphChanged;
+ // this.onStatus('startup...');
+
+ // this.store = new Store({});
+
+ // // Object.keys(prefixes).forEach((prefix) => {
+ // // this.store.setPrefix(prefix, prefixes[prefix]);
+ // // });
+
+ // this.connect(eventsUrl);
+ // this.reconnectOnWake();
+
+ // 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
+
+ // 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('fullGraph', (ev) => {
+ // // this.updates.push({ type: 'fullGraph', data: ev.data });
+ // // this.flushUpdates();
+ // });
+
+ // this.events.addEventListener('patch', (ev) => {
+ // // this.updates.push({ type: 'patch', data: ev.data });
+ // // this.flushUpdates();
+ // });
+ // this.onStatus('connecting...');
+ // }
+
+ // replaceFullGraph(jsonLdText: string, done: () => void) {
+ // // this.quadStore.clear();
+ // // eachJsonLdQuad(this.store.rdf, JSON.parse(jsonLdText),
+ // // this.quadStore.add.bind(this.quadStore), function () {
+ // // done();
+ // // });
+ // // or this.store.insert([quad], quad.graph, function() {});
+ // }
+
+
+ // patchGraph(patchJson: string, done: () => void) {
+ // var patch = JSON.parse(patchJson).patch;
+
+ // // if (!this.store) {
+ // // throw new Error('store ' + this.store);
+ // // }
+
+ // async.series([
+ // // (done) => {
+ // // eachJsonLdQuad(this.store.rdf, patch.deletes,
+ // // this.quadStore.remove.bind(this.quadStore), done);
+ // // },
+ // (done) => {
+ // // eachJsonLdQuad(this.store.rdf, patch.adds,
+ // // this.quadStore.add.bind(this.quadStore), done);
+ // },
+ // /* seriesDone */ (done) => {
+ // done();
+ // }
+ // ], done);
+ // }
+ // close() {
+ // if (this.events) {
+ // this.events.close();
+ // }
+ // }
+
+ // testEventUrl(eventsUrl: string): Promise {
+ // return new Promise((resolve, reject) => {
+ // this.onStatus('testing connection');
+ // fetch(eventsUrl, {
+ // method: "HEAD",
+ // credentials: "include",
+ // }).then((value) => {
+ // if (value.status == 403) {
+ // reject();
+ // return;
+ // }
+ // resolve();
+ // }).catch((err) => {
+ // reject();
+ // });
+ // });
+ // }
+
+ // flushOneUpdate(update: Update, done: () => void) {
+ // if (update.type == 'fullGraph') {
+ // this.onStatus('sync- full graph update');
+ // let onReplaced = () => {
+ // this.onStatus('synced');
+ // this.onGraphChanged();
+ // done();
+ // };
+ // this.replaceFullGraph(update.data, onReplaced);
+ // } else if (update.type == 'patch') {
+ // this.onStatus('sync- updating');
+ // let onPatched = () => {
+ // this.onStatus('synced');
+ // this.onGraphChanged();
+ // done();
+ // };
+ // this.patchGraph(update.data, onPatched);
+ // } else {
+ // this.onStatus('sync- unknown update');
+ // throw new Error(update.type);
+ // }
+ // }
+
+}
\ No newline at end of file
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 src/suffixLabels.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/suffixLabels.js Wed Dec 04 00:09:15 2019 -0800
@@ -0,0 +1,72 @@
+class SuffixLabels {
+ constructor() {
+ this.displayNodes = new Map(); // internal string : { label, link }
+ this.usedSuffixes = {usedBy: null, children: new Map()};
+ }
+
+ planDisplayForNode(node) {
+ const uri = node.nominalValue;
+ this._planDisplayForUri(uri);
+ };
+
+ _planDisplayForUri(uri) {
+ if (this.displayNodes.has(uri)) {
+ return;
+ }
+
+ const segments = uri.split('/');
+ let curs = this.usedSuffixes;
+ let label = null;
+
+ for (let i = segments.length - 1; i >= 0; i--) {
+ const seg = segments[i];
+ if (curs.usedBy && curs.usedBy != uri) {
+ this._prependClashingUri(curs);
+ }
+
+ if (!curs.children.has(seg)) {
+ const child = {usedBy: null, children: new Map()};
+ curs.children.set(seg, child);
+
+ if (label === null ) {
+ label = SuffixLabels._tailSegments(uri, segments.length - i);
+ child.usedBy = uri;
+ }
+ }
+ curs = curs.children.get(seg);
+ }
+ this.displayNodes.set(uri, {label: label});
+ }
+
+ _prependClashingUri(curs) {
+ // 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 = 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 = null;
+
+ }
+
+ getLabelForNode(node) {
+ return this.displayNodes.get(node.nominalValue).label;
+ }
+
+ static _tailSegments(uri, n) {
+ let i = uri.length;
+ for (let rep = 0; rep < n; rep++) {
+ i = uri.lastIndexOf('/', i - 1);
+ }
+ return uri.substr(i + 1);
+ }
+};
+
+export { SuffixLabels }
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 src/suffixLabels_test.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/suffixLabels_test.html Wed Dec 04 00:09:15 2019 -0800
@@ -0,0 +1,91 @@
+
+
+
+ suffixLabels_test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 tsconfig.json
--- a/tsconfig.json Tue Dec 03 21:48:36 2019 -0800
+++ b/tsconfig.json Wed Dec 04 00:09:15 2019 -0800
@@ -5,11 +5,13 @@
"target": "es6",
"rootDirs": [
"node_modules",
+ "src"
],
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": [
"./src/streamed-graph.ts",
+ "./src/streamed_graph_client.ts"
]
}
diff -r 6cd3aaf431b6 -r a7ba8627a7b6 webpack-dev.config.ts
--- a/webpack-dev.config.ts Tue Dec 03 21:48:36 2019 -0800
+++ b/webpack-dev.config.ts Wed Dec 04 00:09:15 2019 -0800
@@ -5,6 +5,8 @@
mode: "development",
entry: [
'./src/streamed-graph.ts',
+ './src/streamed_graph_client.ts',
+
'./src/streamed-graph.css' // doesn't emit anything
],
output: {