changesets
Advanced tools
Comparing version 0.1.2 to 0.1.3
exports.text = require('./text/index') | ||
exports.Changeset = require('./Changeset') | ||
exports.Operation = require('./Changeset') | ||
exports.Operation = require('./Operation') |
@@ -26,3 +26,3 @@ /*! | ||
var Changeset = require('../Changeset') | ||
var Changeset = require('./Changeset') | ||
, Delete = require('./operations/Delete') | ||
@@ -36,2 +36,3 @@ , Insert = require('./operations/Insert') | ||
exports.Changeset = Changeset | ||
@@ -68,1 +69,18 @@ /** | ||
} | ||
/** | ||
* Serializes the given changeset in order to return a (hopefully) more compact representation | ||
* that can be sent through a network or stored in a database | ||
* @alias cs.text.Changeset#pack | ||
*/ | ||
exports.pack = function(cs) { | ||
return cs.pack() | ||
} | ||
/** | ||
* Unserializes the output of cs.text.pack | ||
* @alias cs.text.Changeset.unpack | ||
*/ | ||
exports.unpack = function(packed) { | ||
return Changeset.unpack(packed) | ||
} |
@@ -49,3 +49,3 @@ /*! | ||
, Equal = require('./Equal') | ||
, Changeset = require('../../Changeset') | ||
, Changeset = require('../Changeset') | ||
@@ -52,0 +52,0 @@ |
{ | ||
"name": "changesets", | ||
"version": "0.1.2", | ||
"version": "0.1.3", | ||
"description": "A Changeset library incorporating an operational transformation (OT) algorithm.", | ||
@@ -23,3 +23,5 @@ "repository": { | ||
"main": "./lib/index", | ||
"test": "vows ./test/*", | ||
"scripts": { | ||
"test": "vows ./test/*" | ||
}, | ||
"dependencies": { | ||
@@ -26,0 +28,0 @@ "diff_match_patch": "*" |
| ||
# changesets | ||
This is library allows you to build text-based concurrent multi-user applications. | ||
build text-based concurrent multi-user applications using operational transformation! | ||
It was built with the following requirements in mind: | ||
* intention preservation | ||
* reversibility/invertibility (undo effect) | ||
* convergence | ||
*changesets* allows you to easily create changesets and apply them on all sites of a distributed system using Operational Transformation. It was built with the following requirements in mind: | ||
Easily create changesets and apply them on all sites of a distributed system using Operational Transformation. | ||
* intention preservation (no content corruption; your edits always have the same effect) | ||
* reversibility/invertibility (undo any edit without corrupting the content or the state) | ||
* convergence (everybody sees the same state) | ||
Note: While, at the current stage of development, this library only implements a text-based changeset solution, I intend to add functionality for tree-based data and at some point in the future maybe even images. If you would like to help, feel free to contact me. | ||
### Oppositional what?! | ||
In case the above question just came to your mind, you better start with [Wikipedia's entry on Operational Transformation](https://en.wikipedia.org/wiki/Operational_transformation) and a comprehensive [FAQ concerning OT](http://www3.ntu.edu.sg/home/czsun/projects/otfaq); I particularly recommend reading the latter. | ||
## Install | ||
@@ -29,3 +31,3 @@ `npm install changesets` | ||
``` | ||
You get a `cs.Changeset` object containing multiple `cs.Operation`s. The changeset can be applied to a text as follows: | ||
You get a `cs.text.Changeset` object containing multiple `cs.Operation`s. The changeset can be applied to a text as follows: | ||
```js | ||
@@ -37,4 +39,23 @@ var finalText = changes.apply(text1) | ||
### Serializing changesets | ||
In many cases you will find the need to serialize your changesets in order to efficiently transfer them through the network or store them on disk. | ||
`Changeset#pack()` takes a changeset object and returns the string representation of that changeset. | ||
```js | ||
var serialized = changeset.pack() // '+1:YWI:0+2:Yw:0-3:NA:0+9:YWthYmw:0+b:cmFkYQ:0' | ||
``` | ||
`Changeset.unpack()` takes the output of `Changeset#pack()` and returns a changeset object. | ||
```js | ||
cs.text.Changeset.unpack(serialized) // {"0":{"accessory":0,"pos":1,"len":2,"text":"ab"},"1":{"accessory":0,"pos":2,"len":1,"text":"c"},"2":{"accessory":0,"pos":3,"len":1,"text" ... | ||
``` | ||
If you'd like to display a changeset in a humanly readable form, use `Changeset#inspect`: | ||
```js | ||
changeset.inspect() // [ 'Insert 1:ab', 'Insert 2:c', 'Delete 3:4', 'Insert 9:akabl', 'Insert 11:rada' ] | ||
``` | ||
### Operational transformation | ||
*Inclusion Transformation* as well as *Exclusion Transformation* are supported. | ||
*Inclusion Transformation* as well as *Exclusion Transformation* is supported. | ||
@@ -58,3 +79,5 @@ #### Inclusion Transformation | ||
``` | ||
Since we can at least safely apply one of them, we'll apply changeset A first on the original text. Now, in order to be able to apply changeset B, which still assumes the original context, we need to adjust it, based on the changes of changeset A, so that it still has the same effect on the text that was originally intended. | ||
Doesn't look that good. | ||
But since we can at least safely apply one of them, let's apply changeset A first on the original text. Now, in order to be able to apply changeset B, which still assumes the original context, we need to adjust it, based on the changes of changeset A, so that it still has the same effect on the text. | ||
```js | ||
@@ -70,3 +93,3 @@ var csB_new = csB.transformAgainst(csA) | ||
#### Exclusion Transformation | ||
Imagine a text editor, where users that allows users to undo any edit they've ever done to a document. Naturally, one will choose to store all edits as a list of changesets, where each applied on top of the other results in the currently visible document. | ||
Imagine a text editor, that allows users to undo any edit they've ever done to a document. Naturally, one will choose to store all edits as a list of changesets, where each applied on top of the other results in the currently visible document. | ||
```js | ||
@@ -89,5 +112,5 @@ var versions = | ||
``` | ||
Now we need to transform all following edits against this inverse changeset and in turn transform it against the previously iterated edits. | ||
Now we transform all following edits against this inverse changeset and in turn transform it against the previously iterated edits. | ||
``` | ||
var newEdits = [""] | ||
var newEdits = [] | ||
for (var i=1; i < edits.length; i++) { | ||
@@ -100,6 +123,2 @@ newEdits[i] = edits[i].transformAgainst(inverse) | ||
# More information | ||
Anyone interested in OT may want to start with [Wikipedia's entry on Operational Transformation](https://en.wikipedia.org/wiki/Operational_transformation) and a comprehensive [FAQ concerning OT](http://www3.ntu.edu.sg/home/czsun/projects/otfaq), I particularly recommend reading the latter. | ||
# Under the hood | ||
@@ -114,3 +133,2 @@ *Changesets* makes use of Neil Fraser's [*diff-match-patch* library](https://code.google.com/p/google-diff-match-patch/) for generating the diff between two texts -- an amazing library! | ||
* Perhaps add text length diff to `Operation`s in order to be able validate them | ||
* Add a `pack()`/`unpack()`method to changesets | ||
* Simplify anyundo (large numbers of changesets have to be transformed against each other and an undo changseset) | ||
@@ -117,0 +135,0 @@ |
@@ -73,7 +73,7 @@ /*! | ||
console.log("\n\n", test[0]) | ||
console.dir(cs1.dump()) | ||
console.dir(cs2.dump()) | ||
console.dir(cs1.inspect()) | ||
console.dir(cs2.inspect()) | ||
cs1 = cs1.transformAgainst(cs2) | ||
console.log('=>', cs1.dump()) | ||
console.log('=>', cs1.inspect()) | ||
@@ -121,7 +121,7 @@ return cs1.apply(cs2.apply(test[0])) | ||
console.log("\n\n "+test[0][0]+":", test[0][2], '-', test[0][1]) | ||
console.dir(cs1.dump()) | ||
console.dir(cs2.dump()) | ||
console.dir(cs1.inspect()) | ||
console.dir(cs2.inspect()) | ||
cs2 = cs2.substract(cs1) | ||
console.log('=>', cs2.dump()) | ||
console.log('=>', cs2.inspect()) | ||
@@ -138,2 +138,18 @@ return cs2.apply(test[0][0]) | ||
suite.addBatch({ | ||
'pack/unpack': | ||
{ topic: function() { | ||
return engine.constructChangeset("1234blabliblu", "1ab2c3blablakablibradalu") | ||
} | ||
, 'should be packed and unpacked correctly': function(er, cs) { | ||
var packed = cs.pack() | ||
console.log() | ||
console.log(cs.inspect()) | ||
console.log(packed) | ||
var unpacked = engine.Changeset.unpack(packed) | ||
assert.deepEqual(unpacked, cs) | ||
} | ||
} | ||
}) | ||
suite.export(module) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
37633
13
724
130