Coder Social home page Coder Social logo

please.js's Introduction

please.js

please.js is a Request/Response based wrapper around the PostMessage API that makes use of jQuery Promises. Ever faced trouble communicating between two frames on different domains? Trouble not, just say please!

Here's a quick example to load an iframe window's location.

var frameWindow = $('iframe').get(0).contentWindow;

please(frameWindow).call('window.location.reload');

Here's another one that fetches you the child document's height:

var frameWindow = $('iframe').get(0).contentWindow;

please(frameWindow).get('document.height').then(function (height) {
   console.log('child document\'s height:', height);
});

Note: If you are using the above code on parent's document.onready function, it needs to be wrapped in frame.onload event.

$(document).ready(function () {
   var $frame = $('iframe');
   $frame.load(function () {
      var frameWindow = $frame.get(0).contentWindow;
      please(frameWindow).get('document.height').then(function (height) {
         console.log('child document\'s height:', height);
      });
   });
})

Downloads

Documentation

Setting up

please.js is based on top of jQuery and the jQuery Promise API. jQuery version 1.6 or above is preferred. To make the communication between two windows on different domains work, both of them must be injected with the same version of jQuery and please.js.

Hint: `undefined` values become `null` values in cross-frame communication. This is because JSON serialization does not support `undefined` (e.g. `JSON.stringify([undefined, 'x'])` becomes `[null, 'x']`).

The please global object

Getting started with please.js is easy:

  • Initialize please. In both the windows (frames), add the below code:
please.init(window);

Before initialization however, you must make sure that both the current window and the target window have please.js loaded and initialized.

  • Set the default targetWindow (recipient). Especially useful if the communication is needed only between two frames.
please.defaults({
    // reference to the window to send messages to
    targetWindow: otherWindow,

    // what the target window's origin must be for the communication to facilitate
    targetOrigin: otherWindowOrigin,

    // conditionally restrict communication
    sourceOrigin: function (messageEvent) {
      return (/^https?://example.com/.test(messageEvent.origin));
    }
});
  • To send a message to the targetWindow set using defaults, just call the methods on the please global object.
please.call('window.location.reload');
please.get('window.location.href').then(function (href) {
    // use href variable here
});
  • If you need to send a message to a targetWindow other than the one set using defaults, you could use please as a function and provide targetWindow and targetOrigin as parameters
// navigate the parent window away to some other url.
// works only if the parent window is on *.example.com
please(parent, '*.example.com').set('window.location.href', 'http://www.google.com');

please methods

defaults please.defaults( objectHash )

Sets the default targetWindow to send message to, the targetOrigin of that window, and a test for conditions under which communication should be sourceOrigin.

please.defaults({
    // reference to the window to send messages to
    targetWindow: $('iframe').get(0).contentWindow,

    // what the target window's origin must be for the communication to facilitate
    targetOrigin: '*.example.com',

    // conditionally restrict communication
    sourceOrigin: function (messageEvent) {
      return (/^https?://example.com/.test(messageEvent.origin));
    }
});

call please.call( functionName, [args...] )

Invokes a function functionName in the other window. Pass any arguments needed after the functionName. Returns a jQuery promise object. After the function is invoked in the other frame, the promise is resolved to the return value of the function. For example:

// in the parent frame, have this code:
function sayHello () {
    return 'Hello from parent frame.';
}
// and in the child frame:
please(parent).call('sayHello').then(function (helloString) {
    console.log('Parent said:', helloString);
})

You could also call a method on another object. Just pass the object name in the functionName as well like object.someMethod. For example:

// in the parent frame:
var SomeObject = {
    someMethod: function () {
        return 'someMethod says hello!';
    }
};
// and in the child frame:
please(parent).call('SomeObject.someMethod').then(function (helloString) {
    console.log('SomeObject.someMethod said:', helloString);
});

If you try to call a function that isn't defined, an error is thrown in the other frame and the error object is passed on to the source window. You can catch the error in the failCallback of the then method.

please(parent).call('someUndefinedFunction').then(function (retval) { // success callback
    // this will never execute
    console.log('response recieved from the parent frame.');
}, function (error) { // failure callback
    console.log('error occured: ', error.stack);
});

The failCallback will also be called even if the function executed in the other frame throws an error.

// in the parent frame:
function errorProneFunction () {
    throw new Error('Something went wrong!');
}
// in the child frame:
please(parent).call('errorProneFunction').then(function (retval) { // success callback
    // this will never execute
    console.log('function returned with value:', retval);
}, function (error) {
    console.log('error:', error.message); // Something went wrong!
});

set please.set( propertyName, propertyValue )

Sets a global variable or a property on an object in the other window. Returns a jQuery promise object. After the property is set, the promise object is resolved.

var someIframeWindow = $('iframe').get(0).contentWindow
please(someIframeWindow).set('someVariable', 'foo').then(function () {
    // execute some code
    please(someIframeWindow).get('someVariable').then(function (someVariable) {
        console.log('someVariable:', someVariable);
    });
});

Note: jQuery 1.8+ supports chaining then callbacks by returning promise objects. For 1.8 and above versions, the above code can be simplified like:

var someIframeWindow = $('iframe').get(0).contentWindow
please(someIframeWindow).set('someVariable', 'foo').then(function () {
    return please(someIframeWindow).get('someVariable')
}).then(function (someVariable) {
    console.log('someVariable:', someVariable);
});;

However, since all postmessage requests are sent in a linear order, and responses are received in the same order, a race condition would be impossible in this case. So calling both requests synchronously would also work:

var someIframeWindow = $('iframe').get(0).contentWindow
please(someIframeWindow).set('someVariable', 'foo');
please(someIframeWindow).get('someVariable').then(function (someVariable) {
    console.log('someVariable:', someVariable);
});;

get please.get( propertyName )

Gets a value of a global variable or a property on an object in the other window. Returns a jQuery promise object that resolves with the value of the property requested.

please(parent).get('document.height', function (documentHeight) {
   console.log('parent document\'s height:', documentHeight);
});

If the variable or property is not defined, it returns undefined.

please(parent).get('someUndefinedProperty').then(function (propertyValue) {
    console.log('value of the property: ', propertyValue); // undefined
});

If the property is requested on an object that is undefined, an error is thrown in the other frame and the promise object fails to resolve. You can catch the error in the failCallback of the then method, just like the please.call method above.

var otherFrame = $('iframe').get(0).contentWindow;
please(otherWindow).get('undef.blahBlah', function (propertyValue) { // success callback
    // this never gets executed
    console.log('value of the property: ', propertyValue);
}, function (error) { // faliure callback
    console.log('an error occured')
});

Roadmap

General changes on the list:

  1. Remove all instances of $.extend and add a custom implementation and use that instead.
  2. Add a polyfill for Object.keys.
  3. Use something else instead of $.globalEval.
  4. We can't really write our own Promise/A+ implementation, and unfortunately must depend on jQuery for that. But it would be good to have an API that is generic enough such that people can use this library with the Promise/A+ of their choice. The assumption would be that the custom implementation conforms to the Promise/A+ spec: http://promises-aplus.github.io/promises-spec/. (Basically removing the dependency on jQuery).

Testing: The tests written so far are mostly functional / end-to-end tests with asynchronous behaviour. I've exposed _please under the please namespace so that each individual function can also be tested synchronously as a unit.

Future Roadmap: These items are good to have to enhance the library.

  1. please.$ is basically a mock jQuery object and does not really need the presence of jQuery in the calling window. It is like a test double that always returns a promise that resolves / rejects based on the return value of the corresponding function call. It would be good to have a generic enough API that can mock any object in the other frame in the calling frame, and use that API to mock jQuery.
  2. Add certain logic such that please.js need not be injected and initialized in both the frames. Only a couple of lines would be needed in the other frame to kickstart the communication.
  3. Add support for communication in Chrome Extensions.

License

The MIT License

Copyright (c) 2013-2016 Wingify Software Pvt. Ltd.

please.js's People

Contributors

fleon avatar joscha avatar prayagverma avatar timse avatar

Stargazers

Mike Peterson avatar Bingo avatar Lan Qingyong avatar Godfery Yang avatar Egemen CEYLAN avatar  avatar Chaitanya Chinni avatar VPZ avatar Tenvi avatar Gábor Mihálcz avatar  avatar Shubham Aggarwal avatar owlonex avatar Huy avatar RoLeRo avatar Name avatar Jude avatar Raymond Seger avatar Laughing avatar sean avatar 尹挚 avatar Diosney avatar Titus Dishon avatar Cat  avatar Stephen Rhyne avatar Robert Schiriac avatar Dustin Falgout avatar  avatar LEI avatar Aditya Agarwal avatar Anton Rusinov avatar Jean Farrugia avatar Ganeshan Venkataraman avatar Martin Brecht-Precht avatar Michiel Peters avatar Ahmed Rafi Ullah avatar Chris White avatar Andrew Starodubtsev avatar Chauncey McAskill avatar Rao Venu avatar Daniel Caldas avatar Maciej Adamczak avatar  avatar Sven Weintuch avatar alwayrun avatar  avatar Ramón Chancay Ortega  avatar Fraz Ahmed avatar Vivek Kumar Bansal avatar Miki Oracle avatar Thomas Marrec avatar Sachin Kanaujia avatar Andre Gagnon avatar  avatar 张飞 avatar M Haidar Hanif avatar Søren Louv-Jansen avatar Dilip avatar Alvaro Naveda avatar Andrew avatar Ryc O'Chet avatar  avatar  avatar Brett Brewer avatar Thomas Kjærgaard avatar Abhinav Pobbati avatar MO avatar Su Yang avatar Nami W avatar James Doyle avatar  avatar Karan Sakhuja avatar Anatoli Nicolae avatar Jacob Kelley avatar Sakurai Makoto avatar Nick Deis avatar  avatar Gaurav Parashar avatar Shamasis Bhattacharya avatar william delmas avatar Guillaume Coderre avatar Andrew Gerst avatar Useff Chase avatar  avatar Bharani avatar Ifiok Jr. avatar Angus H. avatar Nguyen Vu Cuong (Ralph) avatar nhducit avatar Ilya Masalygin avatar Keith avatar Muhammad Adeel avatar Gaurav  avatar Ankur Goel avatar mistertest avatar Carlos Paramio avatar Romain Dardour avatar John Williams avatar Hiroyuki Sano avatar Yousuf Soliman avatar

Watchers

Ankit Jain avatar Josh Yu avatar yury avatar Gaurav Nanda avatar Sparsh Gupta avatar Suchit Puri avatar Dinkar Pundir avatar Vyacheslav Murashkin avatar Kushagra Gour avatar Gaurang Jadia avatar Madhur Ahuja avatar Vaidik Kapoor avatar  avatar James Cloos avatar  avatar hunslater avatar Jacob Kelley avatar Promode avatar Shanil Puri avatar Vijayenthiran Subramaniam avatar  avatar  avatar Abhishek Batra avatar Kishan Rajdev avatar Vivek Bansal avatar Varun Malhotra avatar Mahesh Bansod avatar chhavi avatar Akansh Gulati avatar Swapnil Agarwwal avatar AMAN avatar  avatar Gaurav Parashar avatar Rehaan avatar Anil Kapoor avatar Vijay Aggarwal avatar Yousuf Syed avatar Nitish Mittal avatar squiroid avatar  avatar Shubham Soni avatar Sanjay Shreedharan avatar Sanyam Jain avatar Hardik avatar Punit Gupta avatar  avatar

please.js's Issues

Memory leak

Theres an issue on this line
responses[response.id] = response.data;
This array never gets cleared.

console.log('error parsing json data');

@fleon, @chinchang, what would be a good alternative to this?

issues:

  • we can't rely on console in earlier versions of ie, e.g. ie8.
  • some test suites, e.g. mocha with karma use postMessage - causing the test logs to be polluted with this error message whenever they post a string
  • more generally, it is difficult to encapsulate please in large hostile application environments from postMessages being sent and received elsewhere using other means

it may be a good idea to include a signature in requests to distinguish postMessages made using please?
at the very least i suggest we make this logging optional?

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.