Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Presence #2

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.swp
*.DS_Store
node_modules
package-lock.json
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
{
user: '123', // User ID.
changes: 8, // Number of changes made by this user (for change detection).
presence: [ // List of sub-presence objects, per OT type.
{
type: 'rich-text', // The OT type for this presence object.
path: ['some', 'path'], // The path of this presence object.
subPresence: { // The type-specific presence object at this path.
u: '123', c: 8, // An example of an ot-rich-text presence object.
s: [ [ 1, 1 ], [ 5, 7 ], [ 9, 4 ] ]
}
},
{
type: 'text0', // An example of a text0 presence object.
path: ['some', 'other', 'path'],
subPresence: {
u: '123', c: 8,
s: [ [ 1, 1 ], [ 5, 7 ], [ 9, 4 ] ]
}
}
]
}
```


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:
Expand Down
109 changes: 109 additions & 0 deletions lib/json0-presence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const { transformCursor } = require('./text0');
const { convertFromText, canOpAffectPath } = require('./utils');

// Draws from https://github.com/Teamwork/ot-rich-text/blob/master/src/Operation.js
function isValidPresence(presence) {
if (
presence == null ||
typeof presence.u !== 'string' ||
typeof presence.c !== 'number' ||
!isFinite(presence.c) ||
Math.floor(presence.c) !== presence.c ||
!Array.isArray(presence.s)
) {
return false;
}

const selections = presence.s;

for (let i = 0, l = selections.length; i < l; ++i) {
const selection = selections[i];

if (
!Array.isArray(selection) ||
selection.length < 2 ||
selection[0] !== (selection[0] | 0) ||
selection[1] !== (selection[1] | 0)
) {
return false;
}
}

return true;
}

const defaultPresence = {u: '', c: 0, s: []};

const createPresence = presence =>
isValidPresence(presence) ? presence : defaultPresence;

// This needs more thinking/testing, looking a bit more carefully at
// how this is implemented in ot-rich-text, etc.
const comparePresence = function(pres1, pres2) {
// TODO add tests
// 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;
};

const transformPresence = (presence, op, isOwn) => {
for (let c of op){
if(c.si || c.sd){
convertFromText(c)
}
if(c.t === 'text0') {
//json.canOpAffectPath = function(op, path) {
presence = Object.assign({}, presence, {
s: presence.s.map(selection => {
const path = selection.slice(0, selection.length - 2);
if(canOpAffectPath(c, path)) {
const [start, end] = selection.slice(selection.length - 2);
return path.concat([
transformCursor(start, c.o),
transformCursor(end, c.o),
]);
}
return selection;
})
});
}
}

//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;
};

module.exports = {
isValidPresence,
createPresence,
comparePresence,
transformPresence,
};
58 changes: 9 additions & 49 deletions lib/json0.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
var presence = require('./json0-presence')
var utils = require('./utils');
var convertFromText = utils.convertFromText;
var convertToText = utils.convertToText;

/*
This is the implementation of the JSON OT type.

Expand Down Expand Up @@ -49,7 +54,9 @@ var clone = function(o) {
*/
var json = {
name: 'json0',
uri: 'http://sharejs.org/types/JSONv0'
uri: 'http://sharejs.org/types/JSONv0',
commonLengthForOps: utils.commonLengthForOps,
canOpAffectPath: utils.canOpAffectPath
};

// You can register another OT type as a subtype in a JSON document using
Expand Down Expand Up @@ -116,23 +123,6 @@ json.checkObj = function(elem) {
}
};

// helper functions to convert old string ops to and from subtype ops
function convertFromText(c) {
c.t = 'text0';
var o = {p: c.p.pop()};
if (c.si != null) o.i = c.si;
if (c.sd != null) o.d = c.sd;
c.o = [o];
}

function convertToText(c) {
c.p.push(c.o[0].p);
if (c.o[0].i != null) c.si = c.o[0].i;
if (c.o[0].d != null) c.sd = c.o[0].d;
delete c.t;
delete c.o;
}

json.apply = function(snapshot, op) {
json.checkValidOp(op);

Expand Down Expand Up @@ -369,36 +359,6 @@ json.normalize = function(op) {
return newOp;
};

// Returns the common length of the paths of ops a and b
json.commonLengthForOps = function(a, b) {
var alen = a.p.length;
var blen = b.p.length;
if (a.na != null || a.t)
alen++;

if (b.na != null || b.t)
blen++;

if (alen === 0) return -1;
if (blen === 0) return null;

alen--;
blen--;

for (var i = 0; i < alen; i++) {
var p = a.p[i];
if (i >= blen || p !== b.p[i])
return null;
}

return alen;
};

// Returns true if an op can affect the given path
json.canOpAffectPath = function(op, path) {
return json.commonLengthForOps({p:path}, op) != null;
};

// transform c so it applies to a document with otherC applied.
json.transformComponent = function(dest, c, otherC, type) {
c = clone(c);
Expand Down Expand Up @@ -659,5 +619,5 @@ require('./bootstrapTransform')(json, json.transformComponent, json.checkValidOp
var text = require('./text0');

json.registerSubtype(text);
module.exports = json;

module.exports = Object.assign(json, presence);
48 changes: 48 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// helper functions to convert old string ops to and from subtype ops
function convertFromText(c) {
c.t = 'text0';
var o = {p: c.p.pop()};
if (c.si != null) o.i = c.si;
if (c.sd != null) o.d = c.sd;
c.o = [o];
}

function convertToText(c) {
c.p.push(c.o[0].p);
if (c.o[0].i != null) c.si = c.o[0].i;
if (c.o[0].d != null) c.sd = c.o[0].d;
delete c.t;
delete c.o;
}

// Returns the common length of the paths of ops a and b
function commonLengthForOps(a, b) {
var alen = a.p.length;
var blen = b.p.length;
if (a.na != null || a.t)
alen++;

if (b.na != null || b.t)
blen++;

if (alen === 0) return -1;
if (blen === 0) return null;

alen--;
blen--;

for (var i = 0; i < alen; i++) {
var p = a.p[i];
if (i >= blen || p !== b.p[i])
return null;
}

return alen;
};

// Returns true if an op can affect the given path
function canOpAffectPath(op, path) {
return commonLengthForOps({p:path}, op) != null;
};

module.exports = { convertFromText, convertToText, commonLengthForOps, canOpAffectPath };
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -17,7 +17,7 @@
},
"repository": {
"type": "git",
"url": "git://github.com/ottypes/json0"
"url": "git://github.com/houshuang/json0"
},
"keywords": [
"ot",
Expand Down
Loading