GithubHelp home page GithubHelp logo

sharejs's Introduction

NOTE: ShareJS is now ShareDB. See here and here for more information.

.

.

.

ShareJS

Join the chat at https://gitter.im/share/ShareJS

This is a little server & client library to allow concurrent editing of any kind of content via OT. The server runs on NodeJS and the client works in NodeJS or a web browser.

ShareJS currently supports operational transform on plain-text and arbitrary JSON data.

Visit Google groups for discussions and announcements

Immerse yourself in API Documentation.

Build Status

Browser support

ShareJS should work with all browsers, down to IE5.5 (although IE support hasn't been tested with the new version).

That said, I only test regularly with FF, Safari and Chrome, and occasionally with IE8+. File bug reports if you have issues

Installing and running

# npm install share

Run the example server with:

# coffee node_modules/share/examples/server.coffee

Not all of the sharejs 0.6 examples have been ported across yet. I'd love some pull requests!

ShareJS depends on LiveDB for its database backend & data model. Read the livedb readme for information on how to configure your database.

Run the tests:

# npm install
# mocha

Server API

To get started with the server API, you need to do 2 things:

  • Decide where your data is going to be stored. You can mess around using the livedb inmemory store. For more options, see the livedb api.
  • Decide how your client and server will communicate. The easiest solution is to use browserchannel.

To create a ShareJS server instance:

var livedb = require('livedb');
var sharejs = require('share');

var backend = livedb.client(livedb.memory());
var share = require('share').server.createClient({backend: backend});

The method is called createClient because its sort of a client of the database... its a weird name, just roll with it.

The sharejs server instance has 3 methods you might care about:

  • To communicate with a client, create a node stream which can communicate with a client and use share.listen(stream) to hand control of the stream to sharejs. See the section below on client server communication for an example of this.
  • share.rest() returns a connect/express router which exposes the sharejs REST API. This code is in the process of moving to its own repo. In the meantime, the documentation is here
  • You can intercept requests to the livedb backend to do access control using sharejs middleware. share.use(method, function(action, callback){...}) will make your function intercept & potentially rewrite requests. This is not currently documented, but when it is, the documentation will live here.

Client server communication

ShareJS requires you to provide a way for the client to communicate with the server. As such, its transport agnostic. You can use browserchannel, websockets, or whatever you like. ShareJS requires the transport to:

  • Guarantee in-order message delivery. (Danger danger socket.io does not guarantee this)
  • Provide a websocket-like API on the client
  • Provide a node object stream to the server to talk to a client.

When a client times out, the server will throw away all information related to that client. When the client client reconnects, it will reestablish all its state on the server again.

It is the responsibility of the transport to handle reconnection - the client should emit state change events to tell sharejs that it has reconnected.

Server communication

The server exposes a method share.listen(stream) which you can call with a node stream which can communicate with the client.

Here's an example using browserchannel:

var Duplex = require('stream').Duplex;
var browserChannel = require('browserchannel').server

var share = require('share').server.createClient({backend: ...});
var app = require('express')();

app.use(browserChannel({webserver: webserver}, function(client) {
  var stream = new Duplex({objectMode: true});

  stream._read = function() {};
  stream._write = function(chunk, encoding, callback) {
    if (client.state !== 'closed') {
      client.send(chunk);
    }
    callback();
  };

  client.on('message', function(data) {
    stream.push(data);
  });

  client.on('close', function(reason) {
    stream.push(null);
    stream.emit('close');
  });

  stream.on('end', function() {
    client.close();
  });

  // Give the stream to sharejs
  return share.listen(stream);
}));

And here is a more complete example using websockets.

Client communication

The client needs a websocket-like session object to communicate. You can use a normal websocket if you want:

var ws = new WebSocket('ws://' + window.location.host);
var share = new sharejs.Connection(ws);

Sharejs also supports the following changes from the spec:

  • The socket can reconnect. Simply call socket.onopen again when the socket reconnects and sharejs will reestablish its session state and send any outstanding user data.
  • If your underlying API allows data to be sent while in the CONNECTING state, set socket.canSendWhileConnecting = true.
  • If your API allows JSON messages, set socket.canSendJSON = true to avoid extra JSON stringifying.

If you use browserchannel, all of this is done for you. Simply tell browserchannel to reconnect and it'll take care of everything:

var socket = new BCSocket(null, {reconnect: true});
var share = new sharejs.Connection(socket);

Client API

The client API can be used either from nodejs or from a browser.

From the server:

var connection = require('share').client.Connection(socket);

From the browser, you'll need to first include the sharejs library. You can use browserify and require('share').client or include the script directly.

The browser library is built to the node_modules/share/webclient directory when you install sharejs. This path is exposed programatically at require('share').scriptsDir. You can add this to your express app:

var sharejs = require('share');
app.use(express.static(sharejs.scriptsDir));

Then in your web app include whichever OT types you need in your app and sharejs:

<script src="text.js"></script>
<script src="json0.js"></script>
<script src="share.js"></script>

This will create a global sharejs object in the browser.

Connections

The client exposes 2 classes you care about:

  • The Connection class wraps a socket and handles the communication to the sharejs server. You use the connection instance to create document references in the client.
  • All actual data you edit will be wrapped by the Doc class. The document class stores an in-memory copy of the document data with your local edits applied. Create a document instance by calling connection.get('collection', 'docname').

ShareJS also allows you to make queries to your database. Live-bound queries will return a Query object. These are not currently documented.

To get started, you first need to create a connection:

var sjs = new sharejs.Connection(socket);

The socket must be a websocket-like object. See the section on client server communication for details about how to create a socket.

The most important method of the connection object is .get:

connection.get(collection, docname): Get a document reference to the named document on the server. This function returns the same document reference each time you call connection.get(). collection and docname are both strings.

Connections also expose methods for executing queries:

  • createFetchQuery(index, query, options, callback): Executes a query against the backend and returns a set of documents matching the query via the callback.
  • createSubscribeQuery(index, query, options, callback): Run a query against the backend and keep the result set live. Returns a Query object via the callback.

The best documentation for these functions is in a block comment in the code.

For debugging, connections have 2 additional properties:

  • Set connection.debug = true to console.log out all messages sent and recieved over the wire.
  • connection.messageBuffer contains the last 100 messages, for debugging error states.

Documents

Document objects store your actual data in the client. They can be modified syncronously and they can automatically sync their data with the server. Document objects can be modified offline - they will send data to the server when the client reconnects.

Normally you will create a document object by calling connection.get(collection, docname). Destroy the document reference using doc.destroy().

Documents start in a dumb, inert state. You have three options to get started:

  • Normally, you want to call doc.subscribe(callback). This will fetch the current data from the server and subscribe the document object to a feed of changes from other clients. (If you don't want to be subscribed anymore, call doc.unsubscribe([callback])).
  • If you don't want a live feed of changes, call doc.fetch(callback) to get the data from the server. Your local document will be updated automatically every time you submit an operation.
  • If you know the document doesn't exist on the server (for example the doc name is a new GUID), you can immediately call doc.create(type, data, callback).

There's a secret 4th option - if you're doing server-side rendering, you can initialize the document object with bundled data by calling doc.ingestData({type:..., data:...}).

To call a method when a document has the current server data, pair your call to subscribe with doc.whenReady(function() { ... }. Your function will be called immediately if the document already has data.

Both subscribe and fetch take a callback which will be called when the operation is complete. In ShareJS 0.8 this callback is being removed - most of the time you should call whenReady instead. The semantics are a little different in each case - the subscribe / fetch callbacks are called when the operation has completed (successfully or unsuccessfully). Its possible for a subscription to fail, but succeed when the client reconnects. On the other hand, whenReady is called once there's data. It will not be called if there was an error subscribing.

Once you have data, you should call doc.getSnapshot() to get it. Note that this returns the doc's internal doc object. You should never modify the snapshot directly - instead call doc.submitOp.

Editing documents

Documents follow the sharejs / livedb object model. All documents sort of implicitly exist on the server, but they have no data and no type until you 'create' them. So you can subscribe to a document before it has been created on the server, and a document on the server can be deleted and recreated without you needing a new document reference.

To make changes to a document, you can call one of these three methods:

  • doc.create(type, [data], [context], [callback]): Create the document on the server with the given type and initial data. Type will usually be 'text' or 'json0'. Data specifies initial data for the document. For text documents, this should be an initial string. For JSON documents, this should be JSON stringify-able data. If unspecified, initial data is an empty string or null for text and JSON, respectively.
  • doc.submitOp(op, [context], [callback]): Submit an operation to the document. The operation must be valid for the given OT type of the document. See the text document OT spec and the JSON document OT spec. Consider using a context instead of calling submitOp directly. (Described below)
  • doc.del([context], [callback]): Delete the document on the server. The document reference will become null.

In all cases, the context argument is a user data object which is passed to all event emitters related to this operation. This is designed so data bindings can easily ignore their own events.

The callback for all editing operations is optional and informational. It will be called when the operation has been acknowledged by the server.

To be notified when edits happen remotely, register for the 'op' event. (See events section below).

If you want to pause sending operations to the server, call doc.pause(). This is useful if a user wants to edit a document without other people seeing their changes. Call doc.resume() to unpause & send any pending changes to the server.

Editing Contexts

The other option to edit documents is to use a Document editing context. Document contexts are thin wrappers around submitOp which provide two benefits:

  1. An editing context does not get notified about its own operations, but it does get notified about the operations performed by other contexts editing the same document. This solves the problem that multiple parts of your app may bind to the same document.
  2. Editing contexts mix in API methods for the OT type of the document. This makes it easier to edit the document. Note that the JSON API is currently a bit broken, so this is currently only useful for text documents.

Create a context using context = doc.createContext(). Contexts have the following methods & properties:

  • context.submitOp(op, callback): Wrapper for doc.submitOp(op, context, callback).
  • context._onOp = function(op) {...} This is a hook for you / the type API to add your own logic when operations happen. If you're using the text API, bind to context.onInsert = ... and context.onRemove = ... instead.
  • context.destroy(): Destroy the context. The context will stop getting messages.

If you're making a text edit binding, bind to a document context instead of binding to the document itself.

Document events

In the nodejs tradition, documents are event emitters. They emit the following events:

  • ready: Emitted when the document has data from the server. Consider using whenReady(callback) instead of this event so your function is called immediately if the document already has data from the server.

  • subscribe: Emitted when the document is subscribed. This will be re-emitted when the document is resubscribed each time the client reconnects.

  • unsubscribe: Emitted when the document is unsubscribed. This will be re-emitted whenever the document is unsubscribed due to the client being disconnected.

  • nothing pending: Emitted after sending data to the server, when there are no outstanding operations to send. Pair with hasPending to find out when there is outstanding data. This is useful for displaying "Are you sure you want to close your browser window" messages to the user.

  • create: Emitted when the document has been created. Called with (context).

  • del: Emitted when the document has been deleted. The del event is triggered with (context, oldSnapshot).

  • before op: Emitted right before an operation is applied. Called with (op, context).

  • op: Emitted right after each part of an operation is applied. Called with (op, context). This is usually called just once, but you can specify doc.incremental = true to tell the document to break the operation into smaller parts and emit them one at a time.

  • after op: Emitted after an operation (all of it) is applied. Called with (op, context).

Operations lock the document. For probably bad reasons, it is illegal to call submitOp in the event handlers for create, del, before op or op events. If you want to make changes in response to an operation, register for the after op or unlock events.

Examples

Here's some code to get started editing a text document:

<textarea id='pad' autofocus>Connecting...</textarea>
<script src="channel/bcsocket.js"></script>
<script src="text.js"></script>
<script src="share.js"></script>
<script>
var socket = new BCSocket(null, {reconnect: true});
var sjs = new sharejs.Connection(socket);

var doc = sjs.get('docs', 'hello');

// Subscribe to changes
doc.subscribe();

// This will be called when we have a live copy of the server's data.
doc.whenReady(function() {
  console.log('doc ready, data: ', doc.getSnapshot());
  
  // Create a JSON document with value x:5
  if (!doc.type) doc.create('text');
  doc.attachTextarea(document.getElementById('pad'));
});

And a JSON document:

var socket = ...;
var sjs = new sharejs.Connection(socket);

var doc = sjs.get('users', 'seph');

// Subscribe to changes
doc.subscribe();

// This will be called when we have a live copy of the server's data.
doc.whenReady(function() {
  console.log('doc ready, data: ', doc.getSnapshot());
  
  // Create a JSON document with value x:5
  if (!doc.type) doc.create('json0', {x:5});
});

// later, add 10 to the doc.snapshot.x property
doc.submitOp([{p:['x'], na:10}]);

See the examples directory for more examples.


License

ShareJS is proudly licensed under the MIT license.

sharejs's People

Contributors

aslakhellesoy avatar bfirsh avatar bnoguchi avatar collin avatar crypticswarm avatar daredevildave avatar dcolens avatar dgreisen avatar dignifiedquire avatar dqminh avatar espadrine avatar fredericheem avatar gsathya avatar henryoswald avatar josephg avatar jukowski avatar luto avatar max-mapper avatar mcolyer avatar my8bird avatar nateps avatar noansknv avatar nornagon avatar nwinter avatar obijywk avatar pedrosanta avatar rburns avatar seidtgeist avatar wmertens avatar zamfi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sharejs's Issues

Crash!

DEBUG: Error: msg.doc missing. Probably the client reconnected without telling us - this is a socket.io bug.
    at Socket.<anonymous> (/home/josephg/src/ShareJS/src/server/socketio.coffee:297:21)
    at Socket.$emit (events.js:64:17)
    at SocketNamespace.handlePacket (/home/josephg/src/ShareJS/node_modules/socket.io/lib/namespace.js:348:20)
    at Manager.onClientMessage (/home/josephg/src/ShareJS/node_modules/socket.io/lib/manager.js:436:38)
    at WebSocket.onMessage (/home/josephg/src/ShareJS/node_modules/socket.io/lib/transport.js:387:20)
    at Parser.<anonymous> (/home/josephg/src/ShareJS/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js:38:10)
    at Parser.emit (events.js:64:17)
    at /home/josephg/src/ShareJS/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js:209:16
    at Parser.expectHandler (/home/josephg/src/ShareJS/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js:220:15)
    at Parser.add (/home/josephg/src/ShareJS/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js:338:24)

node.js:134
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
TypeError: Cannot read property 'listener' of undefined
    at /home/josephg/src/ShareJS/src/server/socketio.coffee:51:30
    at /home/josephg/src/ShareJS/src/server/socketio.coffee:178:18
    at /home/josephg/src/ShareJS/src/server/socketio.coffee:167:18
    at /home/josephg/src/ShareJS/src/server/socketio.coffee:140:20
    at /home/josephg/src/ShareJS/src/server/socketio.coffee:200:22
    at /home/josephg/src/ShareJS/src/server/model.coffee:35:18
    at Command.callback (/home/josephg/src/ShareJS/src/server/db/redis.coffee:121:18)
    at RedisClient.return_reply (/home/josephg/src/ShareJS/node_modules/redis/index.js:425:29)
    at RedisReplyParser.<anonymous> (/home/josephg/src/ShareJS/node_modules/redis/index.js:81:14)
    at RedisReplyParser.emit (events.js:64:17)

Start session with code

Is there a way to start a share session with code based on the contents of Ace?

Right now I'm doing this:

sharejs.open(docID, 'text', options, function(doc, err) {
  if (doc.created)
    doc.submitOp({i:editor.code(), p:0});

  doc.attach_ace(editor.ace)
})

use the last path component type to disambiguate OT types

Since objects cannot have integer keys in JS, it's sufficient to check the type of the last path component to disambiguate object operations from operations on lists and strings.

object[0] === object['0'] always.

To go deeper into pairing down the number of OTs, taking a bracketed list as an argument to list operations allows for changing multiple items and disambiguating the operation from a string operation, further removing the need for different OTs based on type.

Remove awful closure syntax in the client

To make sharejs work with closure, we've sacrificed a fair bit of readability. I've moved to uglify now, and all that nasty syntax should go away.

I don't see much benefit in cleaning up the OT code (Text & JSON) but client.coffee is one of sharejs's main entrypoints and its pretty heavily edited. That code should be cleaned up to remove all the @['awful']() method calls.

Cannot read property '_idleNext' of undefined

Hi,
I installed ShareS this morning, and when I typed the commande bin/sharejs I've gotten this error in the server terminal :(

root@mcmyt-server:~/node/ShareJS# bin/sharejs
Server running at http://127.0.0.1:8000/

_linklist.js:65
item._idleNext = list._idleNext;
^
TypeError: Cannot read property '_idleNext' of undefined
at Object.append (_linklist.js:65:24)
at Object.active (timers.js:136:9)
at Socket._onReadable (net_legacy.js:663:12)
at IOWatcher.onReadable as callback

Best regards,
Moncef

Feature Request: setText method

Can you add a method to set the text in a specified document? I want to overwrite the entire content of a sharejs document but this is not so straightforward with the current API.

Rich text support

ShareJS needs a set of OT functions for editing rich text. They should support annotations (bold, italics, etc) of some sort and there should be a plan on how to embed objects like images in documents.

ShareJS (socket.io connection) not working on my live server

ShareJS seems to work fine on my local machine. But when I put it on a public server, I've been getting the following error where it appears that a socket.io connection fails:

d is undefined
((function(){var a,b,c,d,e,f,g,h,i,j,k...on("connect failed")}}()})).call(this)

share.js - Line 1
I don't know what the d variable is, so its quite difficult to attempt to debug.

Other features of my scenario:
I'm using redis as the default data store.
Redis seems to get a connection from sharejs server.
The ace editor attaches fine on the client side, though sharing is not enabled due to the aforementioned error.
I have replicated this error in my own project as well as the examples provided.

I was to give a demo of this project to a group in a couple of days, so hopefully I can resolve this soon!

If you need additional details, let me know.
Thanks,
Dev

Text OT Performance

The current text OT type has quadratic transform time for transforming lists of operations.

It should be possible to speed this up immensely by using clever algorithms.

Client code and type code is ugly because of closure compiler

Currently there's lots of awful looking code like this:

        @socket['on'] 'connect', @connected
        @socket['on'] 'disconnect', @disconnected
        @socket['on'] 'message', @onMessage

and this

    @['close'] = @close = (callback) ->

and this

        if last['i']? && c['i']? and last['p'] <= c['p'] <= (last['p'] + last['i'].length)

... all in order to avoid names being munged by the closure compiler. There are two ways to fix this:

  1. Create a closure externs file to define things closure isn't allowed to rename
  2. Use uglify instead of closure to minify sharejs

Startup time is really slow!

ShareJS is taking ages to connect initially when the server is awhile away.

Reduce the number of roundtrips needed.

Update sharejs in npm

The README says:

Note: Until I update sharejs in npm, you'll need this too: cd node_modules/share cake build

Any chance this can happen soon? I'd like to avoid the building step when deploying.

JSON API

We need a decent API for editing JSON objects.

I'm thinking of something like this:

Document paths are more easily written using strings, separated by dots (.). We'll have an escape character for literal dots. All functions which take path strings can take arrays instead.

Unfortunately, if we use \ as the escape character you'll have to write literal dots as \\.. I'm not sure what would be better.

Document API methods:

  • doc.get('a.b.c'): Get the object at path ['a','b','c']. Equivalent to doc.snapshot['a']['b']['c']
  • doc.set('a.b.c', 55): Set ['a','b'] to 55. This will emit an op which replaces whatever is there now with the new value. Cannot be used on strings or numbers.
  • doc.insert('a.b.4', 'hi'): This requires that there's a list, object or string at 'a.b'. If its a:
    • string: Insert 'hi' into the string at character position 4. (Throws exception if the inserted element isn't a string)
    • array: Insert 'hi' as a new element into the list
    • object: Set object[4] = 'hi'. Same as doc.set('a.b.4', 'hi').
  • doc.wrap('a.b.c'): Get a fake document object which wraps the element at ['a','b','c']. If the element at the named path is moved, the result should keep working. If this element is a:
    • string: The object has the API methods from text documents (getText(), insert(), del(), on('insert', fn), on('delete', fn)). Operations effect the named path. (This allows you to bind JSON document string elements to a text editor)
    • object or array: The object inherits the JSON API methods.
    • number: The object has add(val), subtract(val) and getValue() methods, as well as an .on('added', function(byValue) {...}) event handler.

We'll need some events as well... I'm not really sure what these should look like.

We could rename doc.get() to doc.getRaw() and call doc.wrap() doc.get() instead. I have a feeling that editing objects via wrappers will be the most common way to edit them.. but I'm not sure if this API is as clear.

Tests are not run on compiled client

Currently, the nodeunit tests aren't run on the closure compiled client code. There's a bunch of ways closure can break the client - and without tests, there's no confidence that it works.

The compiled share.js file should be wrapped in a nodejs module or something and the client tests should be run on it.

Mangled text when multiple people type starting from the same place

For example, if the textbox is empty and both people start typing at the same time, what they type gets interleaved, resulting in mangled text, when the desired behavior is that one complete sentence comes after the other.

This might not be quite a bug, the OT algorithm might be working correctly, but this is still an issue. EtherPad, notably, behaves correctly in this situation, and I don't think it's just because it has higher latency due to lack of WebSockets.

The fix is probably to be smarter about where to put the cursor after applying a remote op. What is currently happening, is that if the cursor is at the end of a line, and a remote op inserts something at the end of the line, the cursor comes after what was inserted. I think that if instead, what the remote op inserts comes after the cursor, then rather than interleaving, text inserted remotely should build up further and further away from text being inserted locally.

Properly evaluate uglify.js

Uglify is faster, and has better node.js support than closure.

Figure out if we end up with a significantly bigger filesize. If we don't, switch to it.

Off-by-one error on Opera/IE when inserting text

Summary:
When using the attach_textarea functionality, and inserting text, Opera and IE8 will insert the text at an off-by-one location versus where it should be.

Steps to reproduce:

  1. Go to http://sharejs.org with Chrome and Opera
  2. Using Chrome, clear the "etherpad in 4 lines" and type 123

Expected results:
3. Opera shows 1, 2 and 3 on consecutive lines

Actual results:
3. Opera shows 1, empty line, empty line, 3, 2

Notes:

  • Reloading Opera makes it show properly.
  • IE8 has the same issue, IE9 is fine

Creating documents is confusing

At the moment, documents are implicitly created when their type is set. This must be the first operation applied to a document.

This is needlessly confusing. Creating documents should be a first-class thing.

  • There should be a special 'create document' message in the socketio wire protocol.
  • The model should have a create(docName) method or something like it

Share minified code only in production

ShareJS should serve minified code only when NODE_ENV=production is set.

Or if there is a way to disable it, it should be documented better :)

I had today hard time debugging it.

Webclient build error with coffeescript 1.1.1

Building the webclient caused the following error:

jeunet:ShareJS soph$ cake webclient

node.js:134
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
Error: Command failed: cat: webclient/concatenation.js: No such file or directory

    at ChildProcess.exithandler (child_process.js:102:15)
    at ChildProcess.emit (events.js:67:17)
    at Socket.<anonymous> (child_process.js:172:12)
    at Socket.emit (events.js:64:17)
    at Array.<anonymous> (net.js:829:12)
    at EventEmitter._tickCallback (node.js:126:26)

Running CoffeeScript version 1.1.1

IE error 'trimRight' in the wiki demo

When I try the "wiki demo" in IE9 I get this error:
"Object doesn't support property or method 'trimRight'"

It only seems to happen when the following ace option is used:
editor.session.setUseWrapMode(true);

I haven't tried it yet, but probably this issue gets solved by just updating the ace.js :)

ShareJS really slow on ubuntu

I just ran the benchmark tests on my laptop and on my ubuntu box (11.10). From my mac laptop <-> my mac laptop, I get 1750 ops per second. If either the client or the server (or both) are running on my ubuntu box, I get exactly 40ms of latency with each request. So the number of ops processed per second drops from 1750 down to 25.

I don't know where that latency is coming from. Its using socket.io 0.8.6. I'm not sure if sharejs.org has the same problem.

JSON support

It should be possible to edit JSON objects using sharejs. A spec for editing JSON is in a block comment in src/types/json.coffee. This should be fleshed out and implemented.

Dependencies

Might want to mention these in readme:

nodeunit
redis

Authentication

It should be possible to lock down a sharejs server, and restrict read / edit access to documents.

This can be done by adding some access control methods to the options file. These methods could be called by the model to check whether or not a user is allowed to perform the requested operation on the requested document.

Remove JSON OT clone() function

The clone function in the JSON OT code isn't ok:

  • Its slow
  • It requires browser support for JSON
  • It shouldn't be necessary, damnit

It should be removed.

To do so, we'll probably need a way to properly roll back operation application. If an op gets half-applied, then an exception is raised during processing, we should instead invert the applied op components and apply them to restore the state.

API for history management

getOps should be exposed in the REST and socket.io interfaces, so the client can request historical operations for timeline views and things like that.

Add a way for the server to tell the client its document is readonly

The authorization code should be able to specify that a client's view of a document is readonly.

This message should be passed to the client when the document is opened, and appear as a property on document (doc.isReadOnly)

The ace & textarea views should display the text area accordingly (ie, disabled).

json document nesting feature proposal

I have a project management application that I'm working on and I'm finding that the ability to nest arbitrary sharejs documents within a sharejs json document would be extremely helpful. Take these objects for example:

{
  name : 'some project',
  description : 'some description',
  tasks : [
    <<[task] sharejs json document>>,
    <<[task] sharejs json document>>,
    <<[task] sharejs json document>>,
  ] 
}  // project
{
  name : 'task 1',
  description : <<[description] sharejs text document>>
} // task

In this case, I'm imagining a project document with a set of task documents inside. The projects path 'tasks' can be operated on just like a regular sharejs list. Each task is itself a sharejs json document, and each task contains a sharejs text document at path 'description'. This text document behaves just like a regular sharejs text document.

I can get almost all the behavior I want just by flattening these objects, but the text document part is problematic. I'd love to be able to take advantage of all the fancy stuff built-in to the text document type while being connected to a json document.

Disclaimer: I'm just beginning to play with web sockets, so I may be "doing it wrong". If that's the case maybe you can provide some pointers.

Any thoughts?

EventEmitter memory leak detected

I'm getting the following warning:

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.

Trace:
at Socket. (events.js:139:15)
at Socket.once (events.js:160:8)
at EventEmitter._setBackChannel (share/node_modules/browserchannel/lib/server.coffee:250:24)
at Object.handle (share/node_modules/browserchannel/lib/server.coffee:516:28)
....

Server API documentation

The server API should be documented.

Primarily, the following APIs need docs:

  • Creating a new server programatically
  • Model APIs

JSON OT - nested documents

Currently, JSON OT uses the "text" type for strings it contains.
I would like to be able to change this (per node in the json) to text-composable or other types that may be added in the future.

Socket.io v0.7.3: 'has no method send'

ShareJS installed me the version 0.7.3 of socket.io, but when I ran the exampleserver the demos weren't working.
I kept receiving the message socket.io - 'has no method send'.
I installed socket.io 0.6.0 and now everything seems to be good.

PS: It's very plausible that I made something wrong, I'm new to node.js.

Implement metadata: active sessions, cursor etc

Maybe this is the wrong way to think about ShareJS, but is there a way to get a list of currently active sessions (potentially with metadata)?

I'd love to implement something like this:

Database performance

The redis database layer is pretty slow at the moment because the code insists on storing / retrieving everything from the server on every operation and query.

There are two easy ways this could be made faster:

  • Introduce a document cache between the database and the model code
  • Make the database layer only commit new document snapshots infrequently.

This change should make the redis database perform about as well as the memory database, which is about a 2x performance boost to the whole system.

OT Documentation

Need documentation on the OT system.

This should cover:

  • How it works
  • How to write your own OT types

Better client testing

The client's test coverage is unusually low. It should have tests as thorough as the model tests.

These tests are conspicuously missing:

  • Disconnect then reconnect works
  • Documents are reopened when sharejs reconnects
  • A closed document is not automatically reopened when sharejs reconnects
  • A closed document can be reopened
  • When a document is reopened, it specifies the type, the version, and doesn't request a new snapshot.
  • open() still (eventually) works when the connection is closed but reconnecting
  • Ops applied while a document is closed get sent to the server when it reconnects
  • Remote ops applied while a document is closed get (correctly) applied locally
  • Disconnect while an op is in-flight. The op should be resent when reconnection happens, with dupIfSource: set correctly
  • connecting, connected, handshake, ok events are all fired from the connection
  • Connection status events are fired from the document
  • Bare sharejs.open returns the connection object
  • The bare sharejs.open connection is closed when all documents have been manually closed

I bet at least one of these things doesn't currently work correctly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.