Client API
==========

.. _client_api:


Simple javascript browser client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Javascript client is designed to be very simple. I think the most difficult part of it is
configuration. But lets go through its API step by step.

When you have Centrifuge instance running - it is time to communicate with it from browser.

First, import javascript client into your web page:

.. code-block:: html

    <script src="https://raw.github.com/FZambia/centrifuge/master/javascript/centrifuge.js"></script>

Javascript client built on top of Event Emitter written by `Oliver Caldwell <https://github.com/Wolfy87>`_.


Then create new centrifuge object:

.. code-block:: javascript

    var centrifuge = new Centrifuge();

You've just created empty object which does not know anything about your Centrifuge
installation. To tell it that information you must use method ``configure``

.. code-block:: javascript

    centrifuge.configure({
        url: "",
        project:"",
        user: "",
        token: ""
    })

**url** is a Centrifuge endpoint - SockJS or Websocket. Note that in case of using SockJS
it must be imported on your page before Centrifuge's javascript client.  In case of using
SockJS additional configuration parameter can be used - ``protocols_whitelist``. It defines
allowed transports and by default equals ['websocket', 'xdr-streaming', 'xhr-streaming',
'iframe-eventsource', 'iframe-htmlfile', 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling',
'jsonp-polling'].

**project** is project ID from Centrifuge. Every project in Centrifuge has unique ID.
You can see it on project settings page of administrative web interface. This is
just a string.

**user** is your web application's current user unique ID. It must be string and can
be empty string if you want to allow anonymous access to your real-time application.

**token** is a secret key generated by your web application based on project secret key,
project ID and user ID. Every project in Centrifuge has secret key. You must never
show it to anyone else. The only two who know secret key are Centrifuge itself and
your web application backend. To create token you must use HMAC algorithm. For example
for python:

.. code-block:: python

    import hmac

    def get_client_token(secret_key, project_id, user):
        """
        Create token to validate information provided by new connection.
        """
        sign = hmac.new(str(secret_key))
        sign.update(str(project_id))
        sign.update(str(user))
        token = sign.hexdigest()
        return token

Correct token guarantees that connection request to Centrifuge contains valid
information about project and user IDs. Token is similar to HTTP cookie, client must
not show it to anyone else. Remember that you must  always use channels in private
namespaces when working with data of limited access.

Also you can optionally provide extra parameter ``info`` when connecting to Centrifuge:

```javascript
var centrifuge = new Centrifuge({
    url: 'http://centrifuge.example.com',
    token: 'token',
    project: 'id',
    user: 'id',
    info: '{"first_name": "Alexandr", "last_name": "Emelin"}'
});
```

``info`` is an additional information about user connecting to Centrifuge. It must
be valid JSON string. But to prevent client sending wrong ``info`` this JSON string
must be used while generating token:

.. code-block:: python

    import hmac

    def get_client_token(secret_key, project_id, user, user_info=None):
        sign = hmac.new(str(secret_key))
        sign.update(project_id)
        sign.update(user)
        if user_info is not None:
            sign.update(user_info)
        token = sign.hexdigest()
        return token


If you don't want to use ``info`` - you can omit this parameter while connecting
to Centrifuge. But if you omit it then make sure that it does not affect token
generation - i.e. in this case you need to generate token without ``sign.update(user_info)``.

You can combine Centrifuge initialization and configuration and write in this way:

.. code-block:: javascript

    centrifuge = new Centrifuge({
        url: "",
        project:"",
        user: "",
        token: ""
    });

Now centrifuge client configured and you are ready to start communicating.

It is as simple as:

.. code-block:: javascript

    centrifuge.connect();

This line makes actual connection request to Centrifuge with data you provided
in configure method. Of course you do not want to just connect. You want to listen
or to send messages from(into) channels. The first step is to subscribe on channel
of your interest. But you can only start subscribing when connection with Centrifuge
was successfully established. If you try to subscribe on channel before connection
established - your subscription request will be rejected by Centrifuge. There is
an event about successful connection and you can bind your subscription logic to it
in this way:

.. code-block:: javascript

    centrifuge.on('connect', function() {
        // now your client connected
    });

Also you ``disconnect`` and ``error`` events available:

.. code-block:: javascript

    centrifuge.on('disconnect', function() {
        // do whatever you need in case of disconnect
    });

    centrifuge.on('error', function(error_message) {
        // called every time error occurred
    });

When your client connected, it is time to subscribe on channel of certain namespace. Just write:

.. code-block:: javascript

    var subscription = centrifuge.subscribe('namespace:channel', function(message) {
        // called when message received from this channel
    });


If namespace of channel has publish option on you can publish messages into this
channel. But you can not do it immediately after subscription request. You can
only publish when ``subscribe:success`` event will be fired. The same in case of presence
and history requests. Lets publish message, get presence and get history data as
soon as our subscription request returned successful subscription response:

.. code-block:: javascript

    subscription.on('ready', function() {

        // publish into channel
        subscription.publish("hello");

        // get presence information (who is currently subscribed on this channel)
        subscription.presence(function(message) {
            console.log(message);
        });

        // get history (last messages sent) for this channel
        subscription.history (function(message) {
            console.log(message);
        });

        subscription.on('join', function(message) {
            // called when someone subscribes on channel
        });

        subscription.on('leave', function(message) {
            // called when someone unsubscribes from channel
        });

    });

You can unsubscribe from subscription:

.. code-block:: javascript

    subscription.unsubscribe();

In some cases you need to disconnect your client from Centrifuge:

.. code-block:: javascript

    centrifuge.disconnect();

After calling this client will not try to reestablish connection periodically. You must call
``connect`` method manually.


Make it even more simple
~~~~~~~~~~~~~~~~~~~~~~~~

To make things even more simple ``centrifuge.dom.js`` jQuery plugin can be used.

In most cases you application does not need all real-time features of Centrifuge.
If your application does not need complicated subscription management, dynamic channels
then ``centrifuge.dom.js`` can help you a lot.

Many of you heard about AngularJS or KnockoutJS. Those libraries use html attributes
to control application behaviour. When you change attributes and their values you
change your application logic. This is very flexible technique. Why not use this power
to add some real-time on your site.

First, add ``centrifuge.dom.js`` on your page:

.. code-block:: html

    <script src="rawgithub.com/FZambia/centrifuge/master/javascript/centrifuge.dom.js"></script>


Note, that ``centrifuge.dom.js`` requires **jQuery**!

When enabled that plugin searches for special html-elements on your page, creates a
connection to Centrifuge, subscribes on necessary channels and triggers event on
html-elements when new message from channel received.

All you need to do in this case is write how your page will react on new messages:

.. code-block:: javascript

    $(' #html-element').on('centrifuge.message', function(event, message) {
        console.log(message.data);
    });


Let's see how it looks in practice. Consider comments for example.

The user of your web application writes a new comment, clicks submit button.
Your web application's backend processes new data, validates it, saves as
usually. If everything ok you then must send POST request with comment data
into Centrifuge so that new comment will appear on the screen of all connected
clients.

Let's make it work in five steps.

STEP 1) Add all necessary scripts into your web application's main template.
These are ``jQuery``, ``SockJS`` (optional), ``centrifuge.js``, ``centrifuge.dom.js``

STEP 2) In main template initialize plugin:

.. code-block:: javascript

    $(function(){
        $.centrifuge_dom({});
    });


STEP 3) Also add html-elements with proper attributes in main template with connection
address, token, user ID and project ID values.

.. code-block:: html

    <div id="centrifuge-address" data-centrifuge-value="{{ centrifuge_address }}"></div>
    <div id="centrifuge-token" data-centrifuge-value="{{ centrifuge_token }}"></div>
    <div id="centrifuge-user" data-centrifuge-value="{{ centrifuge_user }}"></div>
    <div id="centrifuge-project" data-centrifuge-value="{{ centrifuge_project }}"></div>

Here I use syntax of Django templates. In your case it can look slightly different.
The values of connection address, token, user ID and project ID must provide your
web app's backend.

STEP 4) On the page with comments add the following html-element with channel and namespace
names in attributes

.. code-block:: html

    <div class="centrifuge" id="comments-handler" data-centrifuge-channel="comments" data-centrifuge-namespace="public"></div>

STEP 5) On the same page add some javascript

.. code-block:: javascript

    $(function() {
        $("#comments-handler").on("centrifuge.message", function(event, message) {
            $("body").append(message.data);
        });
    });


That's all, baby!

Moreover now to to add some new real-time elements on your pages you only need to do
last two steps.

In some scenarios you need to handle errors and disconnects. This can be done by listening
for ``centrifuge.disconnect`` and ``centrifuge.error`` events on handler elements.

For example

.. code-block:: javascript

    $("#comments-handler").on("centrifuge.disconnect", function(err) {
        console.log("disconnected from Centrifuge");
    });

