oyvindkinsey / easyXDM

A javascript library providing cross-browser, cross-site messaging/method invocation.
http://easyxdm.net
MIT License
2.38k stars 265 forks source link

easyXDM - easy Cross-Domain Messaging

easyXDM is a Javascript library that enables you as a developer to easily work around the limitation set in place by the Same Origin Policy, in turn making it easy to communicate and expose javascript API's across domain boundaries.

Warning This repository is not actively maintained as the services it provides is supported natively by all browsers.

Some of the goals for the project are that it should

How easyXDM works

At the core easyXDM provides a transport stack capable of passing string based messages between two windows, a consumer (the main document) and a provider (a document included using an iframe). It does this by using one of several available techniques, always selecting the most efficient one for the current browser.
For all implementations the transport stack offers bi-directionality, reliability, queueing and sender-verification.

Using JavaScript only (no Flash, Silverlight, extra html files etc) easyXDM provides the following browsers with stacks with latency of less than 15ms:

In browsers not mentioned here, and not supporting the postMessage API, the following transports will be used, depending on support and configuation:

How to use easyXDM

When using easyXDM you first load the consumer document and then let easyXDM load the provider. This is by default done in a hidden iframe, but you can also configure easyXDM to display the iframe in a specific container, and with a specific style attached.

The library provides two main object types that utilize this transport stack:

The easyXDM.Socket

.. is a thin wrapper around the transport stack and lets you send strings between the consumer and the provider.

To set up a simple Socket this is what you will need to add to the consumer


    var socket = new easyXDM.Socket({
        remote: "http://path.to/provider/", // the path to the provider
        onMessage:function(message, origin) {
            //do something with message
        }
    });

And this is what's needed for the provider

    var socket = new easyXDM.Socket({
        onMessage:function(message, origin) {
            //do something with message
        }
    });

Use this for sending the strings to the other end:

    socket.postMessage("hello world");

In addition the following config properties can be set for both consumer and provider

These properties can be set only on the consumer

These properties can be set only on the provider

A socket can be torn down (removing the iframe, etc.) using

    socket.destroy();

The easyXDM.Rpc

... constructor lets you create a proxy object with method stubs and uses JSON-RPC to invoke these methods and return the responses.

The Rpc uses the same transport stack as the Socket, and so uses the same config properties.

To set up a simple Rpc this is what you will need to add to the consumer

    var rpc = new easyXDM.Rpc({
        remote: "http://path.to/provider/" // the path to the provider
    }, 
    {
        local: {
            helloWorld: function(successFn, errorFn){
                // here we expose a simple method with no arguments
                // if we want to return a response, we can use `return ....`,
                // or we can use the provided callbacks if the operation is async
                // or an error occurred
            }
        },
        remote: {
            helloWorld:{
                // here we tell the Rpc object to stub a method helloWorld for us
            }
        }
    });

Call the methods like this

    rpc.helloWorld(1,2,3, function(response){
        // here we can do something with the return value from `helloWorld`
    }, function(errorObj){
        // here we can react to a possible error
    });

And this is what's needed for the provider

    var rpc = new easyXDM.Rpc({},
    {
        local: {
            helloWorld: function(one, two, thre_args, successFn, errorFn){
                // here we expose a simple method with three arguments
                // that returns an object
                return {
                    this_is: "an object"
                };
            }
        },
        remote: {
            helloWorld:{
                // here we tell the Rpc object to stub a method helloWorld for us
            }
        }
    });

Call the methods like this

    rpc.helloWorld(); // easyXDM automatically changes it's behavior depending on the presence of callback methods for `success` and for `error`. 

The Rpc configurations local and remote properties can be left out if empty. Both properties can have multiple methods defined.

When calling the stubs you can provide up to two callback functions after the expected arguments, the first one being the method that will receive the callback in case of a success, and the next the method that will receive the callback in case of an error.

If an error occurs in the execution of the stubbed method then this will be caught and passed back to the error handler. This means that you in the body of the exposed method can use throw "custom error"; to return a message, or you can pass a message, and an optional object containing error data to the error callback. If the error handler is present, then this will be passed an object containing the properties

In addition to the local and remote properties, you can set the following

In order for easyXDM.Rpc to use JSON-RPC it needs access to functioning encode/decode methods for JSON, and this can be provided by setting the serializer. If not set easyXDM will try to use the native JSON object, and will even work with the faulty toJSON and evalJSON provided by earlier Prototype Js.

If you want to conditionally include Douglas Crockfords JSON2 library (or any other that will provide window.JSON) then you can add this directly after the script that includes easyXDM

    <script type="text/javascript">
        easyXDM.DomHelper.requiresJSON("http://path/to/json2.js");
    </script>

This will only include it if not natively supported.

An rpc object can be teared down (iframe removed etc) using

    rpc.destroy();

The shipped /cors/ interface

Since either end is free to use AJAX etc the Rpc object can be used to easily expose enable cross-domain AJAX. For this the library comes with a default /cors/index.html (/cors/) document that exposes a method request(object config, function successFn, function errorFn), where config can have the following properties:

If the request succeeds the success handler will be passed an object with the following properties

If the request fail the error handler will be passed an object with the following properties

This is how you can use it:

    var rpc = new easyXDM.Rpc({
        remote: "http://foo.bar/cors/"
    },
    {
        remote: {
            request: {}
        }
    });

    rpc.request({
        url: "/resource/x/y/z/",
        method: "POST",
        data: {foo: "bar", bar: "foo"}
    }, function(response){
        alert(response.data);
    });

easyXDM.noConflict

If you want two or more instances of easyXDM to run on the same page, you can put your instance into a namespace using easyXDM.noConflict method. This method returns control of easyXDM global object to the other library and returns an instance of itself.

This is useful if you embed your code on the page and cannot guarantee that it does not already define window.easyXDM.

It also takes a single argument, a string representation of the namespace. We need it to get access to the instance in the parent window (when using SameOriginTransport).

Example:

    // Let's assume we already have an instance of easyXDM on the page, but
    // we need to load another one and put it under PROJECT.easyXDM. Here is
    // how you do it.
    var PROJECT = { easyXDM: easyXDM.noConflict("PROJECT") };

For more information

There are several examples and demos available through the main website, and in the documentation.

Tests

For development a test suit is used - you can run this here:

License

easyXDM is distributed under the MIT license. Please keep the exisisting headers.

Attribution

Main developer Øyvind Sean Kinsey - oyvind@kinsey.no, @okinsey, http://kinsey.no

The following has contributed to the project