Skip to content
Pathompong Puengrostham edited this page May 18, 2015 · 27 revisions

Version 0.7 docs, and incomplete.

Client API docs for ShareJS 0.6

The client library lets you view & edit documents as well as make live queries to the database.

As of 0.7, ShareJS doesn't depend on any particular stream interface. Instead it requires a websocket-style stream object. You have to create and manage the stream yourself, including reconnections. My preferred way to do this is to use node-browserchannel.

To accept connections, the ShareJS server expects a NodeJS 0.10 stream. Here's some example code which wraps the browserchannel API in a stream and passes it to ShareJS:

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

// This hosts share.js on http://myserver.com/share.js. See connect/express docs.
var server = connect(connect.static(sharejs.scriptsDir));

var backend = livedb.client(livedbMongo('localhost:27017/test?auto_reconnect', safe:false));

// Create the sharejs server instance.
var share = sharejs.server.createClient({backend:backend});

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

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

  stream._read = function() {};

  stream.headers = client.headers;
  stream.remoteAddress = stream.address;

  client.on('message', function(data) {
    stream.push(data);
  });
  stream.on('error', function(msg) {
    client.stop();
  });
  client.on('close', function(reason) {
    stream.emit('close');
    stream.emit('end');
    stream.end();
  });

  // Actually pass the stream to ShareJS
  share.listen(stream);
}));

Accessing the client library

From node.js:

var sharejs = require('share').client;

sharejs.open('hello', 'text', 'http://example.com:8000/channel', function(error, doc) {
    ...
});

From a website hosted by the share.js server:

<script src="/channel/bcsocket.js"></script>
<script src="/share/share.js"></script>
<script>
  sharejs.open('hello', 'text', function(error, doc) {
      ...
  });
</script>

From a website hosted elsewhere:

<script src="http://example.com:8000/channel/bcsocket.js"></script>
<script src="http://example.com:8000/share/share.js"></script>
<script>
  sharejs.open('hello', 'text', 'http://example.com:8000/channel', function(error, doc) {
      ...
  });
</script>

Opening documents

The easy way

  • open()

    sharejs.open(docName, type, [url | options], function(error, doc){ ... })

    Open a sharejs document with the given name and type. A handle to the document will be passed to the callback once its snapshot has been received by the server.

    This method is a convenience method for connection.open, below.

    If the document does not already exist, it will be created using the specified type before being returned to the callback. If the document already exists and has a different type, an error will be passed to the callback.

    Document names are case sensitive and cannot contain slashes (/).

    The type can be specified as a string (eg, 'text') or an object (eg, require('share').types.text).

    The URL is a browserchannel URL. It looks like http[s]://hostname:port/channel. (Note the channel on the end specifies the default sharejs resource identifier. This may change in a future version.)

    The URL is only needed when using sharejs from a node.js app or when you're running sharejs from a different machine than the page's host. In the browser, it will default to 'PROTO://HOSTNAME:PORT/channel'. If the default is fine, leave it out.

    Instead of the URL you can pass in an options object which can contain the url (origin) and an additional authentication option

// Both origin and authentication are optional
options = {
  origin: 'http://hostname:port/channel',
  authentication: '123456789'
}

Manually managed connections

You only need to manually use connections if you're doing something tricky. Most people should just call sharejs.open.

A Connection manages a connection to a share.js server. You only need one connection to a given server no matter how many documents you have open.

If you use the sharejs.open API (above), connections are created for you automatically.

  • new Connection()

    connection = new sharejs.Connection(url)

    Create a connection to a sharejs server running at the specified url. See sharejs.open() above for more information on what the url should be.

  • connection.open()

    connection.open(docName, type, [url], function(error, doc) {...})

    Open a sharejs document with the given name and type. A handle to the document will be passed to the callback once its snapshot has been received by the server.

    If all you want to do is open documents of a known type on a known server, I recommend using sharejs.open, above. This way, the connection will be managed for you.

    If the document does not already exist, it will be created using the specified type before being returned to the callback. If the document already exists and has a different type, an error will be passed to the callback.

    Document names are case sensitive.

    The type can be specified as a string (eg, 'text') or an object (eg, require('share').types.text).

  • connection.openExisting()

    connection.openExisting(docName, function(error, doc) {...})

    Open an existing document. If the document does not exist, the callback is passed null.

  • connection.disconnect()

    connection.disconnect()

    Disconnect from the server. Once disconnected, all documents currently open through the connection will stop sending and receiving ops. There is no reconnect.

  • connection.on()

    connection.on("eventname", function(e) {
        //Do something here, possibly with e
    }

    The connection object emits several different events depending on the state of the connection. You can use on() to listen for those events and respond accordingly. The events supported (used in place of eventname) are:

    • "ok" This event is triggered whenever the connection has been successfully established or re-established. At this point the server has been negotiated with, authentication (if any) has passed, and the connection is ready for use.

    • "error" This event is triggered when there is an interruption to the connection (or if the connection is not able to be established in the first place). The e argument passed to the callback function contains some basic details on the cause of the error. Based on testing, it seems that this event will be triggered about 30 seconds after an established connection is interrupted (e.g. because the server has gone down or the Internet connection has been severed).

    • "connect failed" This event is triggered if the client fails to authenticate with the server. This only applies to servers where you have defined a custom authentication function. See User Access Control for more information.

Documents

All open methods in the client return Document objects. These objects are handles to documents sitting on the share.js server. The document object implements client-side OT so you don't have to worry about concurrency in your app.

You cannot create documents directly. Instead, use any of the open() methods described above.

For documentation on using JSON documents, see JSON Client API. All the methods in the JSON client API are accessible through the document object if you're using a JSON document.

  • doc.getText() (Text documents only)

    doc.getText()

    Get the contents of the document as a string.

  • doc.insert() (Text documents only)

    doc.insert(position, text, [function(error, appliedOp) {...}])

    Insert the given text at the specified position in the document. The position is an integer specifying the character position from the start of the document.

    Eg: Given a document with text abc, insert a d at the end by calling doc.insert(3,'d').

    Locally, the document is edited immediately. (If you call getText() straight after doc.insert, your document will already have been edited). The callback is called once the server has acknowledged the operation.

    The world won't end if you don't, but for consistency you should use unix line endings.

    If you make multiple calls to doc.insert(), doc.del() and doc.submitOp() within the same event handler, your edits will be composed together and sent as a single operation.

  • doc.del() (Text documents only)

    doc.del(position, length, [function(error, appliedOp) {...}])

    Delete length characters from the document at the specified position in the document. The position is an integer specifying the character position from the start of the document.

    Eg: Given a document with text abcd, delete the d at the end by calling doc.del(1, 3).

    Locally, the document is edited immediately. (If you call getText() straight after doc.insert, your document will already have been edited). The callback is called once the server has acknowledged the operation.

    If you make multiple calls to doc.insert(), doc.del() and doc.submitOp() within the same event handler, your edits will be composed together and sent as a single operation.

  • doc.snapshot

      doc.snapshot
    

    The document's current state. For text documents, this is a string containing the contents of the document. This snapshot includes any modifications made locally which have not yet propagated to the server.

  • doc.version

      doc.version
    

    The document's version on the server. This is a number counting from 1 when the document was first created. It is incremented each time an operation is applied to the document on the server. The client will compose together multiple operations if they happen close together, so be aware may not be incremented as often as you expect it to.

  • doc.type

      doc.type
    

    The OT type of the document. This object contains the document type's OT functions. See the section on OT types for details.

  • doc.submitOp()

    doc.submitOp(op, [function(error, appliedOp) {...}])

    This is a pretty low level function for editing documents. If you're editing text, you probably want to use the high level API methods (insert(), del(), etc) instead.

    This has been changed since v0.3 - the optional version parameter was removed.

    Submit an operation to the document. The operation must be valid given the document's type. The operation will be applied to the local document snapshot immediately (before submitOp returns).

    It will be applied to the server as soon as possible (only one op is allowed in-flight at a time). If multiple ops are applied within the same scheduler frame, they are composed together before being sent. Ie, if you call doc.submit(op1); doc.submit(op2);, the client will compose op1 and op2 for you.

    If a callback is specified, it will be called once the server has acknowledged the operation. It is passed the operation as it was accepted by the server, or an error message in the second argument. This will not necessarily be the same as the op that was submitted originally. You don't need to wait for the callback before calling submitOp() again.

    If the op is invalid, submitOp will throw an exception and the document will not be modified. If your code generating the ops is correct, this should never happen.

    I expect most people will never use the callback field of submitOp. Just call submitOp(myCoolOp) as much as you like and the client code should take care of everything else.

    Text operations look like this:

    Insert 'Some text' at position 100 in the document:

      {i:'Some text', p:100}
    

    Delete 'blargh' at position 300:

      {d:'blargh', p:300}
    

    Multiple changes can be specified in the same operation. This deletes 'Sam' in the document at position 10 in the document string, and replaces it with 'Seph' at the same location.

      [{d:'Sam', p:10}, {i:'Seph', p:10}]
    

    See the documentation on types for more detail, as well as op specs for other types.

  • doc.attach_textarea(element)

  • doc.close()

      doc.close([callback])
    

    Close the document. When the server acknowledges that the document has been closed, the callback is called and the 'closed' event is emitted.

Events

The document is a simple event emitter. Use doc.on(event, callback) to register callbacks and doc.removeListener(event, callback) to remove them. Callbacks are called with the doc as context. That is, within a callback, you can refer to doc with the this keyword.

  • insert

    doc.on('insert', function(position, text) {})

    (Text documents only) Emitted whenever a remote client inserts text at position. Positions are specified as character offsets from the start of the document.

    If a remote client makes multiple changes to the document within a single operation, the document snapshot will be updated with all changes before your callbacks are called.

    Eg: Given a document ace, a client inserts a b and a d to make the document abcde. You have registered doc.on('insert', foo). ShareJS will call foo('b', 1) then foo('d', 3).

  • delete

    doc.on('delete', function(position, text) {})

    (Text documents only) Emitted whenever a remote client deletes text at position. Positions are specified as character offsets from the start of the document.

    If a remote client makes multiple changes to the document within a single operation, the document snapshot will be updated with all changes before your callbacks are called.

    Eg: Given a document abcde, a client deletes a b and a d to make the document ace. You have registered doc.on('delete', foo). ShareJS will call foo('b', 1) then foo('d', 2).

  • remoteop

    doc.on('remoteop', function(op) {})

    Emitted each time the document is changed remotely with the op from the server. If you're hooking sharejs up to an editor, listen to this event and update the contents of the editor window accordingly.

    By the time your callback is called, the document's snapshot and version will have been updated by the change. If you want, your listener method can just read the snapshot out of the document again.

  • change

    doc.on('change', function(op) {})

    Emitted each time the document is changed either locally or remotely. The op which modified the document is passed to the callback.

  • acknowledge

    doc.on('acknowledge', function(op) {})

    Emitted each time the server acknowledges a sent operation. The acknowledge op is passed to the callback. Note: it was implemented as of 7223fb6.

  • closed

    doc.on('closed', function() {})

    Emitted when the server has acknowledged that the document is closed.