diff --git a/src/ng/httpBackend.js b/src/ng/httpBackend.js index 28107966f643..9b2d7361e057 100644 --- a/src/ng/httpBackend.js +++ b/src/ng/httpBackend.js @@ -49,16 +49,13 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc var callbackId = '_' + (callbacks.counter++).toString(36); callbacks[callbackId] = function(data) { callbacks[callbackId].data = data; + callbacks[callbackId].called = true; }; var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), - function() { - if (callbacks[callbackId].data) { - completeRequest(callback, 200, callbacks[callbackId].data); - } else { - completeRequest(callback, status || -2); - } - callbacks[callbackId] = angular.noop; + callbackId, function(status, text) { + completeRequest(callback, status, callbacks[callbackId].data, "", text); + callbacks[callbackId] = noop; }); } else { @@ -158,33 +155,39 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc } }; - function jsonpReq(url, done) { + function jsonpReq(url, callbackId, done) { // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: // - fetches local scripts via XHR and evals them // - adds and immediately removes script elements from the document - var script = rawDocument.createElement('script'), - doneWrapper = function() { - script.onreadystatechange = script.onload = script.onerror = null; - rawDocument.body.removeChild(script); - if (done) done(); - }; - - script.type = 'text/javascript'; + var script = rawDocument.createElement('script'), callback = null; + script.type = "text/javascript"; script.src = url; - - if (msie && msie <= 8) { - script.onreadystatechange = function() { - if (/loaded|complete/.test(script.readyState)) { - doneWrapper(); + script.async = true; + + callback = function(event) { + removeEventListenerFn(script, "load", callback); + removeEventListenerFn(script, "error", callback); + rawDocument.body.removeChild(script); + script = null; + var status = -1; + var text = "unknown"; + + if (event) { + if (event.type === "load" && !callbacks[callbackId].called) { + event = { type: "error" }; } - }; - } else { - script.onload = script.onerror = function() { - doneWrapper(); - }; - } + text = event.type; + status = event.type === "error" ? 404 : 200; + } + + if (done) { + done(status, text); + } + }; + addEventListenerFn(script, "load", callback); + addEventListenerFn(script, "error", callback); rawDocument.body.appendChild(script); - return doneWrapper; + return callback; } } diff --git a/test/ng/httpBackendSpec.js b/test/ng/httpBackendSpec.js index 5c9cbf586c36..837fbab9967a 100644 --- a/test/ng/httpBackendSpec.js +++ b/test/ng/httpBackendSpec.js @@ -39,7 +39,8 @@ describe('$httpBackend', function() { fakeDocument = { $$scripts: [], createElement: jasmine.createSpy('createElement').andCallFake(function() { - return {}; + // Return a proper script element... + return document.createElement(arguments[0]); }), body: { appendChild: jasmine.createSpy('body.appendChild').andCallFake(function(script) { @@ -325,13 +326,7 @@ describe('$httpBackend', function() { expect(url[1]).toBe('http://example.org/path'); callbacks[url[2]]('some-data'); - - if (script.onreadystatechange) { - script.readyState = 'complete'; - script.onreadystatechange(); - } else { - script.onload(); - } + browserTrigger(script, "load"); expect(callback).toHaveBeenCalledOnce(); }); @@ -346,71 +341,13 @@ describe('$httpBackend', function() { callbackId = script.src.match(SCRIPT_URL)[2]; callbacks[callbackId]('some-data'); - - if (script.onreadystatechange) { - script.readyState = 'complete'; - script.onreadystatechange(); - } else { - script.onload(); - } + browserTrigger(script, "load"); expect(callbacks[callbackId]).toBe(angular.noop); expect(fakeDocument.body.removeChild).toHaveBeenCalledOnceWith(script); }); - if(msie<=8) { - - it('should attach onreadystatechange handler to the script object', function() { - $backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, noop); - - expect(fakeDocument.$$scripts[0].onreadystatechange).toEqual(jasmine.any(Function)); - - var script = fakeDocument.$$scripts[0]; - - script.readyState = 'complete'; - script.onreadystatechange(); - - expect(script.onreadystatechange).toBe(null); - }); - - } else { - - it('should attach onload and onerror handlers to the script object', function() { - $backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, noop); - - expect(fakeDocument.$$scripts[0].onload).toEqual(jasmine.any(Function)); - expect(fakeDocument.$$scripts[0].onerror).toEqual(jasmine.any(Function)); - - var script = fakeDocument.$$scripts[0]; - script.onload(); - - expect(script.onload).toBe(null); - expect(script.onerror).toBe(null); - }); - - } - - it('should call callback with status -2 when script fails to load', function() { - callback.andCallFake(function(status, response) { - expect(status).toBe(-2); - expect(response).toBeUndefined(); - }); - - $backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); - expect(fakeDocument.$$scripts.length).toBe(1); - - var script = fakeDocument.$$scripts.shift(); - if (script.onreadystatechange) { - script.readyState = 'complete'; - script.onreadystatechange(); - } else { - script.onload(); - } - expect(callback).toHaveBeenCalledOnce(); - }); - - it('should set url to current location if not specified or empty string', function() { $backend('JSONP', undefined, null, callback); expect(fakeDocument.$$scripts[0].src).toBe($browser.url());