diff --git a/browserid/static/dialog/resources/network.js b/browserid/static/dialog/resources/network.js index f0fd530ddf3b84bc9e43ff9c545f5dec316dbebe..234624f1365e124b01f3aab02ec00cac88b1f1c6 100644 --- a/browserid/static/dialog/resources/network.js +++ b/browserid/static/dialog/resources/network.js @@ -40,35 +40,10 @@ BrowserID.Network = (function() { var csrf_token, xhr = $, server_time, - auth_status; + auth_status, + hub = OpenAjax.hub; - function withContext(cb, error) { - if (typeof auth_status === 'boolean' && typeof csrf_token !== 'undefined') cb(); - else { - xhr.ajax({ - url: "/wsapi/session_context", - type: "GET", - success: function(result) { - csrf_token = result.csrf_token; - server_time = { - remote: result.server_time, - local: (new Date()).getTime() - }; - auth_status = result.authenticated; - _.defer(cb); - }, - error: error, - dataType: "json" - }); - } - } - - function clearContext() { - var undef; - csrf_token = server_time = auth_status = undef; - } - - function createDeferred(cb) { + function deferResponse(cb) { if (cb) { return function() { var args = _.toArray(arguments); @@ -79,6 +54,19 @@ BrowserID.Network = (function() { } } + function get(options) { + xhr.ajax({ + type: "GET", + url: options.url, + // We defer the responses because otherwise jQuery eats any exceptions + // that are thrown in the response handlers and it becomes very difficult + // to debug. + success: deferResponse(options.success), + error: deferResponse(options.error), + dataType: "json" + }); + } + function post(options) { withContext(function() { var data = options.data || {}; @@ -91,12 +79,53 @@ BrowserID.Network = (function() { type: "POST", url: options.url, data: data, - success: options.success, - error: options.error + // We defer the responses because otherwise jQuery eats any exceptions + // that are thrown in the response handlers and it becomes very difficult + // to debug. + success: deferResponse(options.success), + error: deferResponse(options.error) }); }, options.error); } + function withContext(cb, onFailure) { + if (typeof auth_status === 'boolean' && typeof csrf_token !== 'undefined') cb(); + else { + xhr.ajax({ + url: "/wsapi/session_context", + success: function(result) { + csrf_token = result.csrf_token; + server_time = { + remote: result.server_time, + local: (new Date()).getTime() + }; + auth_status = result.authenticated; + cb(); + }, + error: onFailure + }); + } + } + + function clearContext() { + var undef; + csrf_token = server_time = auth_status = undef; + } + + // Not really part of the Network API, but related to networking + $(document).bind("offline", function() { + hub.publish("offline"); + }); + + function xhrError(callback, error) { + return function() { + if (callback) { + callback(); + } + hub.publish("xhrError", error); + }; + } + var Network = { /** * Set the XHR object and clear all context info. Used for testing. @@ -134,7 +163,7 @@ BrowserID.Network = (function() { // session, let's set it to perhaps save a network request // (to fetch session context). auth_status = authenticated; - _.delay(onSuccess, 0, authenticated); + if(onSuccess) onSuccess(authenticated); } catch (e) { onFailure("unexpected server response: " + e); } @@ -155,7 +184,7 @@ BrowserID.Network = (function() { withContext(function() { try { if (typeof auth_status !== 'boolean') throw "can't get authentication status!"; - _.delay(onSuccess, 0, auth_status); + if (onSuccess) onSuccess(auth_status); } catch(e) { if (onFailure) onFailure(e.toString()); } @@ -178,7 +207,7 @@ BrowserID.Network = (function() { // FIXME: we should return a confirmation that the // user was successfully logged out. auth_status = false; - if (onSuccess) _.defer(onSuccess); + if (onSuccess) onSuccess(); }, error: onFailure }); @@ -200,10 +229,7 @@ BrowserID.Network = (function() { site : origin }, success: function(status) { - var staged = status.success; - // why a delay here? Because of the test harness? - // shouldn't the delay be in the test harness? - _.delay(onSuccess, 0, staged); + if (onSuccess) onSuccess(status.success); }, error: onFailure }); @@ -218,10 +244,10 @@ BrowserID.Network = (function() { * I think so (BA). */ emailForVerificationToken: function(token, onSuccess, onFailure) { - xhr.ajax({ + get({ url : "/wsapi/email_for_token?token=" + encodeURIComponent(token), success: function(data) { - onSuccess(data.email); + if (onSuccess) onSuccess(data.email); }, error: onFailure }); @@ -234,12 +260,10 @@ BrowserID.Network = (function() { * @param {function} [onFailure] - Called on XHR failure. */ checkUserRegistration: function(email, onSuccess, onFailure) { - xhr.ajax({ + get({ url: "/wsapi/user_creation_status?email=" + encodeURIComponent(email), success: function(status, textStatus, jqXHR) { - if (onSuccess) { - _.delay(onSuccess, 0, status.status); - } + if (onSuccess) onSuccess(status.status); }, error: onFailure }); @@ -261,9 +285,7 @@ BrowserID.Network = (function() { pass: password }, success: function(status, textStatus, jqXHR) { - if (onSuccess) { - _.delay(onSuccess, 0, status.success); - } + if (onSuccess) onSuccess(status.success); }, error: onFailure }); @@ -294,9 +316,7 @@ BrowserID.Network = (function() { */ resetPassword: function(password, onSuccess, onFailure) { // XXX fill this in. - if (onSuccess) { - _.defer(onSuccess); - } + if (onSuccess) onSuccess(); }, /** @@ -311,7 +331,7 @@ BrowserID.Network = (function() { changePassword: function(oldPassword, newPassword, onSuccess, onFailure) { // XXX fill this in if (onSuccess) { - _.delay(onSuccess, 0, true); + onSuccess(true); } }, @@ -330,9 +350,7 @@ BrowserID.Network = (function() { token: token }, success: function(status, textStatus, jqXHR) { - if (onSuccess) { - _.delay(onSuccess, 0, status.success); - } + if (onSuccess) onSuccess(status.success); }, error: onFailure }); @@ -347,7 +365,7 @@ BrowserID.Network = (function() { cancelUser: function(onSuccess, onFailure) { post({ url: "/wsapi/account_cancel", - success: createDeferred(onSuccess), + success: onSuccess, error: onFailure }); }, @@ -368,8 +386,7 @@ BrowserID.Network = (function() { site: origin }, success: function(status) { - var staged = status.success; - _.delay(onSuccess, 0, staged); + if (onSuccess) onSuccess(status.success); }, error: onFailure }); @@ -383,12 +400,10 @@ BrowserID.Network = (function() { * @param {function} [onfailure] - called on xhr failure. */ checkEmailRegistration: function(email, onSuccess, onFailure) { - xhr.ajax({ + get({ url: "/wsapi/email_addition_status?email=" + encodeURIComponent(email), success: function(status, textStatus, jqXHR) { - if (onSuccess) { - _.delay(onSuccess, 0, status.status); - } + if (onSuccess) onSuccess(status.status); }, error: onFailure }); @@ -404,12 +419,10 @@ BrowserID.Network = (function() { * @param {function} [onFailure] - Called on XHR failure. */ emailRegistered: function(email, onSuccess, onFailure) { - xhr.ajax({ + get({ url: "/wsapi/have_email?email=" + encodeURIComponent(email), success: function(data, textStatus, xhr) { - if(onSuccess) { - _.delay(onSuccess, 0, data.email_known); - } + if(onSuccess) onSuccess(data.email_known); }, error: onFailure }); @@ -429,9 +442,7 @@ BrowserID.Network = (function() { email: email }, success: function(status, textStatus, jqXHR) { - if (onSuccess) { - _.delay(onSuccess, 0, status.success); - } + if (onSuccess) onSuccess(status.success); }, error: onFailure }); @@ -441,15 +452,15 @@ BrowserID.Network = (function() { * Certify the public key for the email address. * @method certKey */ - certKey: function(email, pubkey, onSuccess, onError) { + certKey: function(email, pubkey, onSuccess, onFailure) { post({ url: "/wsapi/cert_key", data: { email: email, pubkey: pubkey.serialize() }, - success: createDeferred(onSuccess), - error: onError + success: onSuccess, + error: onFailure }); }, @@ -458,10 +469,9 @@ BrowserID.Network = (function() { * @method listEmails */ listEmails: function(onSuccess, onFailure) { - xhr.ajax({ - type: "GET", + get({ url: "/wsapi/list_emails", - success: createDeferred(onSuccess), + success: onSuccess, error: onFailure }); }, diff --git a/browserid/static/dialog/test/qunit/resources/network_unit_test.js b/browserid/static/dialog/test/qunit/resources/network_unit_test.js index 840084d99456c32b871ec7862d82339dfdab6658..684e267be41866405f460efc684c4f4f8e8de8b8 100644 --- a/browserid/static/dialog/test/qunit/resources/network_unit_test.js +++ b/browserid/static/dialog/test/qunit/resources/network_unit_test.js @@ -69,7 +69,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func results: { "get /wsapi/session_context valid": contextInfo, "get /wsapi/session_context invalid": contextInfo, - // We are going to test for ajax errors for session_context using + // We are going to test for XHR failures for session_context using // call to serverTime. We are going to use the flag contextAjaxError "get /wsapi/session_context ajaxError": contextInfo, "get /wsapi/session_context contextAjaxError": undefined, @@ -185,14 +185,28 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); +/* + wrappedAsyncTest("authenticate with XHR failure, checking whether application is notified", function() { + xhr.useResult("ajaxError"); - wrappedAsyncTest("authenticate with ajax error after context already setup", function() { + OpenAjax.hub.subscribe("xhrError", function() { + ok(true, "xhr error notified application"); + wrappedStart(); + }); + + network.authenticate("testuser@testuser.com", "ajaxError"); + + stop(); + }); +*/ + wrappedAsyncTest("authenticate with XHR failure after context already setup", function() { xhr.useResult("ajaxError"); + network.authenticate("testuser@testuser.com", "ajaxError", function onSuccess(authenticated) { - ok(false, "ajax error should never pass"); + ok(false, "XHR failure should never pass"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should never pass"); + ok(true, "XHR failure should never pass"); wrappedStart(); }); @@ -229,7 +243,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func }); - wrappedAsyncTest("checkAuth with ajax error", function() { + wrappedAsyncTest("checkAuth with XHR failure", function() { xhr.useResult("ajaxError"); contextInfo.authenticated = false; @@ -258,14 +272,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func }); - wrappedAsyncTest("logout with ajax error", function() { + wrappedAsyncTest("logout with XHR failure", function() { xhr.useResult("ajaxError"); network.logout(function onSuccess() { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -296,13 +310,13 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("complete_email_addition with ajax error", function() { + wrappedAsyncTest("complete_email_addition with XHR failure", function() { xhr.useResult("ajaxError"); network.completeEmailRegistration("goodtoken", function onSuccess(proven) { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -332,14 +346,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("createUser with ajax error", function() { + wrappedAsyncTest("createUser with XHR failure", function() { xhr.useResult("ajaxError"); network.createUser("validuser", "origin", function onSuccess(created) { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -374,14 +388,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("checkUserRegistration with ajax error", function() { + wrappedAsyncTest("checkUserRegistration with XHR failure", function() { xhr.useResult("ajaxError"); network.checkUserRegistration("address", function(status) { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -414,14 +428,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("completeUserRegistration with ajax error", function() { + wrappedAsyncTest("completeUserRegistration with XHR failure", function() { xhr.useResult("ajaxError"); network.completeUserRegistration("token", "password", function(registered) { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -455,14 +469,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("cancelUser with ajax error", function() { + wrappedAsyncTest("cancelUser with XHR failure", function() { xhr.useResult("ajaxError"); network.cancelUser(function() { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -497,14 +511,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("emailRegistered with ajax error", function() { + wrappedAsyncTest("emailRegistered with XHR failure", function() { xhr.useResult("ajaxError"); network.emailRegistered("address", function(taken) { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -537,14 +551,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("addEmail with ajax error", function() { + wrappedAsyncTest("addEmail with XHR failure", function() { xhr.useResult("ajaxError"); network.addEmail("address", "origin", function onSuccess(added) { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -579,14 +593,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("checkEmailRegistration with ajax error", function() { + wrappedAsyncTest("checkEmailRegistration with XHR failure", function() { xhr.useResult("ajaxError"); network.checkEmailRegistration("address", function(status) { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -622,14 +636,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("removeEmail with ajax error", function() { + wrappedAsyncTest("removeEmail with XHR failure", function() { xhr.useResult("ajaxError"); network.removeEmail("invalidemail", function onSuccess() { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -650,14 +664,14 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("requestPasswordReset with ajax error", function() { + wrappedAsyncTest("requestPasswordReset with XHR failure", function() { xhr.useResult("ajaxError"); network.requestPasswordReset("address", "origin", function onSuccess() { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -677,16 +691,16 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("resetPassword with ajax error", function() { + wrappedAsyncTest("resetPassword with XHR failure", function() { xhr.useResult("ajaxError"); /* the body of this function is not yet written network.resetPassword("password", function onSuccess() { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); stop(); @@ -707,16 +721,16 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("changePassword with ajax error", function() { + wrappedAsyncTest("changePassword with XHR failure", function() { xhr.useResult("ajaxError"); /* the body of this function is not yet written. network.changePassword("oldpassword", "newpassword", function onSuccess() { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); @@ -743,17 +757,27 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); - wrappedAsyncTest("serverTime with ajax error before context has been setup", function() { + wrappedAsyncTest("serverTime with XHR failure before context has been setup", function() { xhr.useResult("contextAjaxError"); network.serverTime(function onSuccess(time) { - ok(false, "ajax error should never call success"); + ok(false, "XHR failure should never call success"); wrappedStart(); }, function onFailure() { - ok(true, "ajax error should always call failure"); + ok(true, "XHR failure should always call failure"); wrappedStart(); }); stop(); }); + + wrappedAsyncTest("body offline message triggers offline message", function() { + OpenAjax.hub.subscribe("offline", function() { + ok(true, "offline event caught and application notified"); + start(); + }); + + $("body").trigger("offline"); + stop(); + }); });