Skip to content
Browse files

fake git submodule from git@github.com:johnjbarton/RESTChannel.git

  • Loading branch information...
1 parent 36bdd45 commit f457dba5a40f0d6de3412213e84c4030ad02f0d4 @johnjbarton committed Apr 6, 2012
View
275 extension/RESTChannel/RESTChannel.js
@@ -0,0 +1,275 @@
+// Google BSD license http://code.google.com/google_bsd_license.html
+// Copyright 2011 Google Inc. johnjbarton@google.com
+
+/*globals window console */
+
+
+// Don't use AMD here, it's too hard to sync the load events
+
+window.RESTChannel = (function() {
+
+ var debug = false;
+
+ var msgNumber = 0;
+ var pending = {};
+
+ function Connection() {
+ this.registry = {};
+ }
+
+ Connection.prototype = {
+
+ serial: function(onOk, onErr) {
+ if (!onOk && !onErr) {
+ throw new Error("RESTChannel Connection: No response or error handler");
+ }
+ var serial = ++msgNumber;
+ pending[serial] = {
+ ok: onOk,
+ err: onErr
+ };
+ return serial;
+ },
+
+ attach: function(port, onAttach) {
+ this.port = port;
+ },
+
+ close: function() {
+ this.port.close();
+ },
+
+ // Commands to remote
+ //
+ optionsObject: function(url, onOk, onErr) {
+ this.port.postMessage({
+ method: 'OPTIONS',
+ url: url,
+ serial: this.serial(onOk, onErr)
+ });
+ },
+
+ getObject: function(url, onOk, onErr) {
+ this.port.postMessage({
+ method: 'GET',
+ url: url,
+ serial: this.serial(onOk, onErr)
+ });
+ },
+
+ putObject: function(url, obj, onOk, onErr) {
+ this.port.postMessage({
+ method: 'PUT',
+ url: url,
+ body: obj,
+ serial: this.serial(onOk, onErr)
+ });
+ },
+
+ postObject: function(url, obj, onOk, onErr) {
+ this.port.postMessage({
+ method: 'POST',
+ url: url,
+ body: obj,
+ serial: this.serial(onOk, onErr)
+ });
+ },
+
+ deleteObject: function(url, onOk, onErr) {
+ this.port.postMessage({
+ method: 'DELETE',
+ url: url,
+ serial: this.serial(onOk, onErr)
+ });
+ },
+
+ respond: function(serial, obj) {
+ this.port.postMessage({
+ method: 'REPLY',
+ url: '/',
+ status: 200,
+ serial: serial,
+ body: obj
+ });
+ },
+
+ // System 'options', either all registered URLs or all methods at a URL
+ options: function(obj) {
+ var service = this.registry[obj.url];
+ if (service) {
+ var keys = [];
+ do {
+ keys = keys.concat( Object.keys(service) );
+ service = Object.getPrototypeOf(service);
+ } while(service !== Object.prototype);
+ return keys;
+ } else {
+ return Object.keys(this.registry);
+ }
+ },
+
+ // Commands from remote
+ //
+ register: function(url, handler) {
+ this.registry[url] = handler;
+ // return a 'reference' to this handler
+ return {url: url};
+ },
+
+ dispatch: function(msgObj) {
+ var method = msgObj.method.toLowerCase();
+ var service = this.registry[msgObj.url];
+ if (service && (method in service) ) {
+ // Call service with the connection and the object sent (if any).
+ return service[method](this, msgObj.body);
+ } else {
+ if (method === 'options') {
+ return this.options(msgObj);
+ }
+ }
+ }
+ };
+
+ function RESTChannel(port, connection) {
+ this.connection = connection;
+ port.onmessage = this._onmessage.bind(this);
+ port.start();
+ this.connection.attach(port);
+ }
+
+ var methods = [
+ 'REPLY',
+ 'OPTIONS',
+ 'GET',
+ 'PUT',
+ 'POST',
+ 'DELETE'
+ ];
+
+ RESTChannel.prototype = {
+
+ _badRequest: function(obj) {
+ obj.status = 400;
+ obj.reason = 'Bad Request';
+ this.connection.respond(null, obj);
+ this.connection.close(); // you had your chance, you blew it.
+ },
+
+ _notImplemented: function(serial, obj) {
+ obj.status = 501;
+ obj.reason = "Not Implemented";
+ this.connection.respond(serial, obj);
+ },
+
+ _envelop: function(obj) {
+ return {
+ url: obj.url,
+ method: obj.method,
+ serial: obj.serial
+ };
+ },
+
+ _onmessage: function(event) {
+
+ var msgObj = this._validate(event);
+ if (debug) {
+ console.log('recv: ', msgObj);
+ }
+
+ if (msgObj) {
+ if (msgObj.method === 'REPLY') {
+ var callbacks = pending[msgObj.serial];
+ if (callbacks) {
+ var status = msgObj.status;
+ if (status >= 200 && status < 300 && callbacks.ok) {
+ callbacks.ok(msgObj.body, msgObj);
+ } else if (callbacks.err) {
+ callbacks.err(msgObj);
+ } else {
+ console.error("RESTChannel response but no handlers", msgObj);
+ }
+ } else {
+ console.error("RESTChannel response but no pending message", msgObj);
+ }
+ } else {
+ var envelop = this._envelop(msgObj);
+ var response = this.connection.dispatch(msgObj);
+ if (response) {
+ this.connection.respond(envelop.serial, response);
+ } else {
+ return this._notImplemented(envelop.serial, envelop);
+ }
+ }
+ }
+ },
+
+ _validate: function(event) {
+ if (!event) {
+ return this._badRequest({message: 'No event'});
+ }
+ var msgObj = event.data;
+ if (!msgObj) {
+ return this._badRequest({message: 'No event.data'});
+ }
+ var method = msgObj.method;
+ if (!method || methods.indexOf(method) === -1) {
+ return this._badRequest({
+ message: 'Unknown Method',
+ method: method,
+ url: msgObj.url,
+ serial: msgObj.serial
+ });
+ }
+ var serial = msgObj.serial;
+ if (!serial && method !== 'REPLY') {
+ return this._badRequest({message: 'No serial number'});
+ }
+ var url = msgObj.url;
+ if (!url && method !== 'REPLY') {
+ return this._badRequest({message: 'No URL', serial: serial});
+ }
+ if (debug) {
+ console.log(msgObj.serial+' valid '+msgObj.method+' '+msgObj.url, msgObj);
+ }
+ return msgObj;
+ }
+
+ };
+
+ function accept(connection, onAttach, event) {
+ if (event.data && event.data === "RESTChannel") {
+ if (debug) {
+ console.log(window.location + " RESTChannel accept ", event);
+ }
+ var port = event.ports[0];
+ onAttach( new RESTChannel(port, connection) );
+ } // else not for us
+ }
+
+ function listen(connection, onAttach) {
+ var onIntroduction = accept.bind(null, connection, onAttach);
+ window.addEventListener('message', onIntroduction);
+ return function dispose() {
+ window.removeEventListener('message', onIntroduction);
+ };
+ }
+
+ function talk(listenerWindow, connection, onAttach) {
+ var channel = new window.MessageChannel();
+ if (debug) {
+ console.log('talk post');
+ }
+ listenerWindow.postMessage('RESTChannel', '*', [channel.port2]);
+ onAttach( new RESTChannel(channel.port1, connection) );
+ return function dispose() {
+ channel.port1.close();
+ };
+ }
+
+ return {
+ talk: talk,
+ listen: listen,
+ Connection: Connection
+ };
+
+}());
View
71 extension/RESTChannel/RESTEventSource.js
@@ -0,0 +1,71 @@
+
+function RESTEventSource() {
+}
+
+function makeEventSource(errBack) {
+ // Private Impl
+
+ var listeners = [];
+
+ var privateImpl = {
+ dispatch: function(jsonEvent) {
+ listeners.forEach(function(listener) {
+ var body = listener.body;
+ listener.connection.postObject(
+ body.url, // callback addr
+ {
+ event: jsonEvent // Should not send all of this...
+ },
+ errBack
+ );
+ });
+ }
+ };
+
+ // check that the remote side gave us an addr to fire events at
+ function validate(body) {
+ return body.url;
+ }
+
+ // Public API (generic event dispatch)
+ var publicAPI = {
+
+ get: function(connection) {
+ listeners.some(function(listener) {
+ if (listener.connection === connection) {
+ return listener.body;
+ }
+ });
+ },
+
+ put: function (connection, body) {
+ if ( validate(body) ) {
+ listeners.push({
+ body: body,
+ connection: connection
+ });
+ return {totalListeners: listeners.length};
+ } else {
+ return {error: "URL required"};
+ }
+ },
+
+ 'delete': function(connection, obj) {
+ var found = -1;
+ listeners.some(function(listener, index) {
+ if (listener.connection === connection) {
+ found = index;
+ return true;
+ }
+ });
+ if (found !== -1) {
+ listeners.splice(found, 1);
+ }
+ }
+ };
+
+ return {
+ 'iface': publicAPI,
+ 'impl': privateImpl
+ };
+}
View
100 extension/RESTChannel/test/childFrame.html
@@ -0,0 +1,100 @@
+<html>
+<head>
+<script src="../RESTChannel.js"></script>
+<script>
+// Google BSD license http://code.google.com/google_bsd_license.html
+// Copyright 2011 Google Inc. johnjbarton@google.com
+
+
+/*globals RESTChannel window console */
+
+window.addEventListener('load', function() {
+
+ console.log(window.location.toString() + ' load ');
+
+ // This object will be our 'parent' connection
+ //
+ var connection = new RESTChannel.Connection();
+
+ // Callback fires as soon as possible.
+ //
+ function onAttach() {
+
+ console.log(window.location + ' attach');
+
+ // Add initial methods for the parent to use
+ //
+ connection.register(
+ 'hello', // URL for feature
+ {
+ // feature documentation
+ options: function() {
+ return {
+ put: 'body ignored; {message:}'
+ };
+ },
+
+ // Feature implementation
+ put: function (obj) {
+ return {message:'hey'};
+ }
+ }
+ );
+
+ function childErr(err) {
+ console.error("Child recvd err", err);
+ }
+
+ // Send something to the 'parent'
+ //
+ connection.putObject(
+ 'hello', // at this URL
+ {message:'I am your child'}, // store this object
+ function(reply) { // then call me
+ // Just log for the demo
+ console.log("Child hears: "+reply.message, reply);
+ },
+ childErr // or fail
+ );
+
+ // Test the OPTIONS command.
+
+ function getMethods(urls) {
+ urls.forEach(function(url) {
+ connection.optionsObject(
+ url,
+ function(reply) {
+ console.log('Child finds ' + url +' has ', reply);
+ },
+ childErr
+ );
+ });
+ }
+
+ connection.optionsObject(
+ '*',
+ function(reply) {
+ console.log("Child finds options: ", reply);
+ getMethods(reply);
+ },
+ function(err) {
+ console.error("Parent recvd err", err);
+ }
+ );
+
+
+ }
+
+ console.log(window.location + ' talking ');
+
+ RESTChannel.talk(window.parent, connection, onAttach);
+});
+
+
+
+
+</script>
+</head>
+<body>
+</body>
+</html>
View
66 extension/RESTChannel/test/eventChild.html
@@ -0,0 +1,66 @@
+<html>
+<head>
+<script src="../RESTChannel.js"></script>
+<script>
+// Google BSD license http://code.google.com/google_bsd_license.html
+// Copyright 2011 Google Inc. johnjbarton@google.com
+
+
+/*globals RESTChannel window console */
+
+window.addEventListener('load', function() {
+
+ console.log(window.location.toString() + ' load ');
+
+ var connection = new RESTChannel.Connection();
+
+ function onAttach() {
+
+ console.log(window.location + ' attach');
+
+ // Event Handler Defined
+ //
+ var handler = // becomes {url: 'mouse.overed'}
+ connection.register(
+ 'mouse.overed', // URL for handler
+ {
+ // handle event
+ post: function (connection, obj) {
+ console.log("eventChild got event: ", obj);
+ return {message:'hey'};
+ }
+ }
+ );
+
+ function childErr(err) {
+ console.error("Child recvd err", err);
+ }
+
+ // addEventListener is put remote ref to event name
+ //
+ connection.putObject(
+ 'mouse.over', // event name at this URL
+ handler, // remote ref
+ function(reply) { // then call me
+ console.log("Child hears: "+reply.message, reply);
+ },
+ childErr // or fail
+ );
+
+ //connection.addEventListener('mouse.over', handler);
+
+ }
+
+ console.log(window.location + ' talking ');
+
+ RESTChannel.talk(window.parent, connection, onAttach);
+});
+
+
+
+
+</script>
+</head>
+<body>
+</body>
+</html>
View
77 extension/RESTChannel/test/eventParent.html
@@ -0,0 +1,77 @@
+<html>
+<head>
+<script src="../RESTChannel.js"></script>
+<script src="../RESTEventSource.js"></script><script>
+// Google BSD license http://code.google.com/google_bsd_license.html
+// Copyright 2011 Google Inc. johnjbarton@google.com
+
+
+/*globals RESTChannel window console */
+
+console.log(window.location + ' listening ');
+
+var connection = new RESTChannel.Connection();
+
+function cloneOwn(obj) {
+ var result = {};
+ Object.keys(obj).forEach(function(prop) {
+ result[prop] = obj[prop];
+ });
+ return result;
+}
+
+function fnf() {
+ console.log('Fired and forgetting: ', arguments);
+}
+
+var Mouse = {
+ over: function() {
+
+ var eventSource = makeEventSource(fnf);
+
+ var testElement = document.querySelector('.testElement');
+ testElement.addEventListener(
+ 'mouseover',
+ function eventDelegator(event) {
+ console.log('parent ', event);
+ var jsonEvent = {
+ type: 'mouseover',
+ timestamp: event.timeStamp
+ };
+ eventSource.impl.dispatch(jsonEvent);
+ }
+ );
+
+ return eventSource.iface;
+ }
+};
+
+function onAttach() {
+
+ console.log(window.location + ' attach');
+
+ // a servlet-like module
+ connection.register('mouse.over', Mouse.over());
+}
+
+var onUnload = RESTChannel.listen(connection, onAttach);
+window.addEventListener('unload', onUnload);
+
+window.addEventListener('load', function() {
+ console.log(window.location.toString() + ' load ');
+});
+
+
+</script>
+<style>
+.testElement {
+ background-color: #DFFF95;
+}
+</style>
+</head>
+<body>
+<p>Open the debugging console to see the conversation logged</p>
+<iframe class='RESTChannelClient' src="eventChild.html"></iframe>
+<p class='testElement'>Move your mouse over this element to fire the events</p>
+</body>
+</html>
View
62 extension/RESTChannel/test/parentFrame.html
@@ -0,0 +1,62 @@
+<html>
+<head>
+<script src="../RESTChannel.js"></script>
+<script>
+// Google BSD license http://code.google.com/google_bsd_license.html
+// Copyright 2011 Google Inc. johnjbarton@google.com
+
+
+/*globals RESTChannel window console */
+
+console.log(window.location + ' listening ');
+
+var connection = new RESTChannel.Connection();
+
+function onAttach() {
+
+ console.log(window.location + ' attach');
+
+ connection.register('hello', {
+ put: function (obj) {
+ return {message:'Did you do your homework?'};
+ }
+ }
+ );
+
+ connection.putObject(
+ 'hello',
+ {message:'I am your parent'},
+ function(reply) {
+ console.log("Parent hears: "+reply.message, reply);
+ },
+ function(err) {
+ console.error("Parent recvd err", err);
+ }
+ );
+
+ connection.optionsObject(
+ '*',
+ function(reply) {
+ console.log("Parent finds options: ", reply);
+ },
+ function(err) {
+ console.error("Parent recvd err", err);
+ }
+ );
+}
+
+var onUnload = RESTChannel.listen(connection, onAttach);
+window.addEventListener('unload', onUnload);
+
+window.addEventListener('load', function() {
+ console.log(window.location.toString() + ' load ');
+});
+
+
+</script>
+</head>
+<body>
+<p>Open the debugging console to see the conversation logged</p>
+<iframe class='RESTChannelClient' src="childFrame.html"></iframe>
+</body>
+</html>

0 comments on commit f457dba

Please sign in to comment.
Something went wrong with that request. Please try again.