diff --git a/README.md b/README.md index cc1de54..6314a10 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,42 @@ The JSON OT type can be used to edit arbitrary JSON documents. +It has been forked from https://github.com/ottypes/json0 and modified to add Presence. + +## Presence + +(inspired by https://github.com/Teamwork/ot-rich-text#presence) + +The shape of our presence data is as follows: + +``` +{ + u: '123', // user ID + c: 8, // number of changes made by this user + s: [ // list of selections + [ 1, 1 ], // collapsed selection + [ 5, 7 ], // forward selection + [ 9, 4 ] // backward selection + ] +} +``` + +Each selection listed in `s` ends with a 2-element array containing the selection start index and the selection end index. The elements in the array preceeding the last two represent the path of a `text0` entry within the `json0` data structure. + +For example, the following entry in the `s` array represents the user's cursor position within the `content` field (`data.content`): + +``` +['content', 2, 2] +``` + +We can access deeply nested entries with this structure as well. For example, the following `s` entry represents a text selection in `data.files[3].text`: + +``` +['files', 3, 'text', 4, 7] +``` + +The rest of the README content is from the original repo https://github.com/ottypes/json0. + ## Features The JSON OT type supports the following operations: diff --git a/lib/json0.js b/lib/json0.js index dc3a405..4b562e7 100644 --- a/lib/json0.js +++ b/lib/json0.js @@ -133,6 +133,63 @@ function convertToText(c) { delete c.o; } +// not checking anything here, we should probably check that u: exists +// (only thing we care about at json0 top level), and then delegate +// to any subtypes if there is already subtype presence data +json.createPresence = function(presence) { + return presence; +}; + +// this needs more thinking/testing, looking a bit more carefully at +// how this is implemented in ot-rich-text, etc. +json.comparePresence = function(pres1, pres2) { + if (!pres1 || !pres2) { + return false; + } + if (!pres1.p || !pres2.p) { + return false; + } + if (pres1.t !== pres2.t) { + return false; + } + if (pres1.t && subtypes[pres1.t]) { + if (pres1.p[0] === pres2.p[0]) { + return subtypes[pres1.t].comparePresence(pres1, pres2); + } + } else return pres1 === pres2; +}; + +// this is the key function, always run client-side, both on +// the client that creates a text-change, and on the clients +// that receive text-changes (ops). if there are no ops, just +// return presence, if there are ops, delegate to the subtype +// responsible for those ops (currently only ot-rich-text). +// I am making assumptions many places that all ops will be +// of the same subtype, not sure if this is a given. +// We're only concerned about the first level of object/array, +// not sure if the spec allows nesting of subtypes. +json.transformPresence = function(presence, op, isOwn) { + if (op.length < 1) { + return presence; + } + const representativeOp = op[0]; + const opType = op[0].t; + const path = representativeOp.p && representativeOp.p[0] + if (opType && subtypes[opType] && path) { + if (!presence.p || !presence.p[0] || presence.p[0] !== path) { + return presence + } + // return result of running the subtype's transformPresence, + // but add path and type, which the subtype will not include + presence = { + ...subtypes[opType].transformPresence(presence, op, isOwn), + p: op[0].p, + t: op[0].t + }; + } + return presence; +}; + json.apply = function(snapshot, op) { json.checkValidOp(op); diff --git a/package.json b/package.json index b6c9df6..3d98220 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "ot-json0", - "version": "1.1.0", + "name": "@houshuang/ot-json0", + "version": "1.2.0", "description": "JSON OT type", "main": "lib/index.js", "directories": { @@ -17,7 +17,7 @@ }, "repository": { "type": "git", - "url": "git://github.com/ottypes/json0" + "url": "git://github.com/houshuang/json0" }, "keywords": [ "ot",