1<html> 2 <head> 3 <script src="http://apprtc.appspot.com/_ah/channel/jsapi"></script> 4 </head> 5 <!-- 6 Helper HTML that redirects Google AppEngine's Channel API to Objective C. 7 This is done by hosting this page in an iOS application. The hosting 8 class creates a UIWebView control and implements the UIWebViewDelegate 9 protocol. Then when there is a channel message, it is encoded in an IFRAME. 10 That IFRAME is added to the DOM which triggers a navigation event 11 |shouldStartLoadWithRequest| in Objective C which can then be routed in the 12 application as desired. 13 --> 14 <body onbeforeunload="closeSocket()" onload="openSocket()"> 15 <script type="text/javascript"> 16 // QueryString is copy/pasta from 17 // chromium's chrome/test/data/media/html/utils.js. 18 var QueryString = function () { 19 // Allows access to query parameters on the URL; e.g., given a URL like: 20 // http://<url>/my.html?test=123&bob=123 21 // parameters can now be accessed via QueryString.test or 22 // QueryString.bob. 23 var params = {}; 24 25 // RegEx to split out values by &. 26 var r = /([^&=]+)=?([^&]*)/g; 27 28 // Lambda function for decoding extracted match values. Replaces '+' 29 // with space so decodeURIComponent functions properly. 30 function d(s) { return decodeURIComponent(s.replace(/\+/g, ' ')); } 31 32 var match; 33 while (match = r.exec(window.location.search.substring(1))) 34 params[d(match[1])] = d(match[2]); 35 36 return params; 37 } (); 38 39 var channel = null; 40 var socket = null; 41 42 function openSocket() { 43 if (!QueryString.token || !QueryString.token.match(/^[A-z0-9_-]+$/)) { 44 // Send error back to ObjC. This will assert in GAEChannelClient.m. 45 sendMessageToObjC("JSError:Missing/malformed token parameter " + 46 QueryString.token); 47 throw "Missing/malformed token parameter: " + QueryString.token; 48 } 49 channel = new goog.appengine.Channel(QueryString.token); 50 socket = channel.open({ 51 'onopen': function() { 52 sendMessageToObjC("onopen"); 53 }, 54 'onmessage': function(msg) { 55 sendMessageToObjC("onmessage:" + 56 encodeURIComponent(JSON.stringify(msg.data))); 57 }, 58 'onclose': function() { 59 sendMessageToObjC("onclose"); 60 }, 61 'onerror': function(err) { 62 sendMessageToObjC("onerror:" + 63 encodeURIComponent(JSON.stringify(err.code)) + 64 ":message:" + 65 encodeURIComponent(JSON.stringify(err.description))); 66 } 67 }); 68 } 69 70 function closeSocket() { 71 socket.close(); 72 } 73 74 // Add an IFRAME to the DOM to trigger a navigation event. Then remove 75 // it as it is no longer needed. Only one event is generated. 76 function sendMessageToObjC(message) { 77 var iframe = document.createElement("IFRAME"); 78 iframe.setAttribute("src", "js-frame:" + message); 79 // For some reason we need to set a non-empty size for the iOS6 80 // simulator... 81 iframe.setAttribute("height", "1px"); 82 iframe.setAttribute("width", "1px"); 83 document.documentElement.appendChild(iframe); 84 iframe.parentNode.removeChild(iframe); 85 } 86 </script> 87 </body> 88</html> 89