Published
Edited
Sep 10, 2019
10 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
async function* messagesFromEditSocket(token, form, cancel) {
const gen = Generators.queue(notify => {
const {id, version} = form;
// Create WebSocket connection.
const socket = new WebSocket(`wss://ws.observablehq.com/document/${id}/edit`);
console.log(socket);
const msgHistory = [];
const object = {socket, msgHistory};
socket.onopen = () => {
// The server requires a "hello" message with a session token,
// as well as the local version number of the notebook.
console.log('read socket opened');
socket.send(`{"type":"hello","token":"${token}","version":${version}}`);
// The server acknowledges the client's "hello" by returning "hello".
// The server then sends a "save" message with an array "events"
// which contains all changes between the local version number and the server's version
// and if there are comments, it sends a "comments" message, containing the comment data.
notify(object);
};
socket.onmessage = (m) => {
console.log(m);
const parsed = JSON.parse(m.data);
// only add non-pong messages to history
if (parsed.type !== 'pong') {
msgHistory.push(parsed);
notify(object);
}
else console.log('received pong', m);
}
socket.onerror = (e) => notify(Promise.reject(new Error("socket error", e)));
// The Observable editor sends a "ping" to the server every 30 seconds or so to keep the socket open,
// and the server immediately responds with a "pong".
// If the server does not receive a "ping" or other message within 50 seconds or so, the socket gets closed.
// We can make do here with a ping every 40 seconds.
const ping = () => {
socket.send('{"type":"ping"}');
console.log('sent ping');
};
const pingInterval = setInterval(ping, 40000);
cancel.then(() => socket.close());
// On close
socket.onclose = () => {
console.log('read socket closed');
clearInterval(pingInterval);
notify(object);
};
return () => socket.close();
});
for await (const obj of gen) {
yield obj;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
function submitSaveMessage(token, form, message, currVersion) {
const {type, events} = message;
if (type !== 'save') {
console.log('not a save event');
return false;
}
const versions = events.map(({version}) => version);
const minVersions = Math.min(...versions);
if (minVersions !== currVersion+1) {
console.log('minimum version must be current version + 1');
return false;
}
if (versions.some((d,i) => d - minVersions !== i)) {
console.log('versions must increase one by one');
return false;
}
// open a new socket so that it's easier to keep the current state in sync
const {id} = form;
const socket = new WebSocket(`wss://ws.observablehq.com/document/${id}/edit`);
// say hello and send message
socket.onopen = () => {
console.log('write socket opened');
socket.send(`{"type":"hello","token":"${token}","version":${currVersion}}`);
socket.send(JSON.stringify(message));
console.log('message sent', message);
};
// close on confirmation or failure
socket.onmessage = (event) => {
console.log('write socket received msg:', event);
if (JSON.parse(event.data).type === 'saveconfirm' || JSON.parse(event.data).type === 'error') {
socket.close();
}
};
// bye
socket.onclose = (event) => {
console.log('write socket closed');
};
return true;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dragdata = {
resetOrder;
return nbCurrState.map(
({value, id,pinned}) => ({data:JSON.stringify(value).slice(0,200), cell:{value,id,pinned}}));
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more