From dc0ffdbdee1c08e649dbcf688b425c2945f4d6e5 Mon Sep 17 00:00:00 2001 From: micgunnu Date: Thu, 25 Sep 2014 12:12:52 +0200 Subject: [PATCH 1/2] Support for async request-respond through post-message --- lib/PluginApi.js | 48 +++++++++++++++++++++++++++++++++++- package.json | 3 +++ test/PluginApi.test.js | 56 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 96 insertions(+), 11 deletions(-) diff --git a/lib/PluginApi.js b/lib/PluginApi.js index dd35755..e304d0b 100644 --- a/lib/PluginApi.js +++ b/lib/PluginApi.js @@ -1,3 +1,4 @@ +var xde = require('cross-domain-events'); var data = {}; var i = 0; @@ -30,12 +31,57 @@ function publish (id, subject) { }); } + + /** + * + * Gardr.reqres + * + * Request-response pattern for sending messages + * between gardr hostplugins and ext banners and plugins. + */ + + // Sends a message to otherWindow with params + // waits for the response, and calls callback upon that. + function request (otherWindow, subject, params, callback) { + xde.sendTo(otherWindow, 'gardr:' + subject, params) + xde.on('gardr:' + subject + ':response', callback); + } + + +// listens to postmessages with subject +// calls the given callback, and upon return +// sends the result back +function respondTo (subject, cb) { + xde.on('gardr:' + subject, onRespondToMsg(subject, cb)) + + function onRespondToMsg (subject, cb) { + + return function (evt) { + // because cb can be async, it is responsible for + // triggering the _onCallbackComplete function + cb(evt, onCallbackComplete.bind(null, subject, evt)); + } + } + + function onCallbackComplete (subject, evt, res) { + xde.sendTo(evt.source, 'gardr:' + subject + ':response', res); + } +} + + var PluginApi = function () { this.id = i++; set(this.id, 'listeners', {}); this.on = subscribe.bind(null, this.id); - this.trigger = publish.bind(null, this.id) + this.trigger = publish.bind(null, this.id); + + // The reqres module enables gardr plugins to communicate after + // the banner has been document writed. + this.reqres = { + request : request, + respondTo : respondTo + } this._reset = function () { set(this.id, 'listeners', {}); }; }; diff --git a/package.json b/package.json index 89c516c..44d03f4 100644 --- a/package.json +++ b/package.json @@ -26,5 +26,8 @@ "karma": "~0.12.1", "karma-browserifast": "~0.4.1", "es5-shim": "~2.3.0" + }, + "dependencies": { + "cross-domain-events": "0.0.2" } } diff --git a/test/PluginApi.test.js b/test/PluginApi.test.js index 7b0c9ab..798791b 100644 --- a/test/PluginApi.test.js +++ b/test/PluginApi.test.js @@ -1,9 +1,20 @@ /*jshint expr: true, nonew: false*/ -var PluginApi = require('../lib/PluginApi.js'); +var PluginApi = require('../lib/PluginApi.js'), + xde = require('cross-domain-events'), + api; describe('PluginApi', function () { + beforeEach(function () { + api = new PluginApi(); + }); + + afterEach(function () { + api._reset(); + }); + describe('constructor', function () { + it('should not throw if no arguments', function () { expect(function () { new PluginApi(); @@ -12,15 +23,6 @@ describe('PluginApi', function () { }); describe('events', function () { - var api; - - beforeEach(function () { - api = new PluginApi(); - }); - - afterEach(function () { - api._reset(); - }); it('should exist', function () { expect(api.on).to.exist; @@ -63,4 +65,38 @@ describe('PluginApi', function () { }).not.to.throw(); }); }); + + describe('reqres', function () { + + describe('end-to-end', function () { + var evtName = 'yoer:bar'; + var reqData = {fooInt : 1337}; + + it('should call request.callback with proper params when there is a responder', function (done) { + callRespondTo(api, evtName); + callRequest(api, evtName, reqData, 1338, done); + }); + }); + }); }); + + +function callRespondTo (api, evtName) { + api.reqres.respondTo(evtName, onXdeCb); + + function onXdeCb (evt, commCb) { + var requestData = evt.data; + var result = requestData.fooInt + 1; + commCb({fooIntResult : result}); + } +} + +function callRequest (api, evtName, reqData, expectedResult, done) { + api.reqres.request(window, evtName, reqData, onXdeCb); + + function onXdeCb (evt) { + var responseData = evt.data; + expect(responseData.fooIntResult).to.equal(expectedResult); + done(); + } +} From 26153d7557eda5fd1e13ef7241b3b4cc5dbf9c72 Mon Sep 17 00:00:00 2001 From: micgunnu Date: Tue, 30 Sep 2014 09:14:25 +0200 Subject: [PATCH 2/2] Removed reqres namespace and added error handling --- lib/PluginApi.js | 25 ++++++++++++++++--------- test/PluginApi.test.js | 30 +++++++++++++++++++----------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/lib/PluginApi.js b/lib/PluginApi.js index e304d0b..7fafc0f 100644 --- a/lib/PluginApi.js +++ b/lib/PluginApi.js @@ -42,16 +42,25 @@ function publish (id, subject) { // Sends a message to otherWindow with params // waits for the response, and calls callback upon that. - function request (otherWindow, subject, params, callback) { - xde.sendTo(otherWindow, 'gardr:' + subject, params) - xde.on('gardr:' + subject + ':response', callback); + function postMessage (subject, params, callback, otherWindow) { + otherWindow = otherWindow || window.top; + + try { + xde.sendTo(otherWindow, 'gardr:' + subject, params); + + xde.on('gardr:' + subject + ':response', function (evt) { + callback(null, evt.data); + }); + } catch(e) { + callback(e); + } } // listens to postmessages with subject // calls the given callback, and upon return // sends the result back -function respondTo (subject, cb) { +function respondToPostMessage (subject, cb) { xde.on('gardr:' + subject, onRespondToMsg(subject, cb)) function onRespondToMsg (subject, cb) { @@ -59,7 +68,7 @@ function respondTo (subject, cb) { return function (evt) { // because cb can be async, it is responsible for // triggering the _onCallbackComplete function - cb(evt, onCallbackComplete.bind(null, subject, evt)); + cb(evt.data, onCallbackComplete.bind(null, subject, evt)); } } @@ -78,10 +87,8 @@ var PluginApi = function () { // The reqres module enables gardr plugins to communicate after // the banner has been document writed. - this.reqres = { - request : request, - respondTo : respondTo - } + this.postMessage = postMessage; + this.respondToPostMessage = respondToPostMessage; this._reset = function () { set(this.id, 'listeners', {}); }; }; diff --git a/test/PluginApi.test.js b/test/PluginApi.test.js index 798791b..6fe784d 100644 --- a/test/PluginApi.test.js +++ b/test/PluginApi.test.js @@ -73,29 +73,37 @@ describe('PluginApi', function () { var reqData = {fooInt : 1337}; it('should call request.callback with proper params when there is a responder', function (done) { - callRespondTo(api, evtName); - callRequest(api, evtName, reqData, 1338, done); + callRespondToPostMessage(api, evtName); + callPostMessage(api, evtName, reqData, 1338, done); }); }); + + describe('errors', function () { + it('should catch xde errors', function () { + api.postMessage('test:event', {}, function (err) { + expect(err).to.exist; + expect(err.message).to.equal('otherWindow does not support postMessage'); + }, 'not a window object'); + }); + }) }); }); -function callRespondTo (api, evtName) { - api.reqres.respondTo(evtName, onXdeCb); +function callRespondToPostMessage (api, evtName) { + api.respondToPostMessage(evtName, onResponseMsg); - function onXdeCb (evt, commCb) { - var requestData = evt.data; - var result = requestData.fooInt + 1; + function onResponseMsg (responseData, commCb) { + var result = responseData.fooInt + 1; commCb({fooIntResult : result}); } } -function callRequest (api, evtName, reqData, expectedResult, done) { - api.reqres.request(window, evtName, reqData, onXdeCb); +function callPostMessage (api, evtName, reqData, expectedResult, done) { + api.postMessage(evtName, reqData, onXdeCb, window); - function onXdeCb (evt) { - var responseData = evt.data; + function onXdeCb (err, responseData) { + expect(err).to.be.null; expect(responseData.fooIntResult).to.equal(expectedResult); done(); }