• Stars
    star
    311
  • Rank 129,774 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 11 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple PostMessage communication library.

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.

More Repositories

1

across-tabs

Easy communication between cross-origin browser tabs. Simplified "CORS"ing!
JavaScript
1,645
star
2

lua-resty-rabbitmqstomp

Opinionated Lua RabbitMQ client library for the ngx_lua apps based on the cosocket API
Lua
192
star
3

dom-comparator

A JS Library that compares two DOM Nodes and outputs what changed between the two.
JavaScript
154
star
4

superelasticsearch

An extended version of the official Elasticsearch Python client.
Python
64
star
5

agentredrabbit

Transport agent that moves data from Redis to RabbitMQ
Python
61
star
6

angularjs-tips

Tips and tricks for doing things in AngularJS framework.
48
star
7

heybot

🤖 A sleek command based Skype chat bot for your team
JavaScript
25
star
8

vwo-node-sdk

VWO Node SDK for server-side testing
JavaScript
22
star
9

q-directives

A faster directive system for Angular.js
JavaScript
19
star
10

vnc

VNC server application for iOS Simulators running on mac machines
C
16
star
11

choti-moti-baaten

Small statements with huge amount of wisdom, knowledge and humour.
SCSS
14
star
12

vwo-python-sdk

VWO Python SDK for server-side A/B Testing
Python
14
star
13

lua-resty-pubsub

Lua Pubsub client driver for the ngx_lua based on the cosocket API
Lua
13
star
14

rest-api-testing-framework

RestAssured based API testing framework
Java
12
star
15

vwo-php-sdk

VWO PHP SDK for server-side A/B Testing
PHP
11
star
16

wingify.github.io

Engineering blog @ Wingify
JavaScript
11
star
17

reconner

A script for automating the reconnaissance process which is the initial phase of a pentesting activity
Shell
10
star
18

vwo-java-sdk

VWO Java SDK for server-side A/B Testing
Java
9
star
19

vwo-android

VWO Android SDK
Java
9
star
20

kroomsa

A search engine for the curious
Python
8
star
21

vwo-react-native

React Native wrapper for VWO A/B testing
Java
7
star
22

cookie-jar.js

A fast and compact micro-cookie manager.
JavaScript
7
star
23

angular-time-picker

🕗 Angular directive for time picker
JavaScript
7
star
24

bayesian-duration-calculator

https://engineering.wingify.com/posts/maths-behind-bayesian-duration-calculator/
JavaScript
6
star
25

vwo-dotnet-sdk

VWO .NET SDK for server-side A/B Testing
C#
5
star
26

vwo-python-sdk-example

Provides a basic demo of how server-side works with VWO Python SDK
Python
4
star
27

marque

Tag Versioning Helper
JavaScript
4
star
28

vwo-ios-sdk

VWO iOS SDK
Objective-C
4
star
29

Oldmonk

Assorted scala utilities - deduplicators, free algebras,
Scala
3
star
30

vwo-dotnet-sdk-example

Provides a basic demo of how server-side works with VWO .NET SDK
C#
3
star
31

bumblebee

Experimental Slack bot for you to interact with your VWO account
Python
3
star
32

vwo-node-sdk-example

Provides a basic demo of how server-side works with VWO NodeJs SDK
JavaScript
3
star
33

vwo-go-sdk

VWO Go SDK for server-side testing
Go
2
star
34

vwo-ruby-sdk

VWO Ruby SDK for server-side A/B Testing
Ruby
2
star
35

vwo-cordova-plugin

VWO Cordova Plugin
Java
2
star
36

vwo-php-sdk-example

Provides a basic demo of how server-side works with VWO PHP SDK
PHP
2
star
37

vwo-react-native-demo

VWO React native app
JavaScript
2
star
38

vwo-java-sdk-example

Provides a basic demo of how server-side works with VWO Java SDK
HTML
2
star
39

kafka-streams-stateful-example

Example to demonstrate aggregation in Kafka Streams
Java
2
star
40

vwo-flutter-sdk

VWO Flutter Wrapper
Dart
2
star
41

vwo-mobile-android

2
star
42

nextjs-vwo-js-sdk

Integrating VWO JavaScript SDK in Next.js
TypeScript
1
star
43

vwo-ruby-sdk-ror-example

Provides a basic demo of ROR app how server-side works with VWO Ruby SDK
Ruby
1
star
44

vwo-tvos-sdk

VWO A/B Testing SDK for tvOS
Objective-C
1
star
45

vwo-ruby-sdk-example

Provides a basic demo of how server-side works with VWO Ruby SDK
HTML
1
star
46

json-minify

1
star
47

vwo-android-snapshot

Snapshot repository for Android VWO SDK.
1
star
48

crypto-js

1
star
49

vwo-prestashop-plugin

PHP
1
star
50

ui-router

angular ui router
JavaScript
1
star
51

vwo-go-sdk-example

Provides a demo of how server-side works with VWO Go SDK
HTML
1
star