1 ;WinChan = (function() {
2 var RELAY_FRAME_NAME = "__winchan_relay_frame";
5 // a portable addListener implementation
6 function addListener(w, event, cb) {
7 if(w.attachEvent) w.attachEvent('on' + event, cb);
8 else if (w.addEventListener) w.addEventListener(event, cb, false);
11 // a portable removeListener implementation
12 function removeListener(w, event, cb) {
13 if(w.detachEvent) w.detachEvent('on' + event, cb);
14 else if (w.removeEventListener) w.removeEventListener(event, cb, false);
17 // checking for IE8 or above
18 function isInternetExplorer() {
19 var rv = -1; // Return value assumes failure.
20 if (navigator.appName === 'Microsoft Internet Explorer') {
21 var ua = navigator.userAgent;
22 var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
23 if (re.exec(ua) != null)
24 rv = parseFloat(RegExp.$1);
29 // checking Mobile Firefox (Fennec)
32 // We must check for both XUL and Java versions of Fennec. Both have
33 // distinct UA strings.
34 var userAgent = navigator.userAgent;
35 return (userAgent.indexOf('Fennec/') != -1) || // XUL
36 (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1); // Java
41 // feature checking to see if this platform is supported at all
42 function isSupported() {
43 return (window.JSON && window.JSON.stringify &&
44 window.JSON.parse && window.postMessage);
47 // given a URL, extract the origin
48 function extractOrigin(url) {
49 if (!/^https?:\/\//.test(url)) url = window.location.href;
50 var m = /^(https?:\/\/[\-_a-zA-Z\.0-9:]+)/.exec(url);
55 // find the relay iframe in the opener
56 function findRelay() {
57 var loc = window.location;
58 var frames = window.opener.frames;
59 var origin = loc.protocol + '//' + loc.host;
60 for (var i = frames.length - 1; i >= 0; i--) {
62 if (frames[i].location.href.indexOf(origin) === 0 &&
63 frames[i].name === RELAY_FRAME_NAME)
72 var isIE = isInternetExplorer();
77 * (IE SPECIFIC) 1. caller adds relay iframe (served from trusted domain) to DOM
78 * 2. caller opens window (with content from trusted domain)
79 * 3. window on opening adds a listener to 'message'
80 * (IE SPECIFIC) 4. window on opening finds iframe
81 * 5. window checks if iframe is "loaded" - has a 'doPost' function yet
82 * (IE SPECIFIC5) 5a. if iframe.doPost exists, window uses it to send ready event to caller
83 * (IE SPECIFIC5) 5b. if iframe.doPost doesn't exist, window waits for frame ready
84 * (IE SPECIFIC5) 5bi. once ready, window calls iframe.doPost to send ready event
85 * 6. caller upon reciept of 'ready', sends args
88 open: function(opts, cb) {
89 if (!cb) throw "missing required callback argument";
91 // test required options
93 if (!opts.url) err = "missing required 'url' parameter";
94 if (!opts.relay_url) err = "missing required 'relay_url' parameter";
95 if (err) setTimeout(function() { cb(err); }, 0);
97 // supply default options
98 if (!opts.window_name) opts.window_name = null;
99 if (!opts.window_features || isFennec()) opts.window_features = undefined;
101 // opts.params may be undefined
105 // sanity check, are url and relay_url the same origin?
106 var origin = extractOrigin(opts.url);
107 if (origin !== extractOrigin(opts.relay_url)) {
108 return setTimeout(function() {
109 cb('invalid arguments: origin of url and relay_url must match');
116 // first we need to add a "relay" iframe to the document that's served
117 // from the target domain. We can postmessage into a iframe, but not a
119 iframe = document.createElement("iframe");
120 // iframe.setAttribute('name', framename);
121 iframe.setAttribute('src', opts.relay_url);
122 iframe.style.display = "none";
123 iframe.setAttribute('name', RELAY_FRAME_NAME);
124 document.body.appendChild(iframe);
125 messageTarget = iframe.contentWindow;
128 var w = window.open(opts.url, opts.window_name, opts.window_features);
130 if (!messageTarget) messageTarget = w;
132 var req = JSON.stringify({a: 'request', d: opts.params});
136 if (iframe) document.body.removeChild(iframe);
141 } catch (securityViolation) {
142 // This happens in Opera 12 sometimes
143 // see https://github.com/mozilla/browserid/issues/1844
144 messageTarget.postMessage(CLOSE_CMD, origin);
147 w = messageTarget = undefined;
150 addListener(window, 'unload', cleanup);
152 function onMessage(e) {
154 var d = JSON.parse(e.data);
155 if (d.a === 'ready') messageTarget.postMessage(req, origin);
156 else if (d.a === 'error') {
161 } else if (d.a === 'response') {
162 removeListener(window, 'message', onMessage);
163 removeListener(window, 'unload', cleanup);
173 addListener(window, 'message', onMessage);
182 // IE7 blows up here, do nothing
188 onOpen: function(cb) {
190 var msgTarget = isIE ? findRelay() : window.opener;
191 if (!msgTarget) throw "can't find relay frame";
192 function doPost(msg) {
193 msg = JSON.stringify(msg);
194 if (isIE) msgTarget.doPost(msg, o);
195 else msgTarget.postMessage(msg, o);
198 function onMessage(e) {
199 // only one message gets through, but let's make sure it's actually
200 // the message we're looking for (other code may be using
201 // postmessage) - we do this by ensuring the payload can
202 // be parsed, and it's got an 'a' (action) value of 'request'.
205 d = JSON.parse(e.data);
207 if (!d || d.a !== 'request') return;
208 removeListener(window, 'message', onMessage);
211 // this setTimeout is critically important for IE8 -
212 // in ie8 sometimes addListener for 'message' can synchronously
213 // cause your callback to be invoked. awesome.
214 setTimeout(function() {
215 cb(o, d.d, function(r) {
217 doPost({a: 'response', d: r});
224 if (e.data === CLOSE_CMD) {
225 try { window.close(); } catch (o_O) {}
228 addListener(isIE ? msgTarget : window, 'message', onMessage);
229 addListener(isIE ? msgTarget : window, 'message', onDie);
231 // we cannot post to our parent that we're ready before the iframe
232 // is loaded. (IE specific possible failure)
234 doPost({a: "ready"});
236 // this code should never be exectued outside IE
237 addListener(msgTarget, 'load', function(e) {
238 doPost({a: "ready"});
242 // if window is unloaded and the client hasn't called cb, it's an error
243 var onUnload = function() {
245 // IE8 doesn't like this...
246 removeListener(isIE ? msgTarget : window, 'message', onDie);
248 if (cb) doPost({ a: 'error', d: 'client closed window' });
250 // explicitly close the window, in case the client is trying to reload or nav
251 try { window.close(); } catch (e) { }
253 addListener(window, 'unload', onUnload);
256 removeListener(window, 'unload', onUnload);
263 open: function(url, winopts, arg, cb) {
264 setTimeout(function() { cb("unsupported browser"); }, 0);
266 onOpen: function(cb) {
267 setTimeout(function() { cb("unsupported browser"); }, 0);