diff --git a/lib/i18n.js b/lib/i18n.js index 130f0c94dfa92b2e2b9da0bb21f0928393fb1c4e..c6600cfeaae9c33778374f7683e26b2c8e7776a0 100644 --- a/lib/i18n.js +++ b/lib/i18n.js @@ -263,5 +263,7 @@ var format = exports.format = function (fmt, obj, named) { return fmt.replace(/%\(\s*([^)]+)\s*\)/g, function(m, v){ return String(obj[v]); }); + } else { + return fmt; } }; diff --git a/resources/static/common/js/lib/winchan.js b/resources/static/common/js/lib/winchan.js index 7e8188e04bc4f11954f5c39c6eb69b8d8518ca20..b31cd2263105de65cf3f27a70f82036295b7e51e 100644 --- a/resources/static/common/js/lib/winchan.js +++ b/resources/static/common/js/lib/winchan.js @@ -1,5 +1,6 @@ ;WinChan = (function() { var RELAY_FRAME_NAME = "__winchan_relay_frame"; + var CLOSE_CMD = "die"; // a portable addListener implementation function addListener(w, event, cb) { @@ -16,7 +17,7 @@ // checking for IE8 or above function isInternetExplorer() { var rv = -1; // Return value assumes failure. - if (navigator.appName == 'Microsoft Internet Explorer') { + if (navigator.appName === 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) @@ -28,7 +29,11 @@ // checking Mobile Firefox (Fennec) function isFennec() { try { - return (navigator.userAgent.indexOf('Fennec/') != -1); + // We must check for both XUL and Java versions of Fennec. Both have + // distinct UA strings. + var userAgent = navigator.userAgent; + return (userAgent.indexOf('Fennec/') != -1) || // XUL + (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1); // Java } catch(e) {}; return false; } @@ -42,7 +47,7 @@ // given a URL, extract the origin function extractOrigin(url) { if (!/^https?:\/\//.test(url)) url = window.location.href; - var m = /^(https?:\/\/[-_a-zA-Z\.0-9:]+)/.exec(url); + var m = /^(https?:\/\/[\-_a-zA-Z\.0-9:]+)/.exec(url); if (m) return m[1]; return url; } @@ -129,8 +134,16 @@ function cleanup() { if (iframe) document.body.removeChild(iframe); iframe = undefined; - if (w) w.close(); - w = undefined; + if (w) { + try { + w.close(); + } catch (securityViolation) { + // This happens in Opera 12 sometimes + // see https://github.com/mozilla/browserid/issues/1844 + messageTarget.postMessage(CLOSE_CMD, origin); + } + } + w = messageTarget = undefined; } addListener(window, 'unload', cleanup); @@ -139,22 +152,35 @@ try { var d = JSON.parse(e.data); if (d.a === 'ready') messageTarget.postMessage(req, origin); - else if (d.a === 'error') cb(d.d); - else if (d.a === 'response') { + else if (d.a === 'error') { + if (cb) { + cb(d.d); + cb = null; + } + } else if (d.a === 'response') { removeListener(window, 'message', onMessage); removeListener(window, 'unload', cleanup); cleanup(); - cb(null, d.d); + if (cb) { + cb(null, d.d); + cb = null; + } } - } catch(e) { } - }; + } catch(err) { } + } addListener(window, 'message', onMessage); return { close: cleanup, focus: function() { - if (w) w.focus(); + if (w) { + try { + w.focus(); + } catch (e) { + // IE7 blows up here, do nothing + } + } } }; }, @@ -175,7 +201,7 @@ o = e.origin; try { d = JSON.parse(e.data); - } catch(e) { } + } catch(err) { } if (cb) { // this setTimeout is critically important for IE8 - // in ie8 sometimes addListener for 'message' can synchronously @@ -188,7 +214,14 @@ }, 0); } } + + function onDie(e) { + if (e.data === CLOSE_CMD) { + try { window.close(); } catch (o_O) {} + } + } addListener(isIE ? msgTarget : window, 'message', onMessage); + addListener(isIE ? msgTarget : window, 'message', onDie); // we cannot post to our parent that we're ready before the iframe // is loaded. (IE specific possible failure) @@ -203,10 +236,11 @@ // if window is unloaded and the client hasn't called cb, it's an error var onUnload = function() { + removeListener(isIE ? msgTarget : window, 'message', onDie); if (cb) doPost({ a: 'error', d: 'client closed window' }); cb = undefined; // explicitly close the window, in case the client is trying to reload or nav - try { window.close(); } catch (e) { }; + try { window.close(); } catch (e) { } }; addListener(window, 'unload', onUnload); return { diff --git a/resources/static/include_js/include.js b/resources/static/include_js/include.js index 3516f7af707e0fc15aa87f1f4140ec8b68b34539..2b173421215b13cfc64079fc43931af2937f550b 100644 --- a/resources/static/include_js/include.js +++ b/resources/static/include_js/include.js @@ -628,6 +628,7 @@ // local embedded copy of winchan: http://github.com/lloyd/winchan ;WinChan = (function() { var RELAY_FRAME_NAME = "__winchan_relay_frame"; + var CLOSE_CMD = "die"; // a portable addListener implementation function addListener(w, event, cb) { @@ -644,7 +645,7 @@ // checking for IE8 or above function isInternetExplorer() { var rv = -1; // Return value assumes failure. - if (navigator.appName == 'Microsoft Internet Explorer') { + if (navigator.appName === 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) @@ -658,8 +659,9 @@ try { // We must check for both XUL and Java versions of Fennec. Both have // distinct UA strings. + var userAgent = navigator.userAgent; return (userAgent.indexOf('Fennec/') != -1) || // XUL - (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1); // Java + (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1); // Java } catch(e) {}; return false; } @@ -673,7 +675,7 @@ // given a URL, extract the origin function extractOrigin(url) { if (!/^https?:\/\//.test(url)) url = window.location.href; - var m = /^(https?:\/\/[-_a-zA-Z\.0-9:]+)/.exec(url); + var m = /^(https?:\/\/[\-_a-zA-Z\.0-9:]+)/.exec(url); if (m) return m[1]; return url; } @@ -683,7 +685,7 @@ var loc = window.location; var frames = window.opener.frames; var origin = loc.protocol + '//' + loc.host; - for (var i = frames.length - 1; i >= 0; i++) { + for (var i = frames.length - 1; i >= 0; i--) { try { if (frames[i].location.href.indexOf(origin) === 0 && frames[i].name === RELAY_FRAME_NAME) @@ -760,8 +762,16 @@ function cleanup() { if (iframe) document.body.removeChild(iframe); iframe = undefined; - if (w) w.close(); - w = undefined; + if (w) { + try { + w.close(); + } catch (securityViolation) { + // This happens in Opera 12 sometimes + // see https://github.com/mozilla/browserid/issues/1844 + messageTarget.postMessage(CLOSE_CMD, origin); + } + } + w = messageTarget = undefined; } addListener(window, 'unload', cleanup); @@ -771,15 +781,21 @@ var d = JSON.parse(e.data); if (d.a === 'ready') messageTarget.postMessage(req, origin); else if (d.a === 'error') { - if (cb) { cb(d.d); cb = null; } + if (cb) { + cb(d.d); + cb = null; + } } else if (d.a === 'response') { removeListener(window, 'message', onMessage); removeListener(window, 'unload', cleanup); cleanup(); - if (cb) { cb(null, d.d); cb = null; } + if (cb) { + cb(null, d.d); + cb = null; + } } - } catch(e) { } - }; + } catch(err) { } + } addListener(window, 'message', onMessage); @@ -789,9 +805,8 @@ if (w) { try { w.focus(); - } - catch(e) { - /* IE7 blows up here, do nothing */ + } catch (e) { + // IE7 blows up here, do nothing } } } diff --git a/tests/i18n-tests.js b/tests/i18n-tests.js index 9001546ba514aca3041a6ccb7f3270b49946f6e2..88287ec5d0ebed4d9fb3ec04b7a8134999a6ccfe 100755 --- a/tests/i18n-tests.js +++ b/tests/i18n-tests.js @@ -27,7 +27,7 @@ suite.addBatch({ "format a string with named values": { topic: function () { var params = { salutation: "Hello", place: "World" }; - return i18n.format("%(salutation)s %(place)s!", params, true); + return i18n.format("%(salutation) %(place)!", params); }, "was interpolated": function (err, str) { assert.equal(str, "Hello World!");